所有结构良好的面向对象软件体系结构都包含了许多模式,当我们评估一个面向对象系统的质量时,有效的方法之一就是要判断系统的设计者是否强调了对象之间的公共协同关系。
设计模式不是一本介绍面向对象设计的书,也不是一本高级的专题技术论文,它描述了在面向对象设计过程中针对特定问题的简洁而优雅的解决方案。所以自己应该总结出每个模式所针对的特定问题,这样有助于我们将业务场景抽象为特定问题,然后运用模式高效解决。
所有的模式均可使用标准的面向对象语言实现,这有时会比特殊的解法多费一些功夫,但是为了增加软件的灵活性和可复用性,多做些工作是值得的。
模式的交叉引用将给你提供寻找其他相关模式的逻辑路径,它将帮助你看清楚模式是如何相互关联的、一个模式怎样与其他模式进行组合、以及哪些模式能在一起工作。下图将用图示方法展现这种关系。
阅读模式分类描述的另一种方法是问题导向法,你可以翻到书中的第1.6节查找有关设计可复用的面向对象系统过程中经常见到的问题,然后阅读解决这些问题的有关模式。有些读者首先通读模式分类描述,然后运用问题导向的方法将模式应用于他们的项目之中。
编写软件过程中,程序员面临着来自耦合性,内聚性以及可维护性,可扩展性,重用性,灵活性等多方面的挑战,设计模式是为了让程序(软件),具有更好的以下特性:
1.代码重用性(即:相同功能的代码,不用多次编写)
2.可读性(即:编程规范性,便于其他程序员的阅读和理解)
3.可扩展性(即:当需要增加新的功能时,非常的方便,称为可维护
4.可靠性(即:当我们增加新的功能后,对原来的功能没有影响)
5.使程序呈现高内聚,低耦合的特性
设计模式原则,其实就是程序员在编程时,应当遵守的原则,也是各种设计模式的基础(即:设计模式为什么这样设计的依据)
设计模式原则,其实就是程序员在编程时,应当遵守的原则,也是各种设计模式的基础(即:设计模式为什么这样设计的依据)
T
1.单一职责原则
2.接口隔离原则
3.依赖倒转(倒置)原则
4.里氏替换原则
5.开闭原则
6.迪米特法则
7.合成复用原则
对类来说的,即一个类应该只负责一项职责。如类A负责两个不同职责:职责1,职责2。当职责1需求变更而改变A时,可能造成职责2执行错误,所以需要将类A的粒度分解为 A1,A2
客户端不应该依赖它不需要的接口,当一个类对另一个类的依赖是基于接口时,应该建立在最小的接口上。
案例:
案例描述:类A借由接口1依赖(使用)类B,但是只使用了接口1的1、2、3方法。同理,类C借由接口1依赖(使用)类D,但是只使用了接口1的1、4、5方法。如果接口Interface1对于类A和类c来说不是最小接口,那么类B和类D必须去实现他们不需要的方法
按隔离原则应当这样处理:将接OInterface1拆分为独立的3个接口,类A和类c分别与他们需要的接口建立依赖关系。也就是采用接口隔离原则,满足接口隔离原则的类图如下:
依赖倒转原则(Dependence Inversion Principle)是指:
1.高层模块不应该依赖低层模块,二者都应该依赖其抽象
2.抽象不应该依赖细节,细节应该依赖抽象
3.依赖倒转(倒置)的中心思想是面向接口编程
4.依赖倒转原则是基于这样的设计理念:相对于细节的多变性,抽象的东西要稳定的多。以抽象为基础搭建的架构比以细节为基础的架构要稳定的多。在java中, 抽象指的是接口或抽象类,细节就是具体的实现类
5.使用接口或抽象类的目的是制定好规范,而不涉及任何具体的操作,把展现细节的任务交给他们的实现类去完成
概念:所有引用基类的地方必须能透明地使用其子类的对象。
实现操作:在子类中尽量不要重写父类的方法
里氏替换原则告诉我们,继承实际上让两个类耦合性增强了,在适当的情况下,可以通过聚合,组合,依赖来解决问题。
1.开闭原则(Open Closed Principle)是编程中最基础、最重要的设计原则
2.一个软件实体如类,模块和函数应该对扩展开放(可以增加类),对修改关闭。用抽象构建框架,用实现扩展细节。
3.当软件需要变化时,尽量通过扩展软件实体的行为来实现变化,而不是通过修改已有的代码来实现变化。
4.编程中遵循其它原则,以及使用设计模式的目的就是遵循开闭原则。
1.一个对象应该对其他对象保持最少的了解
2.类与类关系越密切,耦合度越大
3.迪米特法则(Demeter Principle)又叫最少知道原则,即一一个类对自己依赖的类知道的越少越好。也就是说,对于被依赖的类不管多么复杂,都尽量将逻辑封装在类的内部。对外除了提供的public方法,不对外泄露任何信息
4.迪米特法则还有个更简单的定义:只与直接的朋友通信
5.直接的朋友:每个对象都会与其他对象有耦合关系,只要两个对象之间有耦合关系我们就说这两个对象之间是朋友关系。耦合的方式很多,依赖,关联,组合,聚合等。其中,我们称出现成员变量,方法参数,方法返回值中的类为直接的朋友,而出现在局部变量中的类不是直接的朋友。也就是说,陌生的类最好不要以局部变量的形式出现在类的内部。
合成复用原则(Composite Reuse Principle,CRP)又叫组合 / 聚合复用原则(Composition/Aggregate Reuse Principle,CARP)。它要求在软件复用时,要尽量先使用组合或者聚合等关联关系来实现,其次才考虑使用继承关系来实现。
1.找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起。
2.针对接口编程,而不是针对实现编程。
3.为了交互对象之间的松耦合设计而努力
设计模式分为三种类型,共23种
1.创建型模式:单例模式、抽象工厂模式、原型模式、建造者模式、工厂模式。(强调我们应该怎么去设计对象的创建)
创建型模式用于封装和管理对象的创建,其特点是:“创建与使用相分离”
2.结构型模式:适配器模式、桥接模式、装饰模式、组合模式、外观模式、享元模式、代理模式。(站在软件结构的角度进行设计,使结构更加具备伸缩性、扩展性)
3.行为型模式:模版方法模式、命令模式、访问者模式、迭代器模式、观察者模式、中介者模式、备忘录模式、解释器模式(Interpreter模式) 、状态模式、策略模式、职责链模式(责任链模式)。(站在方法的角度进行设计)
现实生活中,原始社会自给自足(没有工厂),农耕社会小作坊(简单工厂,民间酒坊),工业革命流水线(工厂方法,自产自销),现代产业链代工厂(抽象工厂,富士康)。我们的项目代码同样是由简到繁一步一步迭代而来的,但对于调用者来说,却越来越简单。
酒分啤酒、白酒、红酒三种,创建酒的方式如下:
// 分别创建啤酒、白酒、红酒
if ("beer".equals(type)) {
return new Beer();
} else if ("liquor".equals(type)) {
return new Liquor();
} else if ("redWine".equals(type)) {
return new RedWine();
}
传统生产方式为:每家每户都需要自己书写上述的创建酒对象代码,导致创建对象的代码泛滥。
传统方式的缺点:上述代码不符合开闭原则的,所以当酒类的种类增加时,每处创建酒对象的代码都需要修改。
传统方式的主要角色如下:
传统方式结构图如下:
工厂模式的定义:定义一个创建产品对象的工厂接口,将产品对象的实际创建工作推迟到具体子工厂类当中。
根据产品是具体产品还是具体工厂可分为简单工厂模式和工厂方法模式,根据工厂的抽象程度可分为工厂方法模式和抽象工厂模式。
我们把被创建的对象称为 “产品”,把创建产品的对象称为 “工厂”。如果要创建的产品不多,只要一个工厂类就可以完成,这种模式叫 “简单工厂模式”。如上示例:我们把创建酒的代码封装到指定的一个工厂类中,这样我们有新酒的种类时,只需要修改该类就可,其它有调用到工厂类创建方法来获取酒对象的代码就不需要修改了 -> 简单工厂模式
应用场景
对于产品种类相对较少的情况,考虑使用简单工厂模式。使用简单工厂模式的客户端只需要传入工厂类的参数,不需要关心如何创建对象的逻辑,可以很方便地创建所需产品。
简单工厂模式的主要角色如下: