助力软件开发企业降本增效 PHP / java源码系统,只需一次付费,代码终身使用! 广告
[TOC] 行为型模式是对在不同的对象之间划分责任和算法的抽象化,重点关注类(或者对象)之间的相互作用。 行为型模式分为类行为型模式和对象行为型模式两种。包含 责任链模式、命令模式、解释器模式、迭代器模式、中介者模式、备忘录模式、观察者模式、状态模式、策略模式、模版方法模式及访问者模式。 # 1 观察者模式 观察者模式又称发布-订阅模式、模型-视图模式、源-监听器模式、从属者模式 ## 1.1 模式定义 定义了对象间的一种一对多的依赖关系。 一个对象(观察目标)发生改变时,将自动通知其他对象(观察者),其他对象做出相应的反应。 ## 1.2 模式结构 包含如下角色: * Subject:抽象目标(又称主题) 把所有观察者对象的引用保存在一个集合中,并且提供接口可以添加、删除观察者 * ConcreteSubject:具体目标 抽象目标类的子类,通常包含有经常发生改变的数据或状态 * Observer:抽象观察者 定义一个更新接口,在得到目标通知时更新自己 * ConcreteObserver:具体观察者 存储与目标状态一致的状态,如果需要还可以持有一个指向具体目标对象的引用 ## 1.3 代码示例 抽象目标: ```java public abstract class Nexus6P { // 所有观察者对象保存在集合中 private List<Customer> mCustomerList = new ArrayList<>(); // 添加观察者 public void attach(Customer customer) { if (customer == null) { throw new NullPointerException(); } if (!mCustomerList.contains(customer)) { mCustomerList.add(customer); } } // 删除观察者 public void detach(Customer customer) { mCustomerList.remove(customer); } // 通知所有注册的观察者 public void notifyCustomers(float nowPrice) { for (Customer customer : mCustomerList) { customer.update(nowPrice); } } } ``` 具体目标: ```java public class ConcreteNexus6P extends Nexus6P { // 包含经常发生改变的数据或状态 private float mNowPrice; public float getNowPrice() { return mNowPrice; } // 改变数据或状态 public void changePrice(float nowPrice) { mNowPrice = nowPrice; // 状态改变,通知各个观察者 this.notifyCustomers(mNowPrice); } } ``` 抽象观察者: ```java public interface Customer { //状态更新接口 void update(float nowPrice); } ``` 具体观察者: ```java public class ConcreteCustomer implements Customer { private static final String TAG = "ConcreteCustomer"; // 存储与目标一致的状态 private float mNowPrice; @Override public void update(float nowPrice) { // 更新观察者状态,使其与观察目标状态保持一致 mNowPrice = nowPrice; if (mNowPrice < 3000) { buyIt(); } else { Log.i(TAG, "Too expensive!"); } } private void buyIt() { // buy it } } ``` 客户端类: ```java public class JingDongStore { public static void main(String[] args) { ConcreteNexus6P nexus6P = new ConcreteNexus6P(); Customer Mark = new ConcreteCustomer(); nexus6P.attach(Mark); // nexus6P.attach(Tom); // ... nexus6P.changePrice(3200); nexus6P.changePrice(2800); } } ``` 观察者模式又分为推模型和拉模型两种,以上例子属于推模型。 * 推模型:由观察目标通过更新方法向观察者推送全部或部分数据 * 拉模型:由观察者主动从观察目标中拉数据,一般在更新方法中观察目标会把自身传递过去,观察者持有观察目标引用 拉模型的例子如下: ```java // 抽象目标 public abstract class Nexus6P { private List<Customer> mCustomerList = new ArrayList<>(); public void attach(Customer customer) { if (customer == null) { throw new NullPointerException(); } if (!mCustomerList.contains(customer)) { mCustomerList.add(customer); } } public void detach(Customer customer) { mCustomerList.remove(customer); } public void notifyCustomers() { for (Customer customer : mCustomerList) { customer.update(this); } } } // 具体目标 public class ConcreteNexus6P extends Nexus6P { private float mNowPrice; public float getNowPrice() { return mNowPrice; } public void changePrice(float nowPrice) { mNowPrice = nowPrice; this.notifyCustomers(); } } // 抽象观察者 public interface Customer { void update(Nexus6P nexus6P); } // 具体观察者 public class ConcreteCustomer implements Customer { private static final String TAG = "ConcreteCustomer"; // 可以持有具体观察目标的引用 private ConcreteNexus6P mConcreteNexus6P; private float mNowPrice; @Override public void update(Nexus6P nexus6P) { // 更新观察者状态,使其与观察目标状态保持一致 mConcreteNexus6P = (ConcreteNexus6P) nexus6P; mNowPrice = mConcreteNexus6P.getNowPrice(); if (mNowPrice < 3000) { buyIt(); } else { Log.i(TAG, "Too expensive!"); } } private void buyIt() { // buy it } } ``` ## 1.4 总结 * 优点在于可以实现表示层和数据逻辑层的分离,在观察目标和观察者之间建立一个抽象的耦合 * 缺点在于一个观察目标有很多直接或间接的观察者时,将所有的观察者全部通知到会花费很多时间;且观察者和观察目标之间有循环依赖时,可能会导致崩溃 # 2 命令模式 又称动作(Action)模式、事物(Transaction)模式 ## 2.1 模式定义 命令模式把一个请求或者操作封装到一个对象中。 ## 2.2 模式结构 * 接收者 负责具体实施和执行请求。 * 抽象命令类 声明了给所有具体命令类的接口方法(execute 方法等),通常叫做执行方法。 * 具体命令类 实现 execute 方法,负责调用接收者的相应操作。 * 请求者 负责调用命令对象(的 execute 方法)执行请求,相关的方法称为行动方法。 * 客户端 负责创建具体命令对象,并确定其接收者。 ## 2.3 代码示例 接收者: ```java public class Nexus6P { // 真正执行命令的相应操作 public void playMusic() { // play music } public void playVideo() { // play video } public void playGames() { // play games } } ``` 抽象命令类: ```java public interface Command { // 执行方法 void execute(); } ``` 具体命令类: ```java public class PlayMusicCommand implements Command { // 接收者 private Nexus6P mNexus6P; public PlayMusicCommand(Nexus6P nexus6P) { mNexus6P = nexus6P; } // 执行方法 @Override public void execute() { mNexus6P.playMusic(); } } public class PlayVideoCommand implements Command { // 接收者 private Nexus6P mNexus6P; public PlayVideoCommand(Nexus6P nexus6P) { mNexus6P = nexus6P; } // 执行方法 @Override public void execute() { mNexus6P.playVideo(); } } public class PlayGamesCommand implements Command { // 接收者 private Nexus6P mNexus6P; public PlayGamesCommand(Nexus6P nexus6P) { mNexus6P = nexus6P; } // 执行方法 @Override public void execute() { mNexus6P.playGames(); } } ``` 请求者: ```java public class PhoneScreen { // 具体命令对象 private Command mPlayMusicCommand; private Command mPlayVideoCommand; private Command mPlayGamesCommand; public void setPlayMusicCommand(Command playMusicCommand) { mPlayMusicCommand = playMusicCommand; } public void setPlayVideoCommand(Command playVideoCommand) { mPlayVideoCommand = playVideoCommand; } public void setPlayGamesCommand(Command playGamesCommand) { mPlayGamesCommand = playGamesCommand; } // 行动方法 public void playMusic() { mPlayMusicCommand.execute(); } // 行动方法 public void playVideo() { mPlayVideoCommand.execute(); } // 行动方法 public void playGames() { mPlayGamesCommand.execute(); } } ``` 客户端: ```java public class CustomerMark { public static void main(String[] args) { // 创建接收者 Nexus6P nexus6P = new Nexus6P(); // 创建具体命令对象 Command playMusicCommand = new PlayMusicCommand(nexus6P); Command playVideoCommand = new PlayVideoCommand(nexus6P); Command playGamesCommand = new PlayGamesCommand(nexus6P); // 创建请求者 PhoneScreen phoneScreen = new PhoneScreen(); phoneScreen.setPlayMusicCommand(playMusicCommand); phoneScreen.setPlayVideoCommand(playVideoCommand); phoneScreen.setPlayGamesCommand(playGamesCommand); // 测试 phoneScreen.playMusic(); phoneScreen.playVideo(); phoneScreen.playGames(); } } ``` ## 2.4 总结 * 命令模式使请求本身成为一个对象,这个对象和其他对象一样可以被存储和传递。 * 优点在于降低系统的耦合度,增加新的命令很方便,而且可以比较容易地设计一个命令队列和宏命令,并方便地实现对请求的撤销和恢复 * 缺点在于可能会导致某些系统有过多的具体命令类 * 命令模式适用情况: * 需要将请求调用者和请求接收者解耦,使得调用者和接收者不直接交互; * 需要在不同的时间指定请求、将请求排队和执行请求; * 需要支持命令的撤销操作和恢复操作,需要将一组操作组合在一起,即支持宏命令。 # 3 策略模式 ## 3.1 模式定义 策略模式是对算法的包装,针对一组算法,将每一个算法封装到具有共同接口的独立的类中,从而使得它们可以相互替换。客户端类可自由选择使用哪一种策略(算法)。 ## 3.2 模式结构 * 抽象策略角色(Strategy) 给出所有具体策略类所需要实现的接口 * 具体策略角色(ConcreteStrategy) 包装了相关的算法或行为 * 环境角色(Context) 环境类在解决某个问题时可以采用多种策略,在环境类中维护一个对抽象策略类的引用实例 ## 3.3 代码示例 抽象策略: ```java public interface ConsumerStrategy { double calculatePrice(double originalPrice); } ``` 具体策略类: ```java public class GoldMedalConsumerStrategy implements ConsumerStrategy { @Override public double calculatePrice(double originalPrice) { return originalPrice * 0.8; } } public class SilverMedalConsumerStrategy implements ConsumerStrategy { @Override public double calculatePrice(double originalPrice) { return originalPrice * 0.9; } } public class BronzeMedalConsumerStartegy implements ConsumerStrategy { @Override public double calculatePrice(double originalPrice) { return originalPrice * 0.95; } } ``` 环境类: ```java public class Price { private ConsumerStrategy mStrategy; public Price(ConsumerStrategy strategy) { mStrategy = strategy; } public double calculate(double phonePrice) { return mStrategy.calculatePrice(phonePrice); } } ``` 客户端类: ```java public class JingDongstore { public static void main(String[] args) { ConsumerStrategy silverMedalConsumerStrategy = new SilverMedalConsumerStrategy(); Price price = new Price(silverMedalConsumerStrategy); double dealPrice = price.calculate(5000); System.out.print("手机成交价格为" + dealPrice); } } ``` ## 3.4 总结 * 策略模式的重心不是如何实现算法,而是如何组织、调用这些算法,从而让程序结构更灵活,具有更好的维护性和扩展性。 * 对于一系列具体的策略算法,大家的地位是完全一样的,正因为这个平等性,才能实现算法之间可以相互替换。所有的策略算法在实现上也是相互独立的,相互之间是没有依赖的。 * 策略模式在每一个时刻只能使用一个具体的策略实现对象,虽然可以动态地在不同的策略实现中切换,但是同时只能使用一个。 * 在策略模式中,应当由客户端自己决定在什么情况下使用什么具体策略角色。 * 策略模式适用情况包括: * 在一个系统里面有许多类,它们之间的区别仅在于它们的行为,使用策略模式可以动态地让一个对象在许多行为中选择一种行为; * 一个系统需要动态地在几种算法中选择一种; * 避免使用难以维护的多重条件选择语句; * 希望在具体策略类中封装算法和与相关的数据结构。 # 4 状态模式 又称状态对象模式 ## 4.1 模式定义 状态模式允许一个对象在其内部状态改变的时候改变其行为,这个对象看上去就像改变了它的类一样 状态模式把所研究的对象(环境角色)的行为包装在不同的状态对象里,每一个状态对象都属于一个抽象状态类的一个子类。 ## 4.2 模式结构 * 环境角色 定义客户端感兴趣的接口,并持有一个具体状态类的实例 * 抽象状态类 定义一个接口,用以封装环境(Context)对象的一个特定的状态所对应的行为 * 具体状态类 每一个具体状态类都实现了环境(Context)的一个状态所对应的行为 ## 4.3 代码示例 抽象状态类: ```java public interface State { // 特定状态所对应的行为 void handle(String sampleParameter); } ``` 具体状态类: ```java public class ConcreteStateA implements State { private static final String TAG = "ConcreteStateA"; @Override public void handle(String sampleParameter) { Log.e(TAG, "handle: " + sampleParameter); } } public class ConcreteStateB implements State { private static final String TAG = "ConcreteStateB"; @Override public void handle(String sampleParameter) { Log.e(TAG, "handle: " + sampleParameter); } } ``` 环境角色类: ```java public class Context { // 持有一个具体状态类的实例 private State mState; public void setState(State state) { mState = state; } // 定义客户端感兴趣的接口 public void request(String sampleParameter) { mState.handle(sampleParameter); } } ``` 客户端: ```java public class Client { public static void main(String[] args) { Context context = new Context(); State state = new ConcreteStateA(); context.setState(state); context.request("test"); } } ``` ## 4.4 总结 * 在具体的状态处理类中经常需要获取环境 (Context) 自身的数据,甚至在必要的时候会回调环境 (Context) 的方法,因此,通常将环境 (Context) 自身当作一个参数传递给具体的状态处理类。 * 客户端一般只和环境 (Context) 交互。客户端可以用状态对象来配置一个环境(Context),一旦配置完毕,就不再需要和状态对象打交道了。 * 策略模式与状态模式的区别 * 可以通过环境类状态的个数来决定是使用策略模式还是状态模式。 * 策略模式的环境类自己选择一个具体策略类,具体策略类无须关心环境类;而状态模式的环境类由于外在因素需要放进一个具体状态中,以便通过其方法实现状态的切换,因此环境类和状态类之间存在一种双向的关联关系。 * 使用策略模式时,客户端需要知道所选的具体策略是哪一个,而使用状态模式时,客户端无须关心具体状态,环境类的状态会根据用户的操作自动转换。 * 如果系统中某个类的对象存在多种状态,不同状态下行为有差异,而且这些状态之间可以发生转换时使用状态模式;如果系统中某个类的某一行为存在多种实现方式,而且这些实现方式可以互换时使用策略模式。