第二章设计模式基本要素与原则
一、基本要素
1) 模式名称(pattem name)
一个名称用来描述模式的问题、解决方案和效果。用于和同事或朋友见相互交互我们设计思想即设计结果,所以发明一个新的设计模式名称往往比发明一个设计模式要困难的多。J
2) 问题(problem)
描述了设计模式在何种情形使用。它解释了设计模式形成的前因后果,描述了特定的设计问题。问题往往就是模式必须满足的一系列先决条件
3) 解决方案(solution)
描述模式的组成成分,成分之间的相互关系以及各自的指着和协作方式。因为模式好比一个模板,他它应用于不同的场合(但这些场合待解决的问题的性质是一样的)所以解决方案并不描述一个特定具体的设计或实现而是提供怎样用一个具有典型的元素组合来解决这个问题
4) 效果(consequences)
效果用来描述设计模式的利弊,效果往往是我们权衡模式是否可用的重要因素,模式效果包括它对系统的灵活性、扩展性或可遗址性的影响(复用是面向对象设计的重要要素之一)。对我们理解和评价模式起了很重要的客观依据
二、设计模式遵循的几个原则
1) 开——闭 原则
提出者:Bertrand Meyer
英文名:OCP (Open-Closed Principle)
内 容:Software entities should be open for extension,but closed for modification.(一个软件实体应当对扩展开发,对修改关闭)
理 解:模块应对扩展开放,对修改关闭。我们在重构代码时尽量在不修改原来代码的情况下进行扩展。
优 点:
² 扩展已有系统提供新的行为。
² 在扩展的同时对旧版本有很好的支持。
² 使得系统更加灵活和很强的适应性。
2) 里氏代换 原则
提出者:Bertrand Liskov
英文名:LSP (Liskov Substitution Principle)
内 容:子类型(subtype)必须能够替换他们的基类型。(如果调用父类型的话,那么换成子类也可以完成)是继承复用的基础
理 解:在模块中应当尽量从抽象类中继承,而不是从具体的类中继承。里氏代换只能子类代换父类,反过来是决对不可以的。
优 点:
² 衍生类可以替换掉基类,软件不受到影响。
² 在扩展的同时对旧版本有很好的支持。
² 软件代码复用的重要基础。
3) 合成复用 原则
英文名:CARP (Composition Aggregation Reference Principle)
合成和聚合都是关联的特殊种类,聚合表示整体和部分的关系,表示“拥有”;合成则是一种更强的“拥有”,部分和整体的生命周期是一样的,合成的新的对象完全支配其组成部分,包括他们的创建和湮灭。一个合成关系的成分对象是不能与另一个合成关系共享的
内 容:合成是值的聚合(Aggregation by Value),聚合是引用的聚合(Aggregation by Reference)
理 解:
1. 聚合就是多个事物聚集在一起打个比方一个大型的商场有多个摊位(做生意的人都知道)每个摊位里摆放的商品集合起来吸引众多买家来购物就形成了繁华的商场,这里可以看出每个摊位的商品不属于商场,商场关门了这些商品可以拿到其他商场中去卖所以商品和商场的关系就是聚合关系;回过头来我们看下摊位和商场的关系,摊位是随着商场的建立而孕育而生的(商场就靠这个来赚钱呢)商场没有了摊位也随之消失所以摊位和商场的关系就是一种合成关系摊位伴随着商城的建立和湮灭他们的生命周期是一样的。
2. 一个合成关系的成分对象是不能与另一个合成关系共享的。我们怎么去理解呢。2个商场 中央商场和大洋百货,两个商场是独立的没有隶属关系(偶在南京),中央商场的摊位和大洋百货是不可能有任何隶属关系的我们不可以说中央商场的摊位是属于大洋百货的(你这话说出来估计有人要。。。。)很好理解吧你直接可以理解为私有财产
3. 我们在开发中尽量使用合成/聚合,而不是使用继承。
在OOD中有两种基本的办法实现服用
1. 通过合成、聚合
优 点:
² 黑箱复用,因为成分对象的内部实现细节新的对象是看不见的。
² 支持包装。
² 复用所需的依赖较少。(这个就是我为什么首选合成聚合而不用继承的主要原因)
² 新类可以把焦点集中在一个任务上,不去关心类成分间的复杂关系
² 复用是可以在运行时间内动态进行,新对象可以动态的引用与成分对象类型相同的对象
² 新对象存取成分对象的唯一方法是通过成分对象的接口(这个也埋下一个缺点)
缺点:
² 系统中会有很对对象需要管理。
2. 继承(很多程序员在初始理解设计模式时复用就是继承这个是错误的,我们在设计模式时优先选择前种,继承我个人理解成合成聚合实现不了时我会用继承)
优 点:
² 新类实现比较容易
² 修改和扩展继承而来的实现比较容易
缺点:
² 继承复用破坏了包装,因为继承讲超类的实现细节暴露给了子类,这种复用是透明的复用,又称“白箱”复用
² 如果超类发生改变,那么子类的实现不得不发生改变
² 从超类继承来的实现是静态的,不可能在运行时间内发生改变,没有足够的灵活性
² 继承只能在有限的环境中使用
何时用继承:
² 子类是超类的一个特殊种类。而不是超类的一个角色,区分“Has-A”和“Is-A”。只有“Is-A”(…是…)关系才符合继承关系,“Has-A”(…有…)关系应当用聚合来描述
补充:
错误的理解“Has-A”和“Is-A”
真确理解“Has-A”和“Is-A”
² 永远不会出现需要将子类换成另外一个类的子类的情况。如果不能肯定就不要用继承
² 子类扩展了超类的责任,而不是具有换调(override)或注销掉(Nulify)超类的责任。如果子类需要大量的置换草类的行为,那么这个类就不因该是超类的子类
4) 依赖倒转 原则
英文名:DIP(Dependence Inversion Principle)
内 容:抽象不应该依赖与细节,细节应当依赖与抽象;要针对接口编程,不要针对实现编程;传递参数,或者在组合聚合关系中,尽量引用层次高的类;
理 解:程序在需要引用一个对象时,应当尽可能的使用抽象类型作为变量的静态方法,这个就是针对接口编程的含义。是达到“开-闭”原则的途径。针对接口编程和抽象类就是要做到一个具体的类应当只实现类的几口和抽象类中声明的方法,而没有多余的方法。做到DIP关键是抽象耦合,LSP是DIP的基础
5) 接口隔离 原则
英文名:ISP(Interface Segregation Principle)
内 容:使用多个专门的接口比使用单一接口要好,过于臃肿的接口是对就口的污染。
理 解:一个类对另一个类的依赖性应当是建立在最小的接口上。不应该强迫客户依赖于他们不用的方法。我们在OOD时恰当的划分角色和角色对应的接口是非常重要的。讲没有关系的接口合并在一期是对角色和接口的一种污染,如果把一些看上去差不多的几口合并成一个几口,貌似是对代码的优化,其实是错误的。原则要求我们在不同的角色要给不同的接口,而不能交给一个接口。向客户提供的接口就像你对客户提供的承诺,承诺越少越好(那些生产厂家天天就琢磨这个了,没想到吧)
优 点:
² 系统可维护性高(我承诺的少当然维护性高了哇哈哈)
6) 抽象 原则
英文名:AP(Abstract Principle)
内 容:抽象类不会有实例,一般作为父类被其他类继承,包含了子类的共同属性和方法
理 解:OOD是好的抽象类是尽可能多的包含共同代码,具体类是不被其他类所继承的。换句话说子类继承了抽象类后,这个子类不应被其他类所继承
7) 迪米特法则
英文名:LoD(Law of Demeter)
内 容:又叫最少知识原则(Least Knowledge Principle LKP)。一个对象应当对其他对象尽可能的少了解。
理 解:俗话说了解的越多,越烦恼这就体现出来了。每个人都听过妈妈的一句“不要跟陌生人说话”。这里的最少知道原则就是让每个类专心的做自己的事情而不去关心其他的事情这样的好处就是尽量的减少了耦合度。
优 点:
² 减少类见的耦合度
总结
通过以上的学习我们基本上把所有设计模式用到的准则都梳理了一遍,细心的朋友可以发现这些原则有不同的层次分类我把他们分成 设计目标和设计细则
在7个原则的内容中他们其实都在解决一个问题,怎样尽可能的封装变化,哪里有变化我们就封装哪里,所以设计模式就是一个封装变化的过程