UML学习PPT-21 面向对象设计原则
合集下载
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
例子
结合依赖倒转原则,重构代码 添加一个抽象的AbstractStranger类,使 Someone依赖于抽象的“AbstractStranger”角 色,而不是具体实现 public abstract class AbstractStranger { abstract void operation3() ; }
例子
重构Friend的provide方法,使其返回抽象角色
public class Friend { private Stranger stranger = new Stranger() ; public void operation2(){} public AbstractStranger provide() { return stranger ; } }
例子
Someone和Friend是朋友类(直接通讯的类)。Friend 类持有一个Stranger类的私有对象,他们是朋友类: public class Friend { private Stranger stranger = new Stranger() ; public void operation2(){} public Stranger provide() { return stranger ; } }
例子
现在Someone对Stranger的依赖完全通过 Friend隔离,这样的结构已经符合狭义迪米特 法则了。 缺点:会在系统里造出大量的小方法,散落在 系统的各个角落。 遵循迪米特法则会使一个系统的局部设计简化, 因为每一个局部都不会和远距离的对象有直接 关联。但是,这也会造成系统的不同模块之间 的通信效率降低,也会使系统的不同模块之间 不容易协调。
注意
一个合理的类,应该仅有一个引起它变化的 原因,即单一职责 在没有变化征兆的情况下应用SRP或其他原 则是不明智的 在需求实际发生变化时就应该应用SRP等原 则来重构代码
开-闭原则 (The Open-Closed Principle)
什么是开-闭原则
一个软件实体应当对扩展开放,对修改关闭。 开-闭原则的优越性 通过扩展已有的软件系统,可以提供新的行 为,以满足对软件的新需求 已有的软件模块不能再被修改,这使软件系 统有一定的稳定性和延续性
public void forward() { stranger.operation3() ; }
}
例子
重构Someone的operation1方法,让其调用新 提供的forward方法 public class Someone { public void operation1( Friend friend ) { friend.forward() ; } }
例子
AbstractStranger成为Someone的朋友类, 而Friend类可以随时替换掉 AbstractStranger的实现类,Someone不 再需要了解Stranger的内部实现细节
广义迪米特法则
将迪米特法则运用到系统的设计中时,应注意:
Байду номын сангаас
在类的划分上,应该创建弱耦合的类 在类的设计上,每一个类都应当尽量降低成员的访 问权限 在类的设计上,只要有可能,一个类应当设计成不 变类 一个对象对其它对象的引用应当降到最低 尽量降低类的访问权限 不要暴露类成员,而应该提供相应的访问器
复用技术——继承
优点
新的实现较为容易 修改或扩展继承而来的实现较为容易 继承复用破坏封装 如果超类发生改变,那么子类的实现不得不改变 从超类继承而来的实现是静态的,不能在运行时间 内发生改变
缺点
复用技术——合成/聚合
优点
新对象与引用对象沟通的唯一方法是被引用 对象的接口,这样保持了封装性 这种复用在运行时刻动态进行 有较多的对象需要管理
面向对象设计原则
面向对象设计原则
类设计 包设计
面向对象设计原则——类设计
SRP,单一职责原则,一个类应该有且只有一个改变的 理由 OCP,开放封闭原则,你应该能够不用修改原有类就 能扩展一个类的行为 LSP,Liskov替换原则,派生类要与其基类自相容 DIP,依赖倒置原则,依赖于抽象而不是实现 ISP,接口隔离原则,客户只要关注它们所需的接口 CARP,合成/聚合复用原则,尽量使用合成/聚合、尽 量不要使用继承 LoD,迪米特法则,不要和陌生人说话
例子
Someone类和Stranger类不是朋友类, 但Someone类却通过Friend类知道了 Stranger类的存在,这显然违反迪米特法 则。
例子
对Someone和Friend类进行重构 public class Friend { private Stranger stranger = new Stranger() ; public void operation2(){} public Stranger provide() { return stranger ; }
举例 对于父类和子类Base和Derived,若程序 能接受Base,那么程序必然可以接受子 类Derived
void Method (Base b){…}; …… Derived d; Method (d);
依赖倒转原则 (Dependence Inversion Principle)
糟糕的设计 很难添加新的功能,因为每一处改动都 会影响系统中过多的模块 当你做了一处改动,却导致系统中的另 一个模块发生问题 很难在别的应用程序中重用这个模块
例子
让Stranger从AbstractStranger类继承
public class Stranger extends AbstractStranger { public void operation3() {} }
例子
重构Someone使其依赖抽象的AbstractStranger
public class Someone { public void operation1( Friend friend ) { AbstractStranger stranger = friend.provide() ; stranger.operation3() ; } }
单一职责原则(SingleResponsibility Principle )
例子
interface DataChannel { public void send(char c); public void recv(); } interface Connection { public void dial(string pno); public void hangup(); }
接口的污染(Interface Contamination) 一些没有经验的设计师往往想节省接口 的数量,将一些功能相近或功能相关的 接口合并,并将这看成是代码优化的一 部分。
接口隔离原则
接口隔离原则(ISP)
一个类对另外一个类的依赖应当是建立在 最小的接口上的。换言之,使用多个专门的接 口比使用单一的总接口要好。
里氏代换原则 (The Liskov Substitution Principle)
里氏代换原则的严格表达
如果对每一个类型为T1的对象o1,都有类 型为T2的对象o2,使得以T1定义的所有程 序P在所有的对象o1都代换成o2时,程序P 的行为没有变化,那么类型T2是类型T1的 子类型。
里氏代换原则 (The Liskov Substitution Principle)
缺点
迪米特法则(Law of Demeter)
迪米特法则(LoD)又叫最少知识原则:
一个软件实体应当尽可能少的与其他实体发 生相互作用 每一个软件单位对其他的单位都只有最少的 知识,而且局限于那些与本单位密切相关的 软件单位。
门面模式(Facade)和中介模式 (Mediator),都是迪米特法则应用的 例子
面向对象设计原则——包设计
REP,重用等价原则,重用的粒度就是发布的粒度 CCP,共同封闭原则,包中的所有类对于同一类性质的 变化应该是共同封闭的 CRP,共同重用原则,一个包中的所有类应该是共同重 用的 ADP,无环依赖原则,在包的依赖关系图中不允许存 在环 SDP,稳定依赖原则,朝着稳定的方向进行依赖 SAP,稳定抽象原则,包的抽象程度应该和其稳定程度 一致
抽象耦合的例子
依赖倒转原则
高层模块不应依赖于低层模块。二者都 应该依赖于抽象。 抽象不应当依赖于细节,细节应当依赖 于抽象 要针对接口编程,不要针对实现编程
依赖倒转原则的不足
导致大量的类出现,不容易实现 依赖倒转原则假定所有的具体类都是会 变化的,这一点并不总是正确的
接口隔离原则 (Interface Segregation Principle)
单一职责原则(SingleResponsibility Principle )
就像一个人身兼数职,而这些事情相互 关联不大,甚至有冲突,那他就无法很 好的解决这些职责,应该分到不同的人 身上去做才对 优点
消除耦合,减小因需求变化引起代码僵化性 臭味
单一职责原则(SingleResponsibility Principle )
遵循依赖倒换原则做些折衷处理,让对象依 赖于抽象层,降低耦合
例子
Someone、Friend和Stranger三个类
例子
Someone类有一个方法接受一个Friend类型的 变量 public class Someone { public void operation1( Friend friend ) { Stranger stranger = friend.provide() ; stranger.operation3() ; } }
狭义的迪米特法则
如果两个类不必彼此直接通信,那么这 两个类就不应当发生直接的相互作用。 如果其中的一个类需要调用另外一个类 的某一个方法,可以通过第三者转发这 个调用。
弊端
系统中存在大量的中介类,这些类之所 以存在完全是为了传递类之间的相互调 用关系——这在一定程度上增加了系统 的复杂度 解决办法
开-闭原则的关键——抽象
实现Open-Closed原则,抽象化是关键。 让模块操作一个抽象的实体。由于模块 依赖于一个固定的抽象实体,因此它可 以是不允许被修改的;同时,通过从这 个抽象体的派生,也可以扩展此模块的 行为
对可变性的封装
考虑系统中什么可能会发生变化 一种可变性不应当散落到代码的很多角落里, 而应该被封装到一个对象里 开闭原则具有理想主义的色彩,它是面向对象 设计的终极目标。 里氏代换原则(LSP)、依赖倒转原则(DIP)、 接口隔离原则(ISP)以及抽象类(Abstract Class)、接口(Interace)等等,都可以看作是 开闭原则的实现方法。
三种耦合关系
零耦合(Nil Coupling)
两个类之间没有耦合关系
具体耦合(Concrete Coupling)
两个具体的类之间,经由一个类对另一个 具体类的直接引用造成的耦合关系
抽象耦合(Abstract Coupling)
一个具体类和一个抽象类(或接口)之间 的耦合关系
具体耦合的例子
接口隔离原则例子
接口隔离原则例子
合成/聚合复用原则 (Composite/ Aggregate Reuse Principle)
定义:
在一个新的对象里面使用一些已有的对象, 使之成为新对象的一部分;新的对象通过向这些 对象的委派达到复用已有功能的目的
简单表述:
代码复用的时候要尽量的使用合成/聚合,尽 量不要使用继承
单一职责原则(SingleResponsibility Principle )
就一个类而言,应该只专注于做一件事 和仅有一个引起它变化的原因 所谓职责
可以理解为功能,就是设计的这个类功能应 该只有一个,而不是两个或更多 也可以理解为引用变化的原因,当你发现有 两个变化会要求我们修改这个类,那么你就 要考虑拆分这个类了
单一职责原则(SingleResponsibility Principle )
例子
interface Modem { public void dial(string pno); public void hangup(); public void send(char c); public void recv(); }