《架构整洁之道》是Uncle Bob|Bob大叔【美】Robert C. Martin(罗伯特 C. 马丁)在2017年写的介绍软件架构的书。本书从最细节的编程范式,再讲到SOLID设计原则,然后依次介绍高阶的组件、软件架构。后面强调了数据库、框架、Web等是实现细节而非架构。最后则是其他人写的拾遗和半工作经历自传。整本书有点像搭建房子时介绍建筑架构,先从砖和钢筋混凝土到房价再到房子等由小到大介绍开了,很有趣。

作者总结了3种编程范式:

  1. 结构化编程:最传统的,程序是欧几里得公理系统;对函数控制权的直接转移进行了限制和规范;
  2. 面向对象编程:通过封装、继承和多态,对程序控制权的间接转移进行了限制和规范;
  3. 函数式编程:通过变量不可变(函数无状态)、对可变进行隔离,对程序中的赋值进行了限制和规范。

作者总结的架构精髓:SOLID原则,可以有效指导我们如何构建每一个程序模块。

S代表Single Responsibility Principle,即单一责任原则:每个模块都只有一个需要被改变的理由。据说与Conway Law直接相关。

O代表Open Close Principle,即开闭原则:允许新增代码来改变系统的行为,而不允许通过改变老代码来改变系统行为。Software entities (classes, modules, functions) should be open for extension but closed for modification

L代表Liskov Substitution Principle,李氏替换原则:组件之间必须满足设计约束才可以相互替换;派生类(子类)对象可以在程序中代替其基类(超类)对象。

I代表Interface Segregation Principle,接口隔离原则:客户(client)不应被迫使用对其而言无用的方法或功能。Clients should not be forced to depend upon interfaces that they don't use。接口隔离,避免不必要的依赖。

D代表Dependency Inversion Principle,依赖反转原则:高层次的策略性代码不应该依赖于底层的实现性代码,依赖关系应该反过来。例如业务逻辑不应该依赖UI或者数据库。High level modules should not depend upon low level modules. Both should depend upon abstractions. Abstractions should not depend upon details. Details should depend upon abstractions。

作者继续阐明组件构建原则。

组件定义:组件是程序部署的最小单元,即可执行文件和库,比如jar文件。

组件的聚合要满足以下三个原则:

  1. REP 复用/发布等同原则:软件复用的最小粒度应该等同与发布的最小粒度。这样任何变更不会导致过多的发布,或者过度的源代码修改。
  2. CCP 共同闭包原则:把那些会同时修改,并且为相同目的而修改的软件模块(类)放到同一个组件中去,即共同闭包。这实际是SRP在组件级别的重述,因为可维护性的重要性远大于可复用性。我们可以通过组件张力图,去计量共同闭包。
  3. CRP 共同复用原则:不要强迫一个组件的用户依赖他们不需要的东西。

组件的耦合应该满足下面三个原则:

  1. 无循环依赖原则:组件依赖关系中不应该出现环状结构。如果有,可以通过DIP解决。
  2. 稳定依赖原则:依赖关系必须指向更稳定的方向。被依赖的最多的组件应该是最稳定的,因为它难以被修改。计量关系:I = Fan-out/(Fan-in + Fan-out)。接口最稳定。
  3. 稳定抽象原则:一个组件的抽象化程度应该与其稳定性保持一致。

讲完组件,作者开始阐述软件架构。

主要是把策略和细节分离,在整个软件中,合理的划分边界,同时合理的规划好边界之间的依赖关系。同时做好策略与层次的设计:低层次的组件应该去依赖高层次的组件。

业务逻辑作为程序中最核心的用来挣钱/省钱的逻辑,应该位于层次同心圆最中间。而实现细节,或者I/O(UI、数据库等),则应位于最外面。依赖关系则是,最外面依赖最里面。
the clean artchitecture.jpeg

个人感觉,软件架构计时做好合理的分层,这里包括水平和垂直分层两个部分。水平只的是从UI、业务逻辑、持久化等数据流去分层;垂直分层则是从业务模块和领域的角度去分层,例如订单模块、CRM模块等。一个合理的软件架构,应该同时考虑这两种分层,并选择与之相应较好的侧重点。Salesforce是典型的侧重水平分层的平台,有明确的UI层(Visualforce Page、aura、lwc)、逻辑层(Apex)、数据层(SOQL、DML)。而目前流行的微服务架构,则更侧重垂直分层。

这两种分层的取舍,作者并没有过多解释,只是强调了策略与细节、分层与边界、分层与依赖等更加基础的内容。为什么?我猜是因为在架构上去侧重某种分层,结果就是出现框接,而作者认为框架是细节,并非架构。

作者还强调很多看起来像框架的东西,其实是实现细节

比如数据库、Web、应用程序框架,他们都是实现细节。

作者还对一个视频销售网站的架构设计,进行了精彩的讨论。

在另外一个人写的拾遗部分,主要讨论了作者的理论和自己的理解。

最后,作者回顾了从20世纪60年代到90年代,作者的软件工作生涯。可以从中看到作者不断总结教训经验,慢慢得出本书的这套理论。

参考

  1. douban 架构整洁之道
  2. wiki 开闭原则
  3. wiki 里氏替换原则
  4. wiki 接口隔离原则
  5. wiki SOLID
  6. zhihu 小话设计模式原则之:依赖倒置原则DIP
  7. The Clean Architecture

有时候我们需要在Salesforce页面下载存放为Attachment的文件;或者我们需要把存成Attachment的图片显示在页面上。

内部站点

如果我们文件为Attachment,在Salesforce Internal (即使用Salesforce账户登录的,非Digital Experience, Site.com),则可以使用

 <a href="{!URLFOR($Action.Attachment.Download, attachmentId)}">File Name</a>

下载文件。

或者使用

<img src="{!URLFOR($Action.Attachment.Download, attachmentId)}" />

显示图片。

如果是File,则可以使用Url.getFileFieldURL(entityId, fieldName)

Site.com

Site.com上的则不能用上面的方法,使用的话会直接报错。这时可以通过Salesforce提供的文件下载api /servlet/servlet.FileDownload?file=xxxx~18,不过要注意,一定要使用Salesorce Internal Url,使用Site.com的相关url肯定也会报错。

  1. 首先在后台获取 Salesforce Instance Url 或 Org Domain Url。例如:
    String orgUrl = Url.getOrgDomainUrl().toExternalForm()
  2. 将其传到前台,拼接成下载地址:
    <a href="{!orgUrl + '/servlet/servlet.FileDownload?file=' + attachment.Id}">File Name</a>

参考文档

  1. Visualforce Developer Guide: $Action
  2. Apex Reference Guide: URLClass
  3. stackexchange: How to get salesforce instance url
  4. Salesforce Developer Forum: Download Attachment from Sites??
  5. Salesforce Developer Forum: How to get downloadable link for attachment?

升级后打开前后端均显示503,查询nginx日志显示:

PHP message: Adapter Typecho_Db_Adapter_Mysql is not available

由于之前升级过PHP,因此估计是PHP的问题。修改Typecho的config.inc.php相关代码为:

/** 定义数据库参数 */
$db = new Typecho_Db('Pdo_Mysql', 'typecho_');

恢复正常。

Salesforce 未锁定包(Salesforce Unlocked Package)是Salesforce最新推广的基于包的开发最佳实践。通过Salesforce CLI,我们可以很方便的制作和部署未锁定包。本文只是简略的将核心步骤展示出来:

  1. 创建一个 SFDX 项目

    sfdx force:project:create -d demo-workspace -n expenser-app -p force-app
  2. 授权Dev Hub环境,该环境必须启用Dev Hub功能和未锁定包和第二代管控包功能

    sfdx auth:web:login -d -a devHub
  3. 创建一个草稿环境(scratch org)并在其中开发包

    sfdx force:org:create -f config/project-scratch-def.json -u scratchOrg1
  4. 保证所有要打包的组件都已经在当前的项目文件夹内
  5. 从 SFDX 项目文件夹,直接创建未锁定包:

    sfdx force:package:create -n "Demo App" -r force-app -t Unlocked -o [email protected]
  6. 检查项目文件夹中的sfdx-project.json文件,CLI 会自动更新项目文件,使其包含上一步创建的包的信息:

    {
    "packageDirectories": [
       {
          "path": "force-app",
          "default": true,
          "package": "Demo App",
          "versionName": "ver 0.1",
          "versionNumber": "0.1.0.NEXT"
       }
    ],
    "namespace": "",
    "sfdcLoginUrl": "https://login.salesforce.com",
    "sourceApiVersion": "50.0",
    "packageAliases": {
       "Expense Manager": "0Hoxxx"
    }
    }
  7. 创建一个包的版本,Salesforce CLI会自动处理包的版本号等

    sfdx force:package:version:create --package "Demo App" -k lushang.me --wait 10
  8. 在另一个创建好的草稿环境中,安装并测试这个版本的包:

    sfdx force:package:install -p "Demo [email protected]" -y testOrg1 -k lushang.me --wait 10 --publishwait 10
  9. 如果上一步骤没问题,则可以发布这个版本的包:

    sfdx force:package:version:promote -p "Demo [email protected]"

参考:

  1. Salesforce DX Developer Guide
  2. Workflow for Unlocked Packages
  3. Release an Unlocked Package

2019年能源消费总量:4860.0Mtce = 1.42434936e+20J = 48亿吨标准煤

国土面积:960万平方公里 = 9.6e+12 m2

我国属太阳能资源丰富的国家之一,全国总面积2/3以上地区年日照时数大于2000小时,年辐射量在5000MJ/m2以上。据统计资料分析,中国陆地面积每年接收的太阳辐射总量为3.3×103~8.4×103MJ/m2,相当于24,000亿吨标准煤的储量。

太阳能储量/能源消费总量:24000/48 = 500倍。

如果使用20%转化率的太阳能电池,则为满足2018年一次能源消耗量,则需要9.6e+12 48 / (24,000 20%) = 9.6 万平方公里(1.44亿亩),1%的国土面积。

考虑到中国有大片沙漠戈壁(总面积达130.8万平方千米,约占全国土地总面积的13.6%),所以我建议:

给沙漠和戈壁披上太阳能电池!发电,减少沙尘危害,实现碳中和!

Q&A、答疑:

  1. 这事能赚钱么?
    按照目前市面上的太阳能电池价格和年发电量比例,不能赚钱。但是能极大地降低中国碳排放量,极大减少石油依赖,极大保护中国的环境。而且能带动太阳能电池板研发制造、电力输送、终端产品(电动汽车、电动工业机械等)非常多的产业发展。如果我们走在前头,还能把这些先进的产业链出口全球,赚取知识产权和研发设计等高端附加值。
  2. 这事儿技术上可行吗?

    • 目前中国能源消费总量年均增速在3.4%左右,按照国家「十四五」计划中的「到2030年达到碳排放峰值」,预计我们最多耗能应该在4860.0Mtce * (1 + 3.4%) ^ 11 = 7095 Mtce, 71亿吨标准煤,或者说 207937422000000000000 焦耳
    • 目前市面上太阳能天池的功率面积比大约在200 瓦特/平米, 计算成每年产出的电能和面积比应该是2522880000 焦耳,那么很清楚就知道咱们需要207937422000000000000/2522880000 = 82420654966 平方米 = 82420 平方公里 = 1.23569 亿亩土地。小于上面假设的9.6万平方公里,更小于130.8万平方千米的沙漠戈壁。说明有的是地方装。
    • 目前中国光伏产业是少数处于世界领先位置的产业,而且还有点产能过剩,我们自己用没问题!
    • 电池技术突飞猛进,储能电池价格节节下降,在2018年,最低0.8 元/Wh即可买到!
  3. 大概要花多少钱?

    • 太阳能电板: 这么大规模的采购,单价可以降低到0.5元/W(原价在1元/W左右)。按照最高71亿吨标准煤的年发电量,需要2.07937e+20J/3.1536e+07s = 6.59365e+12 W的装机量,也就是需要3.296825e+12元,即3.296825万亿人民币。还不到2008年中国4万亿振兴计划嘛,咱们现在可比十多年前富强多了!
    • 储能电池:目前最低0.8元/Wh单价,咱们集中采购再降低一点,0.4元/Wh!再按照一天4小时发电量做储能,需要6.59365e+12 W * 4 * 0.4 = 1.05498e+13,即10.5498万亿人民币。看起来这个项目跟电动汽车一样,电池是大头!
    • 土地:沙漠戈壁啥的,基本上可以免费,就算1000元/亩象征性收点钱。1.64841e+13 W的装机量,目前市面上太阳能电池功率占面积比大约200 W/平米, 再留一个10%的面积用来固定,那么需要9.06628e+10 平米 = 1.35926e+8亩,粗略需要1.26亿亩地。看来目前转化率不错,不需要1.38亿亩土地了。1.26亿亩土地,需要1260亿买地。
    • 运送安装:厂家包邮,沙漠全是沙子,他们可以就近建厂。沙漠治理+安装费用算1万元/亩,需要1.26万亿
    • 配送运输:国家电网赚钱的事情,给他们2000亿
    • 其他费用: 1000亿
    • 总费用: 15.5326万亿,十五万亿的能源投资计划出炉了!
  4. 那么多的太阳能电池和储能电池,哪里来?
    太阳能电池需要单晶硅,沙漠里面都是沙,就地取材即可。然后招标10-100个太阳能电池厂干活。另外中国是稀土大国,储能电池所需原料都有,请现在做动力电池的老曹出马,也干个10-100个电池厂搞生产。中国集中力量干大事的能力,毋庸置疑!
  5. 这么大的工程规模,怎么铺设电池,怎么输送?
    国家电网的事情,你能发电,他们就能输电,不操心。
  6. 谁掏钱啊?
    15.5万亿,你让国家突然出这么多也不太好,毕竟2019年国家财政收入才19万亿。我建议用财政收入的5%,即1万亿,在加上社会资本1万亿,每年2万亿投入,8年即可完工!
  7. 晚上怎么办?靠月亮吗?
    电池,储能电池。另外晚上基本上工业用电木有了,再加上水能发电、核能发电等的补充,相信能够满足夜晚供电的需求。
  8. 收益?

    • 碳中和!一个几乎只用可持续的绿色能源的国家,子孙后代不愁环境污染
    • 一个基本上没有石油依存的国家,没人随便能源卡脖子
    • 带动国内相关产业发展,少说带动50万亿产业总量,增加相关就业人口1亿人
    • 相关技术出国国外,从产品制造、到技术研发、到设计服务,高低搭配,应有尽有,赚一堆外汇!

参考

  1. 国家数据 - 能源 - 能源消费量