一、Java观察者模式
当对象间存在一对多关系时,则使用观察者模式(Observer Pattern)。比如,当一个对象被修改时,则会自动通知依赖它的对象。观察者模式属于行为型模式。
介绍
意图:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
主要解决:一个对象状态改变给其他对象通知的问题,而且要考虑到易用和低耦合,保证高度的协作。
何时使用:一个对象(目标对象)的状态发生改变,所有的依赖对象(观察者对象)都将得到通知,进行广播通知。
如何解决:使用面向对象技术,可以将这种依赖关系弱化。
关键代码:在抽象类里有一个 ArrayList 存放观察者们。
应用实例:
① 拍卖的时候,拍卖师观察最高标价,然后通知给其他竞价者竞价。
② 西游记里面悟空请求菩萨降服红孩儿,菩萨洒了一地水招来一个老乌龟,这个乌龟就是观察者,他观察菩萨洒水这个动作。
优点
① 观察者和被观察者是抽象耦合的。
② 建立一套触发机制。
缺点:
① 如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。
② 如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。
③ 观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。
使用场景
• 一个抽象模型有两个方面,其中一个方面依赖于另一个方面。将这些方面封装在独立的对象中使它们可以各自独立地改变和复用。
• 一个对象的改变将导致其他一个或多个对象也发生改变,而不知道具体有多少对象将发生改变,可以降低对象之间的耦合度。
• 一个对象必须通知其他对象,而并不知道这些对象是谁。
• 需要在系统中创建一个触发链,A对象的行为将影响B对象,B对象的行为将影响C对象……,可以使用观察者模式创建一种链式触发机制。
注意事项:
① JAVA 中已经有了对观察者模式的支持类。
② 避免循环引用。
③ 如果顺序执行,某一观察者错误会导致系统卡壳,一般采用异步方式。
实现
观察者模式使用三个类 Subject、Observer 和 Client。Subject 对象带有绑定观察者到 Client 对象和从 Client 对象解绑观察者的方法。我们创建 Subject 类、Observer 抽象类和扩展了抽象类 Observer 的实体类。
ObserverPatternDemo,我们的演示类使用 Subject 和实体类对象来演示观察者模式。
步骤 1
创建 Subject 类。
import java.util.ArrayList;
import java.util.List;
public class Subject {
private List observers = new ArrayList();
private int state;
public int getState() {
return state;
}
public void setState(int state) {
this.state = state; notifyAllObservers();
}
public void attach(Observer observer){
observers.add(observer);
}
public void notifyAllObservers(){
for (Observer observer : observers) { observer.update();
} } }
步骤 2
创建 Observer 类。
public abstract class Observer {
protected Subject subject;
public abstract void update();
}
步骤 3
创建实体观察者类。
public class BinaryObserver extends Observer{
public BinaryObserver(Subject subject){
this.subject = subject; this.subject.attach(this);
}
@Override public void update() {
System.out.println( "Binary String: " + Integer.toBinaryString( subject.getState() ) );
}}
public class OctalObserver extends Observer{
public OctalObserver(Subject subject){
this.subject = subject; this.subject.attach(this); } @Override public void update() { System.out.println( "Octal String: " + Integer.toOctalString( subject.getState() ) );
}}
public class HexaObserver extends Observer{ public HexaObserver(Subject subject){ this.subject = subject; this.subject.attach(this); } @Override public void update() { System.out.println( "Hex String: " + Integer.toHexString( subject.getState() ).toUpperCase() ); }}
步骤 4
使用 Subject 和实体观察者对象。
public class ObserverPatternDemo { public static void main(String[] args) { Subject subject = new Subject(); new HexaObserver(subject); new OctalObserver(subject); new BinaryObserver(subject); System.out.println("First state change: 15"); subject.setState(15); System.out.println("Second state change: 10"); subject.setState(10); }}
步骤 5
执行程序,输出结果:
First state change: 15Hex String: FOctal String: 17Binary String: 1111Second state change: 10Hex String: AOctal String: 12Binary String: 1010
二、Java模板模式
在模板模式(Template Pattern)中,一个抽象类公开定义了执行它的方法的方式/模板。它的子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进行。这种类型的设计模式属于行为型模式。
介绍
意图:定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
主要解决:一些方法通用,却在每一个子类都重新写了这一方法。
何时使用:有一些通用的方法。
如何解决:将这些通用算法抽象出来。
关键代码:在抽象类实现,其他步骤在子类实现。
应用实例:
① 在造房子的时候,地基、走线、水管都一样,只有在建筑的后期才有加壁橱加栅栏等差异。
② 西游记里面菩萨定好的 81 难,这就是一个顶层的逻辑骨架。
③ spring 中对 Hibernate 的支持,将一些已经定好的方法封装起来,比如开启事务、获取 Session、关闭 Session 等,程序员不重复写那些已经规范好的代码,直接丢一个实体就可以保存。
优点:
① 封装不变部分,扩展可变部分。
② 提取公共代码,便于维护。
③ 行为由父类控制,子类实现。
缺点:每一个不同的实现都需要一个子类来实现,导致类的个数增加,使得系统更加庞大。
使用场景:
① 有多个子类共有的方法,且逻辑相同。
② 重要的、复杂的方法,可以考虑作为模板方法。
注意事项:为防止恶意操作,一般模板方法都加上 final 关键词。
实现
我们将创建一个定义操作的 Game 抽象类,其中,模板方法设置为 final,这样它就不会被重写。Cricket 和 Football 是扩展了 Game 的实体类,它们重写了抽象类的方法。
TemplatePatternDemo,我们的演示类使用 Game 来演示模板模式的用法。
步骤 1
创建一个抽象类,它的模板方法被设置为 final。
public abstract class Game { abstract void initialize(); abstract void startPlay(); abstract void endPlay(); //模板 public final void play(){ //初始化游戏 initialize(); //开始游戏 startPlay(); //结束游戏 endPlay(); }}
步骤 2
创建扩展了上述类的实体类。
public class Cricket extends Game { @Override void endPlay() { System.out.println("Cricket Game Finished!"); } @Override void initialize() { System.out.println("Cricket Game Initialized! Start playing."); } @Override void startPlay() { System.out.println("Cricket Game Started. Enjoy the game!"); }}
public class Football extends Game { @Override void endPlay() { System.out.println("Football Game Finished!"); } @Override void initialize() { System.out.println("Football Game Initialized! Start playing."); } @Override void startPlay() { System.out.println("Football Game Started. Enjoy the game!"); }}
步骤 3
使用 Game 的模板方法 play() 来演示游戏的定义方式。
public class TemplatePatternDemo { public static void main(String[] args) { Game game = new Cricket(); game.play(); System.out.println(); game = new Football(); game.play(); }}
步骤 4
执行程序,输出结果:
Cricket Game Initialized! Start playing.Cricket Game Started. Enjoy the game!Cricket Game Finished! Football Game Initialized! Start playing.Football Game Started. Enjoy the game!Football Game Finished!