学习笔记——面向对象软件开发基础
这里提出了一些点,不是很细,很多地方可以扩展开。
对象和类
对象是一个运行时的概念,而类是一个编译时概念。
类包含了属性和行为,对象是类的实例。
抽象
隐藏掉一些细节,突出本质。
实例:
一级抽象:A的汽车+B的汽车 -> Car
二级抽象:Car+Truck -> Vehicle
抽象成子系统:RaceUISystem VehicleSubSystem
抽象成不同开发层次
面向对象的基本特征
封装(Encapsulation)
—— 类,最基本的封装;对外为接口,封装的是实现。(封装变化)
继承(inheritance/generalization)
—— "is a kind of"。
多态(polymorphism)
—— 重载(overload):同一个类中的同名函数。(静态多态,编译时决定)
—— 重写(override):子类(重新)实现基类的虚函数。(动态多态,运行时决定)
封装的好处:是让设计更flexible,更容易变更。如果封装的好,变更的部分只有实现,接口不需要变化,从而使用者也不需要变化。
继承的好处:通过抽取基类避免重复代码。通过子类化复用已有的类。
类之间的关系
泛化(Generalization)
—— 就是继承
依赖(Dependency)
—— 暂时使用的概念(类A中暂时使用类B中的方法)
关联(Association)
—— USE的概念(类A中将类B的对象或指针或引用作为成员使用)
—— 表示法中包括Role,Multiplicity,Navigability三方面
聚合(Aggregation)
—— Is a part of
—— 聚合和关联的实现方式一致,主要看二者之间是否在逻辑上存在拥有或包含关系。例如Division和Employee,Division中的接口及实现都是为Employee服务的。
组合(Composition)
—— Is a part of
—— 与聚合的区别是: 组合的二者生命周期一致,聚合则不是。例如:windows窗体销毁的时候,内部的空间全需要销毁。
面向对象基本原则
封装变化 —— 要根据具体问题进行具体分析,不要设计过度。
容易变化的内容
|
应对策略
|
UI
|
区分出
UI
类与业务逻辑类
|
对硬件的依赖
|
对于每个硬件,识别一个硬件接口类
|
通信协议
|
识别出通信类,用于通信;
识别出通信数据类,用数据的
wrap
和
unwrap
|
文件格式
|
识别文件解析类用于解析文件数据以及把数据转换成文件需要的格式
|
业务规则
|
把业务规则封装到业务逻辑类中
|
子类个数
|
识别工厂类用于子类对象的创建
|
复杂的算法
|
封装到专门的算法类
|
面向接口编程而不是实现
(Code to an interface rather than to an implementation)
接口:接口定义了一些行为,由其他类来实现
这里的接口是一个抽象概念,可以是Java的interface,可以是C++的抽象类
同一个接口可以由不同的类来实现
面向接口编程的好处:
提供了实现类之间的互换性
可以通过增加实现类扩展功能
缺点:增加了设计的复杂性,使得理解运行时结构变得困难
优先使用(对象)组合,而非(类)继承
继承既是接口级复用也是实现级复用;组合(包括关联、聚合、组合)只是实现级复用。
简单说,继承复用了基类的接口,因此基类的变化对子类会有影响;组合只是使用关系,只要使用方式不发生改变,就不会互相影响。
继承的缺点:
1. 基类的变化导致派生类在不知情的情况下发生了变化,封装性不好。
2. 不可运行时修改(理解不深)
面向对象设计原则
设计的臭味
僵化性(Rigidity) —— 设计难于修改。(重复代码,改一处类似的地方也要改)
脆弱性(Fragility) —— 设计易于遭到破坏。(改一点,其它点也需要改动来迎合它)
顽固性(Immobility) —— 模块或子系统难以独立被复用。
粘滞性(Viscosity) —— 做正确的事比错误的事难。(软件粘滞性 - 为了修改问题,保持设计比改动设计更难。环境粘滞性 - 为了适应开发环境而做的工作,开发环境改变后需要大幅度修改)
不必要的复杂性(Needless Complexity) —— 过分的设计
不必要的重复(Needless Repetiation) —— 滥用复制、粘贴(与第一条有什么区别?)
晦涩性(Opacity) —— 代码随着时间的推移而越来越晦涩。
单一职责原则(SRP, The Single Responsibility Principle)
一个类/一个函数,只做它本职的一件事儿,能引起它变化的也只有一件事儿。这样会尽量的减少变化带来的副作用,使内聚更高,耦合更低。
开闭原则(OPC, The Open-Close Principle)
对扩展开放,对变化关闭。
也就是说,当需求改变时,做到最好的程度是只对代码进行扩展,而不会修改原有的内容。
可维护性和重用性。
里氏替换原则(LSP, The Liskov substitution principle)
子类必须能替换基类。
关注的是怎样良好的使用继承。
正方形、长方形的例子;企鹅、鸟的例子。
依赖倒置原则(DIP, The Dependency Inversion Principle)
高层模块不应当依赖底层模块,它们都应当依赖于抽象。(e.g. 用户写字不应该由于笔的不同而改变使用方法)
抽象不应当依赖细节,细节应当依赖于抽象。(服务端不应该针对某一具体应用而设计,而是应该针对抽象而设计)
接口隔离原则(ISP, The Interface Segregation Principle)
很难用几句话说明白,可以参考:
http://www.iteye.com/topic/526407
DRY(Don't Repeat Yourself Principle)
重复代码更容易出错,在不断的copy paste过程中,容易犯一些细节上的错误。
重复的代码经不起变化,修改一个Bug就可能要改很多处。
可以通过抽取公共部分放置在一个地方避免代码重复。
(写的不对的欢迎拍砖)