我认为,本章是重点中的重点。并非23个模式都被广泛应用,其中常用和最为有效的大概有15个模式。
1、适配器(Adapter)
1)、使用场景
使用一个已经存在的类,但如果他的接口,也就是他的方法和你的要求不相同时,考虑使用是适配器模式。
就是说,双方都不修改自己的代码的时候,可以采用适配器模式。
2)、结构图
3)、相关模式
外观对象:隐藏外部系统的资源适配器也被视为外观对象,因为资源适配器使用单一对象封装了对子系统或系统的访问。
资源适配器:当包装对象时为不同外部接口提供适配时,该对象叫资源适配器
4)、准则
类名后缀为“Adapter”。
5)、用到的GRASP原则
2、工厂模式
1)、使用场景
该模式也常称为“简单工厂”或“具体工厂”。如: 1)、存在复杂创建逻辑 2)、为提高内聚而分离创建者职责(关注点分离) 因此,创建称为工厂的纯虚构对象来处理这些创建职责。
2)、结构
一般xxxFactory应该是单实例类。
3)、相关模式
通常使用 单例模式 来访问工厂模式。
由谁创建工厂呢?一般采用单例模式。
3、单例模式
1)、使用场景
只有唯一实例的类即为“单实例类”。对象需要全局可见性和单点访问。
因此,建议对类定义静态方法用以返回单实例。
2)、相关模式
单例模式:通常用于创建工厂对象和外观对象
以上整合例子:
4、策略模式
1)、使用场景
销售的定价策略(也可叫做规则、政策或算法)具有多样性。在一段时间内,对于所有的销售可能会有10%的折扣,后期可能会对超出200元的销售给予10%的折扣,并且还会存在其他大量的变化。
因此,在单独的类中分别定义每种策略/规则/政策/算法,并且使其具有共同接口。
2 )、结构
策略模式,共同的方法内传入的参数,通常是上下文对象,上图就是sale。
3)、结合工厂模式
1)、使用工厂模式创建这些策略类
2)、使用单例模式创建工厂类。
5、组合模式
1)、使用场景
如果有重叠怎么办?比如: 1)老年人折扣20% 2)购物金额满200元享受15%折扣 因此,如何能够处理像原子对象一样,(多态的)处理一组对象或具有组合结构的对象呢? 答:定义组合和原子对象的类,使他们具有相同的接口。
2)、结构
2)、相关模式
通常与策略和命令模式一起使用。
6、外观模式
1)、使用场景
对一组完全不同的实现或接口(如子系统中的实现和接口)需要公共、统一的接口,隐藏掉子系统复杂的实现或接口。从而使关注点分离。
如:slf4j、通信前置
其与适配器的区别在于,facade模式针对同职能接口的抽象并提供公共、统一的接口,强度的是统一。而adapter模式针对的是适配不同接口且老接口不改的场景,强调的是适配。
2)、结构
3)、例子
class Program { static void Main(string[] args) { Facade facade = new Facade(); facade.MethodA(); facade.MethodB(); Console.Read(); } } class SubSystemOne { public void MethodOne() { Console.WriteLine(" 子系统方法一"); } } class SubSystemTwo { public void MethodTwo() { Console.WriteLine(" 子系统方法二"); } } class SubSystemThree { public void MethodThree() { Console.WriteLine(" 子系统方法三"); } } class SubSystemFour { public void MethodFour() { Console.WriteLine(" 子系统方法四"); } } class Facade { SubSystemOne one; SubSystemTwo two; SubSystemThree three; SubSystemFour four; public Facade() { one = new SubSystemOne(); two = new SubSystemTwo(); three = new SubSystemThree(); four = new SubSystemFour(); } public void MethodA() { Console.WriteLine(" 方法组A() ---- "); one.MethodOne(); two.MethodTwo(); four.MethodFour(); } public void MethodB() { Console.WriteLine(" 方法组B() ---- "); two.MethodTwo(); three.MethodThree(); } }
4)、相关模式
外观模式通常通过单例模式访问。
7、观察者模式(Observer)
1)、定义
观察者模式又叫“发布-订阅(Publish/Subscribe)”模式、又叫“委派事件模型”。
之所以被称为观察者模式,是因为监听器或订阅者在对相应事件进行观察。
之所以被称为委派事件模型,是因为发布者将事件处理委派给了监听器(订阅者)
2)、使用场景
例子:支付成功后, 1)记录日志 2)写数据库 3)前端展示支付结果 传统方式,将以上3个关注点耦合在一起写。随着业务需求越来越多,导致很难维护。
总结:当一个对象的改变需要同时改变其他对象时,而且还不知道有多少对象有待改变时,应该考虑使用观察者模式。
3)、结构
4)、例子
public abstract class Subject { private List<Observer> observers = new ArrayList<Observer>(); //增加观察者 public void attach(Observer observer) { observers.add(observer); } //移除观察者 public void detach(Observer observer) { observers.remove(observer); } //通知 public void notify1() { observers.forEach(o->o.update()); } } public abstract class Observer { public abstract void update(); } public class ConcreteSubject extends Subject { private String subjectState; public String getSubjectState() { return subjectState; } public void setSubjectState(String subjectState) { this.subjectState = subjectState; } } public class ConcreteObserver extends Observer { private String name; private String observerState; private ConcreteSubject subject; public ConcreteObserver( ConcreteSubject subject,String name) { this.subject = subject; this.name = name; } @Override public void update() { observerState = subject.getSubjectState(); System.out.println("观察者"+name+"的新状态是"+observerState); } public ConcreteSubject getSubject() { return subject; } public void setSubject(ConcreteSubject subject) { this.subject = subject; } } public class Main { public static void main(String[] args) { ConcreteSubject s = new ConcreteSubject(); s.attach(new ConcreteObserver(s, "X")); s.attach(new ConcreteObserver(s, "Y")); s.attach(new ConcreteObserver(s, "Z")); s.setSubjectState("ABC"); s.notify1(); } } --输出 观察者X的新状态是ABC 观察者Y的新状态是ABC 观察者Z的新状态是ABC
5)、小结
A、一个事件支持多个订阅者
public static void main(String[] args) { ConcreteSubject s = new ConcreteSubject(); //多个订阅者 s.attach(new ConcreteObserver(s, "X")); s.attach(new ConcreteObserver(s, "Y")); s.attach(new ConcreteObserver(s, "Z")); //一个事件 s.setSubjectState("ABC"); s.notify1(); }
B、订阅者可动态添加或删除
public abstract class Subject { private List<Observer> observers = new ArrayList<Observer>(); //增加观察者 public void attach(Observer observer) { observers.add(observer); } //移除观察者 public void detach(Observer observer) { observers.remove(observer); } //通知 public void notify1() { observers.forEach(o->o.update()); } }