面向对象_学习记

[b]1.4. 面向对象[/b]
面向对象是一种以“对象”作为中心的编程思想,其中对象的含义可以理解为“存在的东西”。
[b]1.5. 为什么要面向对象?[/b]
面向对象思想的核心是“可扩展性”!
[b]1.6. 面向对象应用范围[/b]
能做什么?
既然面向对象思想的核心是“可扩展性”,那么其适用范围就显而易见了:经常变化的地方就是面向对象
应用的地方。
[b]【面向对象类】[/b]
面向对象的类由两部分组成:属性和方法。
属性:指类具有的特性
方法:指类具有的功能
设计属性的一个基本原则:属性最小化原则,即:“属性不可再分”!
设计方法的一个基本原则:方法单一化原则,即:一个方法只做一件事!

[b]2.2. 对象[/b]
有了“类”的清晰定义后,“对象”就比较容易理解了。
对象就是一个具体的类,一个真实存在的类。

[img]http://dl2.iteye.com/upload/attachment/0101/7475/df44406e-9607-35d0-b2f8-eea5ba208536.png[/img]
[b]2.3. 接口[/b]
:接口是一组相关的交互功能点定义的集合。
[b]2.4. 抽象类[/b]
抽象类是一种特殊的类,其特殊性在于抽象类只能用于继承,不能被实例化为具体的对象。例如在Java
中不能new 一个抽象类,但可以extends 一个抽象类。
[b]2.6. 三大核心特征[/b]
封装、继承、多态是面向对象的三大核心特征,三者相辅相成,缺一不可。判断一个编程语言是否是面向
对象的编程语言,就是看其是否支持者三大核心特征。

在面向对象领域,多态的真正含义是:使用指向父类的指针或者引用,
能够调用子类的对象。
[b]2.7. 小结[/b]
 类是一组相似事物的统称
 类设计两个基本原则:属性不可再分,一个方法只做一件事
 对象就是一个具体的类,一个真实存在的类
 接口是一组相关的交互功能点定义的集合
 抽象类只能用于继承,不能被实例化为具体的对象
 抽象类就是基于类而抽象出来的
 抽象即“抽取出来比较像的部分”
 抽象的主要目的是“隔离关注点,降低复杂度”
 封装、继承、多态是面向对象的三大核心特征
 继承类似生物学上的“遗传”
 抽象和继承是前后衔接的关系,先有抽象,通过抽象得出类,后通过继承来表达抽象结果
 多态的真正含义是:使用指向父类的指针或者引用,能够调用子类的对象。
 封装主要原因是“保护隐私”和“隔离复杂度”
[b]4.9. 小结[/b]
 需求是对客户有价值的事情,功能是系统为了实现需求而具备的能力
 修复需求错误的问题的成本非常高昂
 需求分析的目的是挖掘客户的问题,实现客户价值
 需求分析的方法:5W1H8C
 需求分析阶段是不区分面向对象还是面向过程的
 用例方法用于详细分析5W1H8C 中的1H
 用例分析使用NEA 方法
 用例分析的时候不需要画UML 图
 用例图是描述系统用例集合的图,不是用来描述单个用例的流程
 SSD 图可以使得用例更形象化,但不能取代用例
[b]5.6. 小结[/b]
 领域模型是“需求到面向对象的桥梁”
 领域建模的三字经方法:找名词、加属性、连关系
 从用例中可以找到领域模型的名词
 不是所有属性都在用例中存在
 领域模型中的类不是软件类,而只是用于描述领域中的实体,不需要关注方法


计原则和设计模式并不是竞争关系,正好相反,它们是互补的关系。
设计原则和设计模式互补体现在:设计原则主要用于指导“类的定义”的设计,而设计模式主要用于指导
“类的行为”的设计,更通俗一点的讲:设计原则是类的静态设计原则,设计模式是类的动态设计原则。
一般情况下,我们是采用“先设计原则,后设计模式”的方法来操作的。

[b]6.4. 小结[/b]
 设计阶段中,我们将要输出设计模型
 面向对象的设计更多时候是一项艺术
 设计模型主要包含2 部分内容:静态模型、动态模型
 从领域模型中可以得到最初的设计类
 “设计原则”和“设计模式”可以指导我们做出更好的面向对象设计
 设计原则主要是指SOLID 原则
 设计模式是指GoF 提出的设计模式
 根据框架或者规范的要求,拆分出“辅助类”
 动态模型主要有4 种:状态模型、活动模型、序列模型、协作模型
 从用例模型推导出动态模型是一个“分解和分配”的过程

7.4. 小结
 各种编程语言对于面向对象的支持或者实现并不完全相同
 C++中,类的声明和定义是分开的
 C++支持public、protected、private、friend 共4 种访问控制方式
 C++友元访问机制实质上是对类封装的一种破坏
 C++的继承分为public、protected、private 三种继承方式
 C++支持多继承
 C++支持虚拟继承
 C++虚拟继承虽然解决了多继承的钻石问题,但同样带来了复杂性问题
 C++通过虚函数来实现多态
 C++没有语法上的关键字来标识抽象类,而是通过纯虚函数的特性来支持抽象类
 C++没有提供语法上的关键字来标识接口,同样也是通过纯虚函数来实现接口的
 Java 并不区分类的声明和定义两种不同概念的动作,Java 的类只有定义动作
 Java 中的访问控制包含两部分:类的访问控制,成员的访问控制
 Java 的成员访问控制有4 种方式:public、protected、默认、private
 Java 通过关键字extends 来指明继承关系
 Java 默认情况下就是支持多态的,并不需要像C++一样通过关键字来标识
 Java 通过关键字abstract 来标识抽象类,相比C++更加清晰
 Java 通过关键字interface 来标识接口,相比C++也更加清晰

[b]第三部分:面向对象技巧
8. 设计原则
8.1. 内聚和耦合
8.1.1. 内聚[/b]
内聚指一个模块内部元素彼此结合的紧密程度。
虽然判断内聚性的时候我们会考虑元素的结合情况,但其实是否专注模块的职责,才是内聚性的充要条件。
当模块的元素全部都专注于模块的职责的时候,即使元素间的结合不是很紧密,也是符合内聚性的要求的,
这也是CRUD 设计符合内聚性的原因。
所以,判断一个模块(函数、类、包、子系统)“内聚性”的高低,最重要的是关注模块的元素是否都忠
于模块的职责,简单来说就是“不要挂羊头卖狗肉”。
【巧合内聚(Coincidental cohesion)】
模块内部的元素之所以被划分在同一模块中,仅仅是因为“巧合”!
这是内聚性最差的一种内聚,从名字上也可以看出,模块内的元素没有什么关系,元素本身的职责也各不
相同。基本上可以认为这种内聚形式实际上是没有内聚性。
【逻辑内聚(Logical cohesion)】
模块内部的元素之所以被划分在同一模块中,是因为这些元素逻辑上属于同一个比较宽泛的类别!
模块的元素逻辑上都属于一个比较宽泛的类别,但实际上这些元素的职责可能也是不一样的。
例如将“鼠标”和“键盘”划分为“输入”类,将“打印机“、“显示器”等划分为“输出”类。
相比巧合内聚来说,逻辑内聚的元素之间还是有部分凝聚力的,只是这个凝聚力比较弱,但比巧合内聚来
说要强一些。
【时序内聚】
模块内部的元素之所以被划分在同一模块中,是因为这些元素必须按照固定的“时间顺序”进行处理。
这种内聚一般在函数这个级别的模块中比较常见,例如“异常处理”操作,一般的异常处理都是“释放资
源(例如打开的文件、连接、申请的内存)、记录日志、通知用户”,那么把这几个处理封装在一个函数中,
它们之间的内聚就是“时序内聚”。
【过程内聚(Procedural cohesion)】
模块内部的元素之所以被划分在同一模块中,是因为这些元素必须按照固定的“过程顺序”进行处理。
过程内聚和时间内聚比较相似,也是在函数级别的模块中比较常见。例如读写文件的操作,一般都是按照
这样的顺序进行的:判断文件是否存在、判断文件是否有权限、打开文件、读(或者写)文件,那么把这
些处理封装在一个函数中,它们之间的内聚就是“过程内聚”。
【交互内聚(Communicational cohesion)】
模块内部的元素之所以被划分在同一模块中,是因为这些元素都操作相同的数据。
交互内聚的名称和定义差别较大,我也不知道老外为啥这样命名,其实我觉得还不如叫“共享内聚” :)
虽然我们觉得命名不太正确,但为了交流的时候不至于引起误会,我们还是使用交互内聚来说明。
交互内聚最常见的就是数据结构的类定义了,例如Java HashMap 的get、put、clear 等操作。
【顺序内聚(Sequential cohesion)】
模块内部的元素之所以被划分在同一模块中,是因为某些元素的输出是另外元素的输入。
顺序内聚其实就像一条流水线一样,上一个环节的输出是下一个环节的输入。最常见的就是“规则引擎”
一类的处理,一个函数负责读取配置,将配置转换为执行指令;另外一个函数负责执行这些指令。
【功能内聚(Functional cohesion)】
模块内部的元素之所以被划分在同一模块中,是因为这些元素都是为了完成同一项任务。

[b]8.1.2. 耦合[/b]
耦合(或者称依赖)是程序模块相互之间的依赖程度。
【耦合的分类】
【无耦合(No coupling)】
无耦合意味着模块间没有任何关系或者交互。
【消息耦合(Message coupling (low))】
模块间的耦合关系表现在发送和接收消息。
【数据耦合(Data coupling)】
两个模块间通过参数传递基本数据,称为数据耦合。
这里有两点需要特别关注,这也是数据耦合区别于其它耦合类型的关键特征:
1)通过参数传递,而不是通过全局数据、配置文件、共享内存等其它方式
2)传递的是基本数据类型,而不是传递对象,例如Java 中传递integer、double、String 等类型
【数据结构耦合( Data-structured coupling)】
两个模块通过传递数据结构的方式传递数据,成为数据结构耦合,又称为标签耦合(Stamp coupling)。
但标签耦合不是很好理解,而且没法和上面的“数据耦合”联系起来,因此我们一般都用数据结构耦合这
http://blog.youkuaiyun.com/yunhua_lee 面向对象葵花宝典
127
个称呼。
数据结构耦合和数据耦合是比较相近的,主要差别在于数据结构耦合中传递的不是基本数据,而是数据结
构数据。
另外需要注意的是,数据结构中的成员数据并不需要每一个都用到,可以只用到一部分

【控制耦合(Control coupling)】
当一个模块可以通过某种方式来控制另外一个模块的行为时,称为控制耦合。
最常见的控制方式是通过传入一个控制参数来控制函数的处理流程或者输出,例如常见的工厂类。

【外部耦合(External coupling)】
当两个模块依赖相同的外部数据格式、通讯协议、设备接口时,称为外部耦合。

【全局耦合(Globaling coupling)】
当两个模块共享相同的全局数据,称为全局耦合,又叫普通耦合(Common coupling),
不过普通耦合这个名称太容易让人误解了,还是全局耦合能够让人顾名思义。
全局耦合是一种比较常见的耦合方式,尤其是在C/C++的程序中,多多少少都会有一些全局数据。
【内容耦合(Content coupling)】
当一个模块依赖另外一个模块的内部内容(主要是数据成员)时,称为内容耦合。内容耦合是最差的一中
耦合方式,因此有另外一个很形象的名称:病态耦合(Pathological coupling)。
[b]
[b]8.1.3. 高内聚低耦合[/b]

8.2. 类设计原则
8.2.1. SRP[/b]
【SRP 原则详解】
SRP,single responsibility principle,中文翻译为“单一职责原则”!
对应到面向对象设计领域,我们可以说一个类的职责应该如下定义:
1) 类的职责是站在其它类的角度来定义的;
2) 类的职责包含多个相关功能;
因此,SRP 可以翻译成“一个类只负责一组相关的事情”,对应到代码中就是:一个类有多个方法,这些
方法是相关的。

对应到代码上来说,OCP 的应用原则如下:
1) 接口不变:包括函数名、函数参数、函数返回值等,可以应用OCP
2) 接口改变:已有函数修改名称、参数、返回值,或者增加新的函数,OCP 都不再适应

LSP,Liskov substitution principle,中文翻译为“里氏替换原则”。
1) 子类的对象提供了父类的所有行为,且加上子类额外的一些东西(可以是功能,也可以是属性);
2) 当程序基于父类实现时,如果将子类替换父类而程序不需要修改,则说明符合LSP 原则


1) 子类必须实现或者继承父类所有的公有函数,否则调用者调用了一个父类中有的函数,而子类中没有,
运行时就会出错;
2) 子类每个函数的输入参数必须和父类一样,否则调用父类的代码不能调用子类;
3) 子类每个函数的输出(返回值、修改全局变量、插入数据库、发送网络数据等)必须不比父类少,否
则基于父类的输出做的处理就没法完成。

8.2.4. ISP
ISP,Interface Segregation Principle,中文翻译为“接口隔离原则”。
客户端不应该被强迫去依赖它们并不需要的接口

8.2.5. DIP
DIP,dependency inversion principle,中文翻译为“依赖倒置原则”。

DIP 原则主要有两点含义:
1) 高层模块不应该直接依赖低层模块,两者都应该依赖抽象层;
2) 抽象不能依赖细节,细节必须依赖抽象;

SOLID 原则具体的应用场景如下:
 SRP 原则:用于类的设计
当我们想出一个类,或者设计出一个类的原型后,使用SRP 原则核对一下类的设计是否符合SRP 要求。
 OCP 原则:总的指导思想
OCP 原则是一个总的指导思想,在面向对象的设计中,如果能够符合LSP/ISP/DIP 原则,一般情况下就
能够符合OCP 原则了。
除了在面向对象的软件设计中外,OCP 也可以用于指导系统架构设计,例如常见的CORBA、COM 协议,
其实都可以认为是OCP 原则的具体应用和实现。
 LSP 原则:用于指导类继承的设计
当我们设计类之间的继承关系时,使用LSP 原则来判断这种继承关系是否符合LSP 要求。
 ISP 原则:用于指导接口的设计
ISP 原则可以认为是SRP 原则的一个变种,本质上和SRP 的思想是一样。SRP 用于指导类的设计,而ISP
用于指导接口的设计。
 DIP 原则:用于指导如何抽象
当我们设计类之间的依赖关系时,可以使用DIP 原则来判断这种依赖是否符合DIP 原则。DIP 原则和LSP
原则相辅相成:DIP 原则用于指导抽象出接口或者抽象类,而LSP 原则指导从接口或者抽象类派生出新的
子类。

[b]8.3. 小结[/b]
 内聚是指模块内部元素的“凝聚力”
 是否专注模块的职责,才是内聚性的充要条件
 内聚性不是越高越好
 内聚关注模块内部的元素结合程度,耦合关注模块之间的依赖程度
 无论是“低内聚”,还是“高耦合”,其本质都是“不稳定”
 真正好的设计是在高内聚和低耦合间进行平衡
 职责是站在他人的角度来定义的,而不是自己定义的
 职责不是一件事,而是很多事情,但这些事情都是和职责紧密相关的
 SRP 原则用于指导类的设计
http://blog.youkuaiyun.com/yunhua_lee 面向对象葵花宝典
155
 OCP 原则是总的设计指导思想
 LSP 原则用于指导类继承的设计
 ISP 原则用于指导接口的设计
 DIP 原则用于指导类抽象的设计
 不要过度设计

[b]9. 设计模式
9.1. 设计模式简介[/b]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值