库与框架无法帮助我们将应用组织成容易了解、容易维护、具有弹性的架构,所以需要设计模式。
模式不是发明,而是发现。模式不是代码,而是方案。
以下模式被认为是历经验证的OO设计经验。
1) 类图
2) 定义
定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它所有依赖者都会收到通知并自动更新。
3)设计原则
为了交互对象之间的松耦合设计而努力。
4)实现原理
Subject主题类是事件的主导者,通过registerObserver()注册上有需求的观察者,
通过NotifyObserver()实时更新自己状态并告知已经注册的各位观察者。
observer.update(); 由每位观察者自行处理更新的数据。
观察者构造函数里指定主题,并调用该主题的registerObserver()方法实现绑定。
public class WeatherData implements Subject { private ArrayList observers; public WeatherData() { observers = new ArrayList(); } public void registerObserver(Observer o) { observers.add(0); } public void removeObserver(Observer o) { int i = observers.indexOf(o); if (i >= 0) { observers.remove(i); } } public void notifyObserver() { for (int i = 0; i < observers.size(); i++) { Observer observer = (Observer)observers.get(i); observer.update(temperature, humidity, pressure); } } }
public class CurrentCondition { private Subject weatherData; public CurrentCondition (Subject weatherData) { this.weatherData = weatherData; weatherData.registerObserver(this); } public void update(float t, float h, float p) { display(t, h, p); } }
5)点评
如果让观察者去“拉”主题的数据,这样主题会门户大开,被大肆挖掘数据,不够安全。
所以更常用的设计是主题主动“推”送数据给观察者,主题不关心观察者的具体情况,实现松耦合。
6)应用场景
一对多的关系,多个对象实时监听一个对象的状态。
系统级更常见,比如建立一个对象,监听按键,传感器数据等。
2. 策略模式
1) 类图
2) 定义
定义了算法族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。
3)设计原则
针对接口编程,而不是针对实现编程。多用组合,少用继承。
把系统中会变化的部分抽离出来封装。
4)实现原理
在这个类中加一个接口,把行为委托给这个接口处理,
在这个接口下有各种不同的策略实现它的行为,
客户只要通过set和get动态地设置变量就可以在运行时引用正确的策略。
public abstract class Duck { FlyBehavior flyBehavior; public Duck () { } public void setFlyBehavior(FlyBehavior fb) { flyBehavior = fb; } public FlyBehavior getFlyBehavior() { return flyBehavior; } public void performFly () { flyBehavior.fly(); } } public class FlyWithWings implements FlyBehavior { public void fly() { ... ... } }
5)点评
策略模式使用委托模型,增加了对象数量,使代码较复杂,但使用对象组合,所以更具有弹性。
6)应用场景
3. 状态模式
1) 类图
基本同策略模式,唯一区别在于委托的状态是一个抽象类而不是接口。
2) 定义
允许对象在内部状态改变时改变它的行为,对象看起来好像修改了它的类。
3)点评
将一群行为封装在状态对象中,context的行为随时可委托到那些状态对象中的一个,
状态是用在Context中来代表它的内部状态以及行为的,客户不会直接改变context的状态,改变状态是方案中事先定义好的。
4)应用场景
适用于同一行为在不同条件下产生不同表现的场景,把条件看成状态,或者策略。4. 装饰者模式
1) 类图
2) 定义
动态地将责任附加到对象身上,若要扩展功能,装饰者提供了比继承更有弹性的替代方案。
3)设计原则
类应该对扩展开放,对修改关闭。
4)实现原理
public class Mocha extends CondimentDecorator { Beverage beverage; public Mocha(Beverage beverage) { this.beverage = beverage; } public double cost(){ return .20 + beverage.cost(); } }
5)点评
装饰者与被装饰者必须是同一类型,用继承达到类型匹配。
装饰者模式的设计中加入了大量的小类,会让程序变得复杂难懂。
6)应用场景
对象须要添加一个新功能,新功能具有弹性,易于修改更新替代或叠加,同时不会改变原有对象。
典型例子比如JAVA I/O里的读写功能。
5. 适配器模式
1) 类图
2) 定义
将一个类的接口,转换成客户期望的另一个接口,适配器让原本接口不兼容的类可以合作无间。
3)实现原理
客户通过目标接口调用适配器的方法对适配器发出请求。
适配器把请求转换成被适配者的接口。
客户接收到调用的结果,但并未察觉适配器在中间的作用。
public class EnumerationIterator implements Iterator { Enumeration enum; public EnumerationIterator(Enumeration enum) { this.enum = enum; } public boolean hasNext() { return enum.hasMoreElements(); } public Object next() { return enum.nextElement(); } public void remove() { throw new UnsupportedOperationException(); } }
4)点评
如果不用适配器,客户就必须改写代码来调用新的接口。
适配器允许客户使用新的库和子集合而无须改变代码,由适配器负责转换。
5)应用场景
有新的客户需求,不想改变客户代码,又想保留原有系统接口,那么考虑用适配器模式作中间层转换。
6. 外观模式
1) 类图
2)定义
提供了一个统一的接口,用来访问子系统中的一群接口,外观定义了一个高层接口,让子系统更容易使用。
3)设计原则
“最少知识”原则:只和你的密友谈话。
就任何对象而言,在该对象的方法内,我们只应该调用属于以下范围的方法:
5)应用场景
某对象具备复杂的功能,客户希望使用简单的高层接口。
典型的例子如家庭影院系统。
7. 工厂方法模式
1) 类图
2) 定义
定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个,工厂方法让类把实例化推迟到子类。
3)设计原则
依赖倒置原则:要依赖抽象,不要依赖具体类。
不能让高层组件依赖低层组件,而且两者都应该依赖于抽象。
遵循倒置原则应注意:
4)实现原理
首先声明一个工厂方法factroyMethod(),工厂方法将客户和实际创建具体产品的代码分隔开来。
子类工厂继承抽象工厂,子类工厂的工厂方法具体实现低层组件-产品。
public abstract class PizzaStore { public Pizza orderPizza(String type) { Pizza pizza; pizza = createPizza(type); pizza.prepare(); pizza.bake(); pizza.cut(); pizza.box; return pizza; } abstract Pizza createPizza(String type); } public class NYPizzaStore extends PizzaStore { Pizza createPizza(String item) { if (item.equals("cheese")) { return new NYStyleCheesePizza(); } else if (item.equals("veggie")) { return new NYStyleVeggiePizza(); } else if (item.equals("clam")) { return new NYStyleClamPizza(); } else if (item.equals("pepperoni")) { return new NYStylePepperoniPizza(); } else return null; } }客户只要实例化具体的子类工厂,让子类工厂生产产品。
public static void main () { PizzaStore nyStore = new NYPizzaStore(); Pizza pizza = nyStore.orderPizza("cheese"); }
5)点评
工厂方法让子类决定要实例化的类是哪一个,选择使用哪个子类就决定了实际创建哪个产品。
将创建对象的代码集中在一个对象或方法中,可以避免代码的重复,且方便维护。
客户在实例化对象时,只会依赖于接口,而不是具体类。
除了工厂方法模式,还有抽象工厂模式,有利于创建对象的家族。
8. 单件模式
1) 定义
确保一个类只有一个实例,并提供一个全局访问点。
2)实现原理
public class Singleton { private static Singleton uniqueInstance; private Singleton() {} public static Singleton getInstance() { if (uniqueInstance == null) uniqueInstance = new Singleton(); return uniqueInstance; } }
3)应用场景
独一无二的资源,比如线程池,缓存,对话框,偏好,日志,设备驱动等。
这些对象可能非常耗资源,而且初始化后程序一直没有使用它。
9. 命令模式
1) 类图
2) 定义
3)实现原理
关键要理清客户,发起者Invoker,接收者Receiver三者的关系以及命令对象Command与具体动作receiver.Action()之间的关系。
调用者只要调用execute()就可以发出请求,由具体命令调用接收者的一个或多个动作。
public interface Command { public void execute(); } public class LightOnCommand implements Command { //具体命令 Light light; public LightOnCommand(Light light) { this.light = light; } public void execute() { light.on(); // 具体动作 } } public class SimpleRemoteControl { //调用者 Command slot; public SimpleRemoteControl () {} public void setCommand(Command command) { //调用者持有用户的具体命令 slot = command; } public void buttonWasPressed() { //实施动作的时刻 slot.execute(); } } public class RemoteControlTest { public static void main() { SimpleRemoteControl remote = new SimpleRemoteControl(); Light light = new Light(); LightOnCommand lightOn = new LightOnCommand(light); //用户创建具体命令 remote.setCommand(lightOn); remote.buttonWasPressed(); } }
4)点评
一旦有新的动作加入,发起者并不需要改变。
5)应用场景
典型例子:多功能on/off按钮,记录状态可撤销动作,使用宏命令的party模式
更多应用:队列请求,日志请求
10. 模板方法模式
1) 类图