之前学设计模式的时候并没记录,感觉还是很重要的,就从头记录一下。
设计模式的理解
在这里我就不介绍设计模式了。我力从设计模式的思想入手,知道所有设计模式为什么要被发明出来的前提,再结合碰到的实例,来不断完善博客。
像在《设计模式》这本书中有一段话:
本书中涉及的设计模式并不描述新的或未经证实的设计,我们只收录那些在不同系统中多次使用过的成功设计。这些设计的绝大部分以往并无文本记录,它们或是来源于面向对象设计者圈子里的非正式交流,或是来源于某些成功的面向对象系统的某些部分,但对设计新手来说,这些东西是很难学得到的。尽管这些设计不包括新的思路,但我们用一种新的、便于理解的方式将其展现给读者,即:具有统一格式的、已分类编目的若干组设计模式。
在知乎上看到一个大牛的理解:
- 书中的模式不是作者的发明创造或独门秘籍,而是早已存在并已经广泛使用的做法,只不过没有被系统地加以记录。换而言之,只要遵循某些原则,这些所谓模式完全可能在无意识的状态下自发出现在产品代码中。
- 这些模式在各种系统被多次使用。换而言之,你只要接触足够多的代码,必然会大量接触到这些模式的实际应用。只不过在看过《设计模式》一书之前,你可能意识不到这是一个成体系的设计手段。
- 作者认为《设计模式》这本书的价值在于对设计模式进行了有效的组织和便于理解的描述。换而言之,这本书的写作出发点是”便于理解“,并且是面向”设计新手“的。而不少初学者却恰恰觉得这本书难以理解,这说明,作者已经在保证准确性的前提下,选用了他们所认为最便于理解的描述。比本书描述更为显浅的描述,很可能会牺牲准确性(不准确的描述对于新手来说是显然是害处大于好处)。当然某些人认为是作者表达能力有限,这种事情无法求证,但我倾向于前者。
设计模式的相关要求
看懂UML类图和时序图,有了这些知识,看后面的设计模式的结构图就没什么问题了。
UML类图和时序图
设计模式-UML类图和时序图设计模式的类型
根据设计模式的参考书 Design Patterns - Elements of Reusable Object-Oriented Software(中文译名:设计模式 - 可复用的面向对象软件元素) 中所提到的,总共有 23 种设计模式。这些模式可以分为三大类:创建型模式(Creational Patterns)
、结构型模式(Structural Patterns)
、行为型模式(Behavioral Patterns)
。当然,我们还会讨论另一类设计模式:J2EE 设计模式
。
参考自菜鸟教程
创建者模式
这些设计模式提供了一种在创建对象的同时隐藏创建逻辑的方式,而不是使用 new
运算符直接实例化对象。这使得程序在判断针对某个给定实例需要创建哪些对象时更加灵活。严格来说,简单工厂模式把那个不是GoF总结出来的23种设计模式之一。
1 | 每个设计模式前有个复选框,选中表示已经发表的,未选中表示还未发表。下同。 |
- 简单工厂模式(Simple Factory)
- 工厂方法模式(Factory Method Pattern)
- 抽象工厂模式(Abstract Factory Pattern)
- 建造者模式(Builder Pattern)
- 原型模式(Prototype Pattern)
- 单例模式(Singleton Pattern)
结构型模式
这些设计模式关注类和对象的组合。继承的概念被用来组合接口和定义组合对象获得新功能的方式。
- 适配器模式(Adapter Pattern)
- 桥接模式(Bridge Pattern)
- 过滤器模式(Filter、Criteria Pattern)
- 组合模式(Composite Pattern)
- 装饰器模式(Decorator Pattern)
- 外观模式(Facade Pattern)
- 享元模式(Flyweight Pattern)
- 代理模式(Proxy Pattern)
行为型模式
这些设计模式特别关注对象之间的通信。
- 责任链模式(Chain of Responsibility Pattern)
- 命令模式(Command Pattern)
- 解释器模式(Interpreter Pattern)
- 迭代器模式(Iterator Pattern)
- 中介者模式(Mediator Pattern)
- 备忘录模式(Memento Pattern)
- 观察者模式(Observer Pattern)
- 状态模式(State Pattern)
- 空对象模式(Null Object Pattern)
- 策略模式(Strategy Pattern)
- 模板模式(Template Pattern)
- 访问者模式(Visitor Pattern)
J2EE 模式
这些设计模式特别关注表示层。这些模式是由 Sun Java Center 鉴定的。
- MVC 模式(MVC Pattern)
- 业务代表模式(Business Delegate Pattern)
- 组合实体模式(Composite Entity Pattern)
- 数据访问对象模式(Data Access Object Pattern)
- 前端控制器模式(Front Controller Pattern)
- 拦截过滤器模式(Intercepting Filter Pattern)
- 服务定位器模式(Service Locator Pattern)
- 传输对象模式(Transfer Object Pattern)
下面用一张图来整体描述一下设计模式之间的关系:
设计模式的六大原则-源自简书
单一职责原则(Single Responsibility Principle,简称SRP )
- 核心思想:应该有且仅有一个原因引起类的变更
- 问题描述:假如有类Class1完成职责T1,T2,当职责T1或T2有变更需要修改时,有可能影响到该类的另外一个职责正常工作。
- 好处:类的复杂度降低、可读性提高、可维护性提高、扩展性提高、降低了变更引起的风险。
- 需注意:单一职责原则提出了一个编写程序的标准,用“职责”或“变化原因”来衡量接口或类设计得是否优良,但是“职责”和“变化原因”都是不可以度量的,因项目和环境而异。
里氏替换原则(Liskov Substitution Principle,简称LSP)
- 核心思想:在使用基类的的地方可以任意使用其子类,能保证子类完美替换基类。
- 通俗来讲:只要父类能出现的地方子类就能出现。反之,父类则未必能胜任。
- 好处:增强程序的健壮性,即使增加了子类,原有的子类还可以继续运行。
- 需注意:如果子类不能完整地实现父类的方法,或者父类的某些方法在子类中已经发生“畸变”,则建议断开父子继承关系 采用依赖、聚合、组合等关系代替继承。
依赖倒置原则(Dependence Inversion Principle,简称DIP)
- 核心思想:高层模块不应该依赖底层模块,二者都该依赖其抽象;抽象不应该依赖细节;细节应该依赖抽象;
- 说明:高层模块就是调用端,低层模块就是具体实现类。抽象就是指接口或抽象类。细节就是实现类。
- 通俗来讲:依赖倒置原则的本质就是通过抽象(接口或抽象类)使个各类或模块的实现彼此独立,互不影响,实现模块间的松耦合。
- 问题描述:类A直接依赖类B,假如要将类A改为依赖类C,则必须通过修改类A的代码来达成。这种场景下,类A一般是高层模块,负责复杂的业务逻辑;类B和类C是低层模块,负责基本的原子操作;假如修改类A,会给程序带来不必要的风险。
- 解决方案:将类A修改为依赖接口interface,类B和类C各自实现接口interface,类A通过接口interface间接与类B或者类C发生联系,则会大大降低修改类A的几率。
- 好处:依赖倒置的好处在小型项目中很难体现出来。但在大中型项目中可以减少需求变化引起的工作量。使并行开发更友好。
接口隔离原则(Interface Segregation Principle,简称ISP)
- 核心思想:类间的依赖关系应该建立在最小的接口上
- 通俗来讲:建立单一接口,不要建立庞大臃肿的接口,尽量细化接口,接口中的方法尽量少。也就是说,我们要为各个类建立专用的接口,而不要试图去建立一个很庞大的接口供所有依赖它的类去调用。
- 问题描述:类A通过接口interface依赖类B,类C通过接口interface依赖类D,如果接口interface对于类A和类B来说不是最小接口,则类B和类D必须去实现他们不需要的方法。
- 需注意:
- 接口尽量小,但是要有限度。对接口进行细化可以提高程序设计灵活性,但是如果过小,则会造成接口数量过多,使设计复杂化。所以一定要适度
- 提高内聚,减少对外交互。使接口用最少的方法去完成最多的事情
- 为依赖接口的类定制服务。只暴露给调用的类它需要的方法,它不需要的方法则隐藏起来。只有专注地为一个模块提供定制服务,才能建立最小的依赖关系。
迪米特法则(Law of Demeter,简称LoD)
- 核心思想:类间解耦。
- 通俗来讲: 一个类对自己依赖的类知道的越少越好。自从我们接触编程开始,就知道了软件编程的总的原则:低耦合,高内聚。无论是面向过程编程还是面向对象编程,只有使各个模块之间的耦合尽量的低,才能提高代码的复用率。低耦合的优点不言而喻,但是怎么样编程才能做到低耦合呢?那正是迪米特法则要去完成的。
开放封闭原则(Open Close Principle,简称OCP)
- 核心思想:尽量通过扩展软件实体来解决需求变化,而不是通过修改已有的代码来完成变化
- 通俗来讲: 一个软件产品在生命周期内,都会发生变化,既然变化是一个既定的事实,我们就应该在设计的时候尽量适应这些变化,以提高项目的稳定性和灵活性。
一句话概括:单一职责原则告诉我们实现类要职责单一;里氏替换原则告诉我们不要破坏继承体系;依赖倒置原则告诉我们要面向接口编程;接口隔离原则告诉我们在设计接口的时候要精简单一;迪米特法则告诉我们要降低耦合。而开闭原则是总纲,他告诉我们要对扩展开放,对修改关闭。
总结
最后总结一下如何去遵守这六个原则。对这六个原则的遵守并不是是和否的问题,而是多和少的问题,也就是说,我们一般不会说有没有遵守,而是说遵守程度的多少。任何事都是过犹不及,设计模式的六个设计原则也是一样,制定这六个原则的目的并不是要我们刻板的遵守他们,而需要根据实际情况灵活运用。对他们的遵守程度只要在一个合理的范围内,就算是良好的设计。我们用一幅图来说明一下。
图中的每一条维度各代表一项原则,我们依据对这项原则的遵守程度在维度上画一个点,则如果对这项原则遵守的合理的话,这个点应该落在红色的同心圆内部;如果遵守的差,点将会在小圆内部;如果过度遵守,点将会落在大圆外部。一个良好的设计体现在图中,应该是六个顶点都在同心圆中的六边形
在上图中,设计1、设计2属于良好的设计,他们对六项原则的遵守程度都在合理的范围内;设计3、设计4设计虽然有些不足,但也基本可以接受;设计5则严重不足,对各项原则都没有很好的遵守;而设计6则遵守过渡了,设计5和设计6都是迫切需要重构的设计。