序言
班门弄斧,孔府卖文,北大讲学,狗尾续貂。
引子
一个地理编码系统演变故事:如何从小甜甜变成牛夫人。
常见的编程问题:变量公开;参数声明混乱;不遵守函数名称规定的含义;随意添加类函数,类成了大杂烩;将无状态的函数也声明为成员函数;
为什么?
软件设计的核心问题:处理与控制问题的复杂性与需求的易变性。
处理复杂性基本方法论:分治。
笛卡尔认为,现实只有在破裂为极小的单位后才能被人们了解。
——《第三次浪潮》
笛卡尔方法论:直观=》分析=》综合=》列举与归纳
分治的指导原则:高内聚、低耦合。降低耦合才能方便理解系统(应付复杂性),方便局部演化(应付易变性),也因此方便复用。
编程设计分治的基本范式:面向过程与面向对象。
面向过程:动词视角,时间维度为主。事务(解决问题)=过程的分解。
面向对象:名词视角,空间维度为主。事务(解决问题)=对象的协作。
为什么面向对象更有优势?
月上柳梢头,人约黄昏后。
——欧阳修
同来望月人何处,风景依稀似去年。
——刘长卿
约会:商量;前往;共处;互诉衷肠
怀念:动念;前往;独步;发痴
约会这个业务,经过一段时间变化,变成了怀念。
对象是思考关联性的标的,天然容易实现“高内聚、低耦合”。
是什么?
一般教科书介绍面向对象三个特征:继承、封装、多态。
本质指导思想:高内聚、低耦合。
对象具有某种“完整性”。对象只能按照适合它的方式来改变状态、改变行为、实现操作或与其他对象发生关联。
管理复杂性与易变性:抽象,封装
抽象:关注外部视图,接口契约,面向不变。契约:前置条件约束客户;后置条件约束自己。
封装:关注内部视图,隐藏实现,隐藏易变。
核心:思考本质需要。
抽象是什么?
abstraction is selective ignorance.
何谓selective? 吕端大事不糊涂。需求视角。九方皋相马。祭祀的牛。
主观性,艺术性,姜子牙在夫人和文王眼中的不同抽象。
案例:地理几何索引GeomIndexer。本质需求是啥?
封装是什么?
封装是将那些不涉及对象本质特征的秘密隐藏起来。
避免外界不可预测的干预,因此能保证内在状态的可管理,也因此有效管控了耦合。反面案例:房间乱拉电线,全局变量。
案例:工具链批处理中的任务号,批次号的思考。
抽象与封装的对立统一:zoom-in & zoom-out ;最小承诺&最小惊奇
抽象的层次:has-a, is-a
has-a: 组合
is-a: 继承
has-a/is-a案例:DataServer, LocalDataServer, RemoteDataServer
将总是联动的对象进一步抽象:GeomIndexer到MapDataManager。
怎么做?
1,承包商思维:客户视角而非服务者视角
易用性:接口功能单一;联动者自动处理。
案例:MapDataManager的设计思考
案例:分析一个DataModel
class MapRecord {
public:
enum Status {
s_default = 0,
s_create = 1,
s_modify = 2,
s_delete = 3, // delete
s_ref = 4,
s_createdelete = 5
};
enum Type {
unknown = 0,
road = 1,
roadattribute = 2,
roadnode = 3,
roadconnectivity = 4,
....
};
MapRecord();
MapRecord(Type tbType);
~ MapRecord();
MapRecord *Clone() const;
void SetDataID(Int64 id);
Int64 GetDataID() const;
void SetTableName(std::string tbName);
std::string GetTableName();
void SetTableType(Type tbType);
Type GetTableType();
geos::geom::Geometry *GetGeometry() const;
//
void ModifyGeometry(geos::geom::Geometry *pGeometry);
//
bool ModifyFieldValue(const std::string &fieldName,
const std::string &fieldValue);
bool GetFieldValue(const std::string &fieldName,
std::string &fieldValue) const;
bool AddFieldValue(const std::string &fieldName,
const std::string &fieldValue);
bool SetFieldValue(const std::string &fieldName,
const std::string &fieldValue);
bool AddFieldList(std::string &fieldName);
void GetFieldList(std::set<std::string> &fdlist);
.....
}
class RecordConvert {
public:
static MapRecord::Type TableName2TableType(const std::string &tbName);
static std::string TableType2TableName(MapRecord::Type type);
static std::string Status2String(MapRecord::Status status);
static DataFieldType::Type String2FieldType(std::string &type);
static geos::geom::GeometryTypeId GetGeometryType(MapRecord::Type type);
static DataObject *CreateDataMeta(DataObject::Type type);
static std::string GetTableIdName(DataObject::Type type);
static SourceInfoType GetSourceInfoType(DataObject::Type type);
static MapRecord::Type GetSourceInfoTableType(SourceInfoType sType);
};
防止客户误用:确保客户只能通过接口来操控,进而让内部状态可管控。
案例:标精地图融合思路的发现。如何自动管理维护逻辑关系?
2,面向对象的基本原则
本质两大原则:面向接口而非面向实现,以及最小单元
a. 单一职责:职责过多或过细都不合适。度的根本依据是相关性。举例:MapDataManager
b. 开闭原则:对扩展开放,对修改封闭。复用的前提。本质是面向接口原则所规定的。
c. 里氏替换原则:本质是面向接口原则所规定的。
d.依赖倒置原则:实现依赖接口。本质是面向接口原则所规定的。
e. 接口隔离原则:拆分为更细接口,功能尽量单一,用户只需要知道其感兴趣部分。
f.最小知识原则:调用者对被调用者了解越少越好。
3,理解设计模式的层次
以代理模式为例。《人类简史》
任何计算机问题都可以增加一个间接层得到解决。
案例:智能指针;如何在手机上进行分布式测试;网络代理
由小到大,理解架构。
结语
天下何思何虑?天下一致而百虑,殊途而同归。
——《系辞传》