面向对象相关
1 面向对象
1.1 什么是面向对象
面向对象就是以对象来认知客观世界和抽象事物,以封装、抽象、继承、聚合、组合等来组织对象,从而构建相应的软件系统。
按对象去理解现实世界,更符合现实世界的组织方式。对象可以是客观可见,如经脉,花鸟,石头,学生,班级,学校,公司,国家;也可以是抽象出来的,如感情,欲望,神话,故事。对象可以有自身的状态、行为,并可以互相交互。
1.2 面向对象的特征
本质:对象的划分,组织和交互。
设计的许多对象来源于现实世界,但是,结果所得到的的许多类通常在现实世界中并不存在。设计中的抽象对于灵活的设计是至关重要的。例如,描述过程或算法的类在现实中并不存在,却被设计成了对象,实体的状态也被设计成了对象。这些对象在分析阶段,或者设计的早期,并不存在,后来为使设计灵活,扩展、复用,被发掘或抽象了出来。
把流程划分为对象,把结构组织成对象,让对象进行交互。
知:容易理解,减少了细节,降低了复杂性、多样性。封装了过程和变化。
行:容易扩展。对于多样性和变化,直接扩展子类。需要增加新的功能,扩展类型。
1.2.1 继承
Is-a的关系用继承。少用继承,多用组合。
Has-a用组合。
覆盖:override
遮蔽:new
防止继承:sealed
要求子类必须实现:abstrace
1.2.2 多态
本质——函数具有不同实现。
1)静态多态
函数重载:同一个函数名,参数类型、返回值不同。
运算符重载:运算符重载,就是对已有的运算符重新进行定义,赋予其另一种功能,以适应不同的数据类型。
struct Vector
{
public double x,y,z;
public Vector(double x,double y,double z)
{
this.x = x;
this.y = y;
this.z = z;
}
public static Vector operator + (Vector lhs,Vector rhs)
{
}
}
运算符重载的声明方式与方法的声明方式相同,但operator关键字告诉编译器,它实际上是一个运算符重载,后面是相关运算符的符号,在本例中就是+。返回类型是在使用这个运算符时获得的类型。在本例中,把两个矢量加起来会得到另一个矢量,所以返回类型就是Vector。对于这个+运算符重载,返回类型与包含类一样,但这种情况并不是必需的。两个参数就是要操作的对象。对于二元运算符(带两个参数),如+和-运算符,第一个参数是放在运算符左边的值,第二个参数是放在运算符右边的值。
C#要求所有的运算符重载都声明为public和static,这表示它们与它们的类或结构相关联,而不是与实例相关联,所以运算符重载的代码体不能访问非静态类成员,也不能访问this标识符
2)动态多态
定义时,同一个父类型的方法在不同子类有不同实现。
使用时,同样的父类型,指向不同的子类。
运行时,同样的父类型,但因指向的子类不同,呈现不同的状态。
1.2.3 封装
面向对象,封装成函数,封装成类,抽象出上层类或者接口。使用聚合、组合。
类:封装了数据和行为。屏蔽了内部实现细节,只暴露了公共接口来使用。
抽象:向下屏蔽了多样性和变化性。向上统一了归属和行为结果。
父类——向下屏蔽了对象的多样性。向上统一了所属。
接口——向下屏蔽了行为的变化性(不同实现)。向上统一了行为的对外体现(对外结果)。
1.2.4 抽象类和接口
抽象类:类型共属。
接口:行为共有。
抽象:向下屏蔽了多样性和变化性。向上统一了归属和行为结果。
父类——向下屏蔽了对象的多样性。向上统一了所属。
接口——向下屏蔽了行为的变化性(不同实现)。向上统一了行为的对外体现(对外结果)。
所以,抽象类比接口包括的更大。如果只是行为共有,抽象成接口,使用接口继承。
1.2.5 组合和聚合
组合——组成。 同生共死。
聚合——持有。主被性。你死你的。
组合和聚合是有很大区别的,这个区别不是在形式上,而是在本质上: 比如A类中包含B类的一个引用b,当A类的一个对象消亡时,b这个引用所指向的对象也同时消亡(没有任何一个引用指向它,成了垃圾对象),这种情况叫做组合,反之b所指向的对象还会有另外的引用指向它,这种情况叫聚合。 在实际写代码时组合方式一般会这样写: A类的构造方法里创建B类的对象,也就是说,当A类的一个对象产生时,B类的对象随之产生,当A类的这个对象消亡时,它所包含的B类的对象也随之消亡。 聚合方式则是这样: A类的对象在创建时不会立即创建B类的对象,而是等待一个外界的对象传给它 传给它的这个对象不是A类创建的。 现实生活中: 人和人和手,脚是组合关系,因为当人死亡后人的手也就不复存在了。人和他的电脑是聚合关系。
1.3 面向对象设计的6原则
对象定义和交互的原则。总原则开闭。
定义——单一职责。
交互——依赖最小的范围,依赖最少的依赖。
依赖范围最小的方法依赖抽象,抽象包括抽象出父类和抽象出接口。抽象出父类后,如何实现继承。抽象接口时,应该使接口最小,以减少依赖。
依赖最少——迪米特法则
开闭——抽象框架,扩展细节和变化。
尽量降低复杂性,提高抽象。
抽象更稳定。抽象能力是设计的关键能力。
如何抽象:
父类:类型抽象。
接口:行为抽象。
委托:方法抽象。
如果组织对象的交互:
架构模式:分层模式,MVVM,数据总线模式,消息队列,CS/BS,管道过滤器,
设计模式:行为模式(策略,),结构模式(桥接,代理,中介,外观,)
1.3.1 单一职责——一类一职
不要存在多于一个导致类变更的原因。
一个类只提供此类的功能。
扩展:
一个函数只实现一个功能。
一个字段只代表一种含义。
- 可以降低类的复杂度,一个类只负责一项职责,其逻辑肯定要比负责多项职责简单的多;
- 提高类的可读性,提高系统的可维护性;
- 提高类的可读性,提高系统的可维护性;
- 变更引起的风险降低,变更是必然的,如果单一职责原则遵守的好,当修改一个功能时,可以显著降低对其他功能的影响。
在职责扩散到我们无法控制的程度之前,要立刻对代码进行重构。
1.3.2 依赖
1)依赖倒置——依赖抽象(接口和父类)
依赖抽象,不要依赖于具体。外部看到是抽象,不能看到具体。
抽象层之间:对于子类有父类没有的方法,子类尽量不要公开。对外的内容由父类负责。对外看到的是父类,是抽象,尽量不要看到子类的独有内容。
抽象指父类和接口。接口包括抽象出的interface,对外或向上提供的一组函数,屏蔽了内部实现。
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. 细节应该依赖抽象(实现类依赖接口或抽象类)
对外提供一组可访问的函数,屏蔽内部实现。
向上或向下提供一组可访问的函数,使其与本层隔离。
(1)接口隔离——最小接口
定义:客户端不应该依赖它不需要的接口;一个类对另一个类的依赖应该建立在最小的接口上。
定义:客户端不应该依赖它不需要的接口;一个类对另一个类的依赖应该建立在最小的接口上。
问题由来:类A通过接口I依赖类B,类C通过接口I依赖类D,如果接口I对于类A和类B来说不是最小接口,则类B和类D必须去实现他们不需要的方法。
解决方案:将臃肿的接口I拆分为独立的几个接口,类A和类C分别与他们需要的接口建立依赖关系。也就是采用接口隔离原则。
(2)里氏替换——正确继承
任何基类可以出现的地方,子类一定可以出现。
基类可以替换子类。
子类完全实现父类的方法,也可以增加自己独有的方法。
子类函数:入参类型可以大于父类,返回值类型可以小于父类。宽进窄出。
2)迪米特法则—最少知道
定义:一个对象应该对其他对象保持最少的了解。(对自己依赖的类知道的越少越好。)
问题由来:类与类之间的关系越密切,耦合度越大,当一个类发生改变时,对另一个类的影响也越大。
解决方案:尽量降低类与类之间的耦合。
自从我们接触编程开始,就知道了软件编程的总的原则:低耦合,高内聚。无论是面向过程编程还是面向对象编程,只有使各个模块之间的耦合尽量的低,才能提高代码的复用率。低耦合的优点不言而喻,但是怎么样编程才能做到低耦合呢?那正是迪米特法则要去完成的。
迪米特法则又叫最少知道原则,最早是在1987年由美国Northeastern University的Ian Holland提出。通俗的来讲,就是一个类对自己依赖的类知道的越少越好。也就是说,对于被依赖的类来说,无论逻辑多么复杂,都尽量地的将逻辑封装在类的内部,对外除了提供的public方法,不对外泄漏任何信息。迪米特法则还有一个更简单的定义:只与直接的朋友通信。首先来解释一下什么是直接的朋友:每个对象都会与其他对象有耦合关系,只要两个对象之间有耦合关系,我们就说这两个对象之间是朋友关系。耦合的方式很多,依赖、关联、组合、聚合等。其中,我们称出现成员变量、方法参数、方法返回值中的类为直接的朋友,而出现在局部变量中的类则不是直接的朋友。也就是说,陌生的类最好不要作为局部变量的形式出现在类的内部。
————————————————
版权声明:本文为CSDN博主「三级小野怪」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/zhengzhb/article/details/7296930
1.3.3 开闭原则——可扩展,不修改
对扩展开放,对修改关闭。
扩展:增加子类,增加类。增加接口的行为。
修改:使用if-else。
行为抽象:实现开闭原则的关键就在于“抽象”。把系统的所有可能的行为抽象成一个抽象底层,这个抽象底层规定出所有的具体实现必须提供的方法的特征。作为 系统设计的抽象层,要预见所有可能的扩展,从而使得在任何扩展情况下,系统的抽象底层不需修改;同时,由于可以从抽象底层导出一个或多个新的具体实现,可以改变系统的行为,因此系统设计对扩展是开放的。
我们在 软件开发的过程中,一直都是提倡需求导向的。这就要求我们在设计的时候,要非常清楚地了解用户需求,判断需求中包含的可能的变化,从而明确在什么情况下使用开闭原则。
变化封装或抽象:关于系统可变的部分,还有一个更具体的对可变性封装原则(Principle of Encapsulation of Variation, EVP),它从 软件工程实现的角度对开闭原则进行了进一步的解释。EVP要求在做系统设计的时候,对系统所有可能发生变化的部分进行评估和分类,每一个可变的因素都单独进行封装。
我们在实际开发过程的设计开始阶段,就要罗列出来系统所有可能的行为,并把这些行为加入到抽象底层,根本就是不可能的,这么去做也是不经济的。因此我们应该现实的接受修改拥抱变化,使我们的代码可以对扩展开放,对修改关闭。
开闭原则是面向对象设计中最基础的设计原则,它指导我们如何建立稳定灵活的系统。开闭原则可能是设计模式六项原则中定义最模糊的一个了,它只告诉我们对扩 展开放,对修改关闭,可是到底如何才能做到对扩展开放,对修改关闭,并没有明确的告诉我们。以前,如果有人告诉我“你进行设计的时候一定要遵守开闭原 则”,我会觉的他什么都没说,但貌似又什么都说了。因为开闭原则真的太虚了。
在仔细思考以及仔细阅读很多设计模式的文章后,终于对开闭原则有了一点认识。其实,我们遵循设计模式前面5大原则,以及使用23种设计模式的目的就是遵循 开闭原则。也就是说,只要我们对前面5项原则遵守的好了,设计出的软件自然是符合开闭原则的,这个开闭原则更像是前面五项原则遵守程度的“平均得分”,前 面5项原则遵守的好,平均分自然就高,说明软件设计开闭原则遵守的好;如果前面5项原则遵守的不好,则说明开闭原则遵守的不好。
其实笔者认为,开闭原则无非就是想表达这样一层意思:用抽象构建框架,用实现扩展细节。因为抽象灵活性好,适应性广,只要抽象的合理,可以基本保持软件架 构的稳定。而软件中易变的细节,我们用从抽象派生的实现类来进行扩展,当软件需要发生变化时,我们只需要根据需求重新派生一个实现类来扩展就可以了。当然 前提是我们的抽象要合理,要对需求的变更有前瞻性和预见性才行。
说到这里,再回想一下前面说的5项原则,恰恰是告诉我们用抽象构建框架,用实现扩展细节的注意事项而已:单一职责原则告诉我们实现类要职责单一;里氏替换 原则告诉我们不要破坏继承体系;依赖倒置原则告诉我们要面向接口编程;接口隔离原则告诉我们在设计接口的时候要精简单一;迪米特法则告诉我们要降低耦合。 而开闭原则是总纲,他告诉我们要对扩展开放,对修改关闭。
最后说明一下如何去遵守这六个原则。对这六个原则的遵守并不是是和否的问题,而是多和少的问题,也就是说,我们一般不会说有没有遵守,而是说遵守程度的多 少。任何事都是过犹不及,设计模式的六个设计原则也是一样,制定这六个原则的目的并不是要我们刻板的遵守他们,而需要根据实际情况灵活运用。对他们的遵守 程度只要在一个合理的范围内,就算是良好的设计。我们用一幅图来说明一下。
图中的每一条维度各代表一项原则,我们依据对这项原则的遵守程度 在维度上画一个点,则如果对这项原则遵守的合理的话,这个点应该落在红色的同心圆内部;如果遵守的差,点将会在小圆内部;如果过度遵守,点将会落在大圆外 部。一个良好的设计体现在图中,应该是六个顶点都在同心圆中的六边形。
https://www.cnblogs.com/qinying/p/4376484.html
1.4 如何使用面向对象
抽象类型多样性和实现变化性:if-else,抽象出父类或者接口。
依赖抽象。
隐藏细节和内部实现:封装,只对外提供需要的部分。
流程:1.定义成一个对象或多个对象。前面的对象持有后面的对象。对外看到的是第一个对象,或者提供一个对外的适配对象。其中某些多个对象可以向上抽象。
2.抽象成多个对象。在这些对象之外抽象出一个管理对象,负责流程。对外看到的是管理对象。
洗漱--坐车---打卡---工作---吃中饭--工作--打卡--坐车---到家吃饭
1.4.1 (寻找)划分对象
1.4.2 组织对象
1.4.3 对象交互
1.5 设计的技巧
抽象:同类抽象,行为抽象。
委托(事件):同行为抽象。
分离:
衔接方式(依赖方式):聚合,对外接口,事件,消息,队列(公共数据相互连接)
浙公网安备 33010602011771号