浅谈“面向对象”

111 阅读5分钟

序言

班门弄斧,孔府卖文,北大讲学,狗尾续貂。

引子

一个地理编码系统演变故事:如何从小甜甜变成牛夫人。

常见的编程问题:变量公开;参数声明混乱;不遵守函数名称规定的含义;随意添加类函数,类成了大杂烩;将无状态的函数也声明为成员函数;

为什么?

软件设计的核心问题:处理与控制问题的复杂性与需求的易变性。

处理复杂性基本方法论:分治。

笛卡尔认为,现实只有在破裂为极小的单位后才能被人们了解。

——《第三次浪潮》

笛卡尔方法论:直观=》分析=》综合=》列举与归纳

分治的指导原则:高内聚、低耦合。降低耦合才能方便理解系统(应付复杂性),方便局部演化(应付易变性),也因此方便复用。

编程设计分治的基本范式:面向过程与面向对象。

面向过程:动词视角,时间维度为主。事务(解决问题)=过程的分解。

面向对象:名词视角,空间维度为主。事务(解决问题)=对象的协作。

为什么面向对象更有优势?

月上柳梢头,人约黄昏后。

——欧阳修

同来望月人何处,风景依稀似去年。

——刘长卿

约会:商量;前往;共处;互诉衷肠

怀念:动念;前往;独步;发痴

约会这个业务,经过一段时间变化,变成了怀念。

对象是思考关联性的标的,天然容易实现“高内聚、低耦合”。

是什么?

一般教科书介绍面向对象三个特征:继承、封装、多态。

本质指导思想:高内聚、低耦合。

对象具有某种“完整性”。对象只能按照适合它的方式来改变状态、改变行为、实现操作或与其他对象发生关联。

管理复杂性与易变性:抽象,封装

抽象:关注外部视图,接口契约,面向不变。契约:前置条件约束客户;后置条件约束自己。

封装:关注内部视图,隐藏实现,隐藏易变。

核心:思考本质需要。

抽象是什么?

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,理解设计模式的层次

以代理模式为例。《人类简史》

任何计算机问题都可以增加一个间接层得到解决。

案例:智能指针;如何在手机上进行分布式测试;网络代理

由小到大,理解架构。

结语

天下何思何虑?天下一致而百虑,殊途而同归。

——《系辞传》