写点什么

[架构师训练营] Week02 - 学习总结

用户头像
谭方敏
关注
发布于: 2020 年 07 月 09 日

编程的目的是用计算机来解决现实世界的问题。

编程的过程即:在计算机所能理解的模型(解空间)和现实世界(问题空间)之间,建立一种联系。





问题领域,包含与系统所要解决的问题相关的实物和概念的空间。

 

Booch对于对象的描述与:对象具有状态,行为和标识。

状态:表明每个对象可以由自己的数据。

行为:表明每个对象可以产生行为。

标识:表明每个对象都区别于其它的对象(唯一的地址)

 

面向对象编程的三要素(特征)

封装性(encapsulation)

隐藏实现细节(访问控制)

         定义接口

继承性(inheritance)

         IS-A关系

         HAS-A关系(组合)

多态性(polymorphism)

         后期绑定(虚函数)

         向上转型(up casting)

面向对象的终极目标是:高内聚,低耦合。

面向对象的原则包括ocp(开闭原则,open/close原则), dip(依赖倒置原则,dependency inversion principle), lsp(替换原则,Liskov substitution principle), srp(单一职责原则,single responsibility principle), isp(interface segregation principle).



其中, ocp核心思想是:

扩展是开放的(open for extension)意味着模块的行为是可以扩展的。当应用的需求改变时,我们可以对模块进行扩展,使其具有满足那些改变的新行为。换句话说,我们可以改变模块的功能。

修改是关闭的(close for modification) 对模块行为进行扩展时,不必改动模块的源代码或者二进制代码。模块的二进制可执行文件,无论是可链接的库,DLL或者java的jar文件,都无需改变。

ocp例子

--- shape.h ------------------------------
Enum shapeType { circle, square};
Struct Shape {
ShapeType itsType;
}
--- circle.h --------------------------------
Struct Circle {
ShapeType itsType;
Double itsRadius;
Point itsCenter;
}
--- square.h ----------------
Struct Square {
ShapeType itsType;
Double itsSide;
Point itsTopLeft;
}

---- drawAllShapes.cc------------------------
Typedef struct Shape* ShapePointer;
Void DrawAllShapes(ShapePointer list[], int n){
Int I;
For(I = 0; I < n; i++){
Struct Shape* s = list[i];
Switch(s->itsType){
Case square:
DrawSquare((struct Square*)s);
Break;
Case circle:
DrawCircle((struct Circle*)s);
Break;
}
}
}


应用ocp原则后:

Class shape {
Public:
Virtual void Draw() const = 0;
};
Class square:public shape {
Public:
Virtual void Draw() const;
};

Class Circle:public shape{
Public:
Virtual void Draw() const;
}
Void DrawAllShape(vector<Shape*>& list) {
Vector<shape*>::iterator I;
For(I =list.begin();I != list.end();i++){
(*i)->Draw();
}
}


如果要扩展程序,使之能够绘制一种新的形状,我们只需要增加一个新的shape类的派生类。DrawAllShape函数并不需要改变,这样DrawAllShape就符合OCP。无需改动自身代码,就可以扩展它的行为。

开放的是扩展行为,关闭的是二进制可执行文件,dll, jar包,上面例子关注的是DrawAllShape。



dip核心思想:

高层模块不应该依赖于低层模块,二者都应该依赖于抽象。

抽象不应该依赖于细节。细节应该依赖于抽象。

DIP分层结构:

这里倒置的层次不仅仅是依赖关系的倒置,也是接口所有权的倒置。通常会认为工具库应该拥有自己的接口。但当应用DIP后,我们往往发现客户拥有抽象接口,而它们的服务者则从这些抽象接口派生。

DIP例子1:

应用DIP原则后:

lamp的确依赖于buttonserver, 但是buttonserver没有依赖于button, 任何知道如何去操纵buttonserver接口的对象都能够控制lamp.

DIP2例子:

应用DIP原则后:

对应的代码是:



lsp核心思想是:

子类型(subtype)必须能够替换掉它们的基类型(basetype).

替换性质:若对每个类型S的对象o1,都存在一个类型T的对象o2, 使得在所有针对T编写的程序P中,用o1替换o2后,程序P行为功能不变,则S是T的子类型。

假设有一个函数f, 它的参数为指向某个基类B的指针或者引用. 同样假设有B的某个派生类D, 如果把D的对象作为B类型传递给f, 会导致f出现错误的行为。那么D就违反了LSP. 显然, D对于f来说是脆弱的。

例子:



应用lsp原则后:

srp原则核心思想:

就一个类而言,应该仅有一个引起它变化的原因。

例子1:

应用srp原则后:



例子2:

应用srp原则后:

isp原则核心思想:

不应该强迫客户依赖于它们不用的方法。类的胖接口可以分解成多组方法。每一组方法都服务于一组不同的客户程序。这样,一些客户程序可以使用一组成员函数,而其他客户程序可以使用其他组的成员函数。

例子1:

优化后:

对应代码:



或者:

对应代码:



例子2:

优化后:

参考资料:

[Agile Software.Development:Principle. Patterns. and. Practices]((美)Robert.C.Martin).



用户头像

谭方敏

关注

还未添加个人签名 2019.10.22 加入

还未添加个人简介

评论

发布
暂无评论
[架构师训练营] Week02 - 学习总结