一、引子
从学编程一开始就被告知,要想做一名优秀的程序员两大必要技能:1.源码阅读(JDK、C等底层语言封装) 2.设计模式(使用某种语言优雅的落地典型场景功能)。一般随着工作年限的增长,被迫对底层语言/框架源码阅读的越来愈多,但是设计模式如不刻意去学习,永远不会真正掌握。笔者把设计模式比喻成程序员的“绝世神功”,掌握了设计模式,对快速阅读源码、优雅地编写程序有极大的促进作用,可以说就像打通了任督二脉一样。
1.1 设计模式由来
1995年,GoF(Gang of Four四人帮)合作出版了《设计模式:可复用面向对象软件的基础》(Design Patterns – Elements of Reusable Object-Oriented Software) 一书,书中总结了23种面向对象设计模式,也就是大名鼎鼎的GOF设计模式。
1.2 23种 VS 24种
一直听说有24种设计模式(网上一大堆标题24种设计模式,然后列举的只有23种,也是无语),公认的GOF只有23种,还有一种是啥?大部分文章都说是简单工厂模式(Simple Factory)。笔者搜遍全网(连chatGPT也问了),没找到第24种设计模式谁提出的。这第24种设计模式,有说是简单工厂模式,也有说是静态工厂模式,也有说简单工厂模式=静态工厂模式,真是一塌糊涂,乱七八糟。神奇的是这些文章的作者都没深究过这个问题,估计大都是拿来主义。
到底几种?
---23种!!!即GOF的23种设计模式即可。还有2个模式:简单工厂、静态工厂,确实在后续应用比较多,但在1995年未列入GOF。
二、知识预备
想要练成绝世武功,内功心法和外家套路缺一不可。要想练成“设计模式”这一绝世武功,“面向对象设计原则”就是内功心法(遵循的设计原则),“UML统一建模语言”就是外家套路(代码落地建模工具)。练功之前,咱们先简单学习下这2个必备的基础技能。
2.1 面向对象设计原则
面向对象设计的目标之一就是支持可维护性复用,一方面需要实现设计方案的重用,另一方面要确保系统易于拓展和修改,具有较好的灵活性。7种面向对象设计原则,其中前5条是强约束性的建议都做到,后2条尽量做到即可。
1、单一职责原则(Single Responsibility Principle , SRP):一个对象应该只包含单一的职责,并且该职责被完整地封装在一个类中。--单一职责原则是实现高内聚低耦合的指导方针。
理解:
- 一个类只负责一个职责。
2、开闭原则(Open Close Principle):对扩展开放,对修改关闭。--开闭原则是面向对象设计的目标。
理解:
- 使用接口和抽象类。实现不修改原代码,又可以拓展新方法。
3、里氏代换原则(Liskov Substitution Principle):所有引用基类(父类)的地方必须能透明地使用其子类的对象。--里氏代换原则是实现开闭原则的基础。
理解:
- 把父类设计为抽象类或者接口。
- 子类必须实现父类的所有方法。
4、依赖倒转原则(Dependence Inversion Principle):高层模块不应该依赖低层模块,它们都应该依赖抽象。抽象不依赖于细节,细节应该依赖于抽象。--依赖倒转原则就是面向对象设计的主要手段。
理解:
- 要针对接口编程,不要针对实现编程。
- 依赖注入:将一个类的对象传入另一个类,注入时尽量注入父类对象,程序运行时通过子类对象覆盖父类对象。
5、接口隔离原则(Interface Segregation Principle):客户端不应该依赖那些它不需要的接口(方法),--接口级的单一职责原则。
理解:
- 大接口要分割成小接口,接口专用。
- 客户端使用专用接口。
6、迪米特法则(Demeter Principle):即最少知道原则,一个实体应当尽量少的与其他实体之间发生相互作用,减少耦合。
理解:
- 依赖者只依赖该依赖的对象,被依赖者只暴露该暴露的对象。
7、合成复用原则(Composite Reuse Principle):尽量使用合成/聚合的方式,而不是使用继承,降低耦合。
理解:
- 少用继承,降低耦合。
2.2 UML建模
UML1.X有9个图,UML2.0有12个图,具体如下图所示:
一般,我们掌握 UML1.X 常用的9种图即可,分为两类:
- 静态图:用例图、类图、包图、对象图、部署图
- 动态图:顺序图(时序图),通信图(UML1.x 时称为协作图),状态机图,活动图
本文需要使用静态图的一种:类图。关注类图中类之间的6种关系(耦合度大小排序):泛化 = 实现 > 组合 > 聚合 > 关联 > 依赖。
2.2.1 类图-关联关系
- 1.泛化:指的是继承关系,表达一般和特殊。符号:空心三角箭头的实线,箭头指向父类。(注:UML中只有泛化,继承是开发角度的描述。)
- 2.实现:指的是类与接口的关系,表达类实现了接口的特征行为。符号:带三角箭头的虚线,箭头指向接口。
- 3.组合:指的是整体与部分的关系, 但部分不能离开整体而单独存在。符号:带实心菱形的实线,菱形指向整体。
- 4.聚合:指的是整体和部分关系,且部分可以离开整体而单独存在。符号:空心菱形的实心线,菱形指向整体。
- 5.关联:指的是类和类的关系,表达一个类知道另一个类的属性和方法。符号:带普通箭头(或实心三角形箭头)的实心线。
- 6.依赖:指的是使用的关系, 即一个类的实现需要另一个类的协助, 所以要尽量不使用双向的互相依赖。符号:带箭头的虚线,指向被依赖的类。
2.2.2 类图-表示方法
- 从上到下分为3部分:类名、属性、方法。
- 符号含义:+ public、- private、#protected
三、设计模式
本节列举24种设计模式,其中创建型模式6种、结构型模式7种、行为型模式11种。每个模式从3个角度进行分析:定义、UML类图、应用。
3.1 创建型模式
创建型模式,就是用来创建对象的。包含3块:单例/原型模式、工厂模式(简单工厂、工厂方法、抽象工厂)、建造者模式。
1.单例模式(Singleton)
定义:保证一个类仅有一个实例,并提供一个访问它的全局访问点。
UML类图:
应用:待补充
2.原型模式(Prototype)
定义:用原型实例指定创建对象的种类,并且通过拷贝这个原型来创建新的对象。
UML类图:
3.简单工厂模式(Static Factory Method Pattern)
定义:定义一个工厂类,使用static方法创建对象。根据不同参数返回不同实例,每增加一个对象需要修改工厂类,违背了“开闭原则”。--不推荐使用。
UML类图:
应用:
- Calendar 类获取日历类对象,static getInstance方法,根据参数获取一个Calendar子类对象。
- Logback 中的 LoggerFactory日志工厂 ,static getLogger方法根据name/Class获取 Logger 对象。
- Spring的BeanFactory接口,getBean(),根据name/Class获取不同对象,但不是static修饰的,严格来说不算简单工厂。
4.工厂方法模式(Factory Method)
定义:定义一个用于创建对象的接口,让子类决定将哪一个类实例化。此模式使一个类的实例化延迟到其子类。
UML类图:
应用:
- java.net.URLStreamHandlerFactory作为工厂方法接口,定义了抽象方法createURLStreamHandler创建产品URLStreamHandler。工厂方法实现类为sun.misc.Launcher内部类Factory。
5.抽象工厂模式(Abstract Factory)
定义:提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。用于创建一个产品族的产品。
UML类图:
应用:
- java.sql.Connection接口就是抽象工厂接口,定义了创建Statement产品族:Statement createStatement()、PreparedStatement prepareStatement(String sql)、CallableStatement prepareCall(String sql) 。PgConnection impl BaseConnection(extends Connection)就是一个具体工厂,专用于PostgreSql数据库的连接工厂。PgConnection复写了Statement产品族(3个创建)方法。
6.建造者模式(Builder)
定义:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
UML类图:
应用:
- 实际应用中,具体建造者Builder一般直接使用静态内部类实现。创建产品时类似:XXX.Builder(参数1,参数2,...).build()。Mybatis源码org.apache.ibatis.builder包下:MapperBuilderAssistant指挥者中,产品有:ParameterMap、ParameterMapping、ResultMap、、ResultMapping、Discriminator、MappedStatement等。每个产品都有自己的建造者静态内部类Builder(定义了build())。
3.2 结构型模式
1.适配器模式(Adapter)
定义:将一个类的接口转换成客户希望的另外一个接口。Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
UML类图:
应用:
Spring AOP中,org.springframework.aop.framework.adapter包下的AdvisorAdapter接口,有3个适配器实现类:AfterReturningAdviceAdapter、MethodBeforeAdviceAdapter、ThrowsAdviceAdapter把每一个Advisor中的Advice都要适配成对应的MethodInterceptor对象。这里使用的是方式二对象适配器的变种。比如MethodBeforeAdviceAdapter适配器通过getInterceptor()获取到MethodBeforeAdviceInterceptor,最终结合拦截器模式(非GOF23),调用advice的advice.before()实现。
- 目标接口:AdvisorAdapter接口
- 适配者:MethodBeforeAdvice接口
- 适配器:MethodBeforeAdviceAdapter(advisor->advice)+MethodBeforeAdviceInterceptor(把适配者advice做入参构造,最终调用适配者方法执行)
比较复杂,图示如下:
2.桥接模式(Bridge)
定义:将抽象部分与它的实现部分分离,使它们都可以独立地变化。
UML类图:
拆出一个维度出来,进行聚合,可有效减少子类的个数。原本维度1个数*维度2个数->维度1个数的子类+维度2个数的聚合实现+1个聚合接口
应用:
java.sql包下的Driver和Connection接口,通过DriverManager进行桥接(不像图中的聚合实现)。DriverManager定义了registerDriver和getConnection方法。registerDriver可以注册各种驱动。Connection接口可实现连接多种数据库。2个维度分开进行拓展实现,进行了脱耦。
3.组合模式(Composite)
定义:将对象组合成树形结构以表示“部分-整体”的层次结构。它使得客户对单个对象和复合对象的使用具有一致性。
UML类图:
组合模式分为透明式(树枝特有方法在抽象类中体现,树叶类复写空实现,客户端使用时无需区分树枝还是树叶)和安全式(树枝特有方法在树枝类中体现)。当节点具有一致行为时采用透明式,当各节点行为不一致较多或树枝节点较为健壮时采用安全式。
应用:
- Map接口作为Component容器接口;HashMap作为Composite树枝、Node实现了Map接口下的唯一接口Entry<K,V>kv映射实体接口作为Leaf树叶,HashMap有一个属性Node<K,V>[] table链表首歌节点组成的数组,可理解为聚合了Map接口。HashMap复写putAll(Map<? extends K, ? extends V> m),可理解为树枝的特有方法(Node没有),很明显这是安全式组合模式。
4.装饰器模式(Decorator)
定义:动态地给一个对象添加一些额外的职责。就扩展功能而言, 它比生成子类方式更为灵活。
UML类图:
应用:待补充
5.外观模式(Facade)
定义:为子系统中的一组接口提供一个一致的界面,Facade模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。
UML类图:
应用:待补充
6.享元模式(Flyweight)
定义:运用共享技术有效地支持大量细粒度的对象。
UML类图:
应用:待补充
7.代理模式(Proxy)
定义:为其他对象提供一个代理以控制对这个对象的访问。
UML类图:
应用:待补充
3.3 行为型模式
1.责任链模式(Chain of Responsibility)
定义:解除请求的发送者和接收者之间耦合,而使多个对象都有机会处理这个请求。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它。
UML类图:
应用:待补充
2.命令模式(Command)
定义:将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可取消的操作。
UML类图:
应用:待补充
3.解释器模式(Interpreter)
定义:给定一个语言, 定义它的文法的一种表示,并定义一个解释器, 该解释器使用该表示来解释语言中的句子。
UML类图:
应用:待补充
4.迭代器模式(Iterator)
定义:提供一种方法顺序访问一个聚合对象中各个元素,而又不需暴露该对象的内部表示。
UML类图:
应用:待补充
5.中介者模式(Mediator)
定义:用一个中介对象来封装一系列的对象交互。中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。
UML类图:
应用:待补充
6.备忘录模式(Memento)
定义:在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到保存的状态。
UML类图:
应用:待补充
7.观察者模式(Observer)
定义:定义对象间的一种一对多的依赖关系,以便当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并自动刷新。
UML类图:
应用:观察者模式
8.状态模式(State)
定义:允许一个对象在其内部状态改变时改变它的行为。对象看起来似乎修改了它所属的类。
UML类图:
应用:待补充
9.策略模式(Strategy)
定义:定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换。本模式使得算法的变化可独立于使用它的客户。
UML类图:
应用:待补充
10.模板方法模式(Template Method)
定义:定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。Template Method使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
UML类图:
应用:待补充
11.访问者模式(Visitor)
定义:表示一个作用于某对象结构中的各元素的操作。它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。
UML类图:
应用:待补充
===参考===================
设计模式总结
设计模式与23种设计模式的简单介绍
浅谈UML中常用的9种图