这里我们要了解的行为型模式分别是策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式等11中模式。通过类之间的关系我们可以分为下面四类。
一、策略模式(strategy)
策略模式是指“对算法的包装,把使用算法的责任和算法本身分离开来,委派给不同的对象管理。策略算法通常是把一系列的算法包装到一系列的策略类里面,作为一个抽象策略类的子类”。策略模式的意图是将可互换的方法封装在各自独立的类中,并且让每个方法都实现一个公共的操作。
这里我们用订单支付选择支付方式的场景进行说明。
首先我们需要一个支付的策略接口
public interface Payment {
public PayState pay(String orderId, BigDecimal amount);
}
对于支付我们有多种支付方式
public class AliPay implements Payment {
@Override
public PayState pay(String orderId, BigDecimal amount) {
System.out.println("欢迎使用支付宝");
System.out.println("查询账户余额,开始扣款");
return new PayState(200,amount,"支付成功");
}
}
public class WeChatPay implements Payment {
@Override
public PayState pay(String orderId, BigDecimal amount) {
System.out.println("欢迎使用微信支付");
System.out.println("查询账户余额,开始扣款");
return new PayState(200,amount,"支付成功");
}
}
将多种支付方式封装成一个枚举类供用户选择
public enum PayType {
ALI_PAY("1001", new AliPay()),
JD_PAY("1002", new JDPay()),
WECHAT_PAY("1003", new AliPay());
PayType(String code, Payment payment) {
this.code = code;
this.payment = payment;
}
private String code;
private Payment payment;
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public Payment getPayment() {
return payment;
}
public void setPayment(Payment payment) {
this.payment = payment;
}
}
测试之前我们还需要有一个订单类
public class Order {
private String uid;
private String orderId;
private BigDecimal amount;
public Order(String uid, String orderId, BigDecimal amount) {
this.uid = uid;
this.orderId = orderId;
this.amount = amount;
}
/**
* 支付
* @param payType
* @return
*/
public PayState pay(PayType payType) {
return payType.getPayment().pay(this.orderId, this.amount);
}
}
这样用户就可以自由选择支付方式了
public class PayStrategyTest {
public static void main(String[] args) {
Order order = new Order("1001", "2018052611230", new BigDecimal(300));
System.out.println(order.pay(PayType.ALI_PAY));
}
}
策略模式的最终使用哪种方式决定权在用户,系统本身提供不同算法的实现,对各种算法做封装。
使用场景:
- 如果在一个系统里面有许多类,它们之间的区别仅在于它们的行为,那么使用策略模式可以动态地让一个对象在许多行为中选择一种行为。
- 一个系统需要动态地在几种算法中选择一种,如支付方式、旅行方式。
- 如果一个对象有很多的行为,如果不用恰当的模式,这些行为就只好使用多重的条件选择语句来实现。
- 根据客户的需求处理数据的时候选择一种算法,固定的一些算法(不再发生变化的算法,可以扩展),例如购物场景,在支付的时候用户可以选择使用哪种方式支付,但是怎样去支付的用户不知道也不关心。
从上面的学习我们可以发现策略模式和桥接模式似乎一样,这里我们来分析一下它们的不同
模式名称 | 桥接模式 | 策略模式 |
---|---|---|
目的 | 让底层实现和上层接口可以分别演化,从而提高移植性 | 最终执行结果是固定的,执行过程和执行逻辑不同,供客户选择 |
强调 | 为了利用已有的方法或类,强调接口对象仅提供基本操作 | 为了扩展和修改,并提供动态配置,强调接口对象提供的是一种算法 |
二、模板方法模式(Template Method)
模板方法模式是指“定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤”。
抽象模板(Abstract Template)角色:定义了一个或多个抽象操作以便让子类实现。这些抽象操作叫做基本操作,它们是一个顶级逻辑的组成步骤。 定义并实现了一个模板方法。这个模板方法一般是一个具体方法,它给出了一个顶级逻辑的骨架,而逻辑的组成步骤在相应的抽象操作中,推迟到子类实现。顶级逻辑也有可能调用一些具体方法。
具体模板(Concrete Template)角色:实现父类所定义的一个或多个抽象方法,它们是一个顶级逻辑的组成步骤。每一个抽象模板角色都可以有任意多个具体模板角色与之对应,而每一个具体模板角色都可以给出这些抽象方法(也就是顶级逻辑的组成步骤)的不同实现,从而使得顶级逻辑的实现各不相同。
public abstract class AbstractTemplate {
/**
* 模板方法
*/
public final void templateMethod(){
//调用基本方法
abstractMethod();
hookMethod();
concreteMethod();
}
/**
* 基本方法的声明(由子类实现)
*/
protected abstract void abstractMethod();
/**
* 基本方法(空方法)
*/
protected void hookMethod(){}
/**
* 基本方法(已经实现)
*/
private final void concreteMethod(){
//业务相关的代码
}
}
注:为防止恶意操作,一般模板方法都加上 final 关键词。
public class ConcreteTemplate extends AbstractTemplate{
//基本方法的实现
@Override
public void abstractMethod() {
//业务相关的代码
}
//重写父类的方法
@Override
public void hookMethod() {
//业务相关的代码
}
}
模板方法模式与策略模式的异同
模式名 | 模板方法模式 | 策略模式 |
---|---|---|
是否算法与上下文解耦 | 是 | 是 |
如何重用代码 | 使用继承来重用代码 | 运用委托来实现代码重用,并且委托比继承更具有灵活性 |
重点 | 封装算法骨架 | 分离并封装算法实现 |
三、观察者模式(Observer)
观察者模式是指“定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将收到通知并自动更新。”如果被观察的对象发生一点变更,则会通知观察者去具体执行相关的操作。
public interface Observer {
public void change();
}
public class ConcreteObserver implements Observer {
@Override
public void change() {
System.out.println("ConcreteObserver has received!");
}
}
public interface Subject {
/*增加观察者*/
public void add(Observer observer);
/*删除观察者*/
public void del(Observer observer);
/*通知所有的观察者*/
public void notifyObservers();
}
public abstract class AbstractSubject implements Subject {
private List<Observer> list = new Collections.synchronizedList(new ArrayList<Observer>());
@Override
public void add(Observer observer) {
list.add(observer);
}
@Override
public void del(Observer observer) {
list.remove(observer);
}
@Override
public void notifyObservers() {
for (Observer observer: list)
observer.change();
}
}
}
public class ConcreteSubject extends AbstractSubject {
@Override
public void operation() {
System.out.println("update self!");
notifyObservers();
}
}
测试类
public class ObserverTest {
public static void main(String[] args) {
Subject sub = new ConcreteSubject();
sub.add(new ConcreteObserver());
sub.operation();
}
}
使用场景: 1、有多个子类共有的方法,且逻辑相同。 2、重要的、复杂的方法,可以考虑作为模板方法。
四、迭代器模式(Iterator)
迭代器模式是指“提供一种方法顺序访问一个聚合对象中的各个元素,而又不需要暴露该对象的内部结构”。
public interface Iterator {
public boolean hasNext();
public Object next();
}
public interface Container {
public Iterator iterator();
}
public class ConcreteContainer implements Container {
public String names[] = {"Robert" , "John" ,"Julie" , "Lora"};
@Override
public Iterator iterator() {
return new ConcreteIterator();
}
private class ConcreteIterator implements Iterator {
int index;
@Override
public boolean hasNext() {
if(index < names.length){
return true;
}
return false;
}
@Override
public Object next() {
if(this.hasNext()){
return names[index++];
}
return null;
}
}
}
测试类
public class IteratorPatternDemo {
public static void main(String[] args) {
Container container = new ConcreteContainer();
for(Iterator iter = container.iterator(); iter.hasNext();){
String name = (String)iter.next();
System.out.println("Name : " + name);
}
}
}
使用场景: 1、访问一个聚合对象的内容而无须暴露它的内部表示。 2、需要为聚合对象提供多种遍历方式。 3、为遍历不同的聚合结构提供一个统一的接口。
五、责任链模式(Chain of Responsibility)
责任链模式是指“使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,沿着这条链传递该请求,直到有一个对象处理它为止。”
我们创建抽象类 AbstractLogger,带有详细的日志记录级别。然后我们创建三种类型的记录器,都扩展了 AbstractLogger。每个记录器消息的级别是否属于自己的级别,如果是则相应地打印出来,否则将不打印并把消息传给下一个记录器。
创建抽象的记录器类。
public abstract class AbstractLogger {
public static int INFO = 1;
public static int DEBUG = 2;
public static int ERROR = 3;
protected int level;
//责任链中的下一个元素
protected AbstractLogger nextLogger;
public void setNextLogger(AbstractLogger nextLogger){
this.nextLogger = nextLogger;
}
public void logMessage(int level, String message){
if(this.level <= level){
write(message);
}
if(nextLogger !=null){
nextLogger.logMessage(level, message);
}
}
abstract protected void write(String message);
}
创建扩展了该记录器类的实体类
public class ConsoleLogger extends AbstractLogger {
public ConsoleLogger(int level){
this.level = level;
}
@Override
protected void write(String message) {
System.out.println("Standard Console::Logger: " + message);
}
}
public class ErrorLogger extends AbstractLogger {
public ErrorLogger(int level){
this.level = level;
}
@Override
protected void write(String message) {
System.out.println("Error Console::Logger: " + message);
}
}
public class FileLogger extends AbstractLogger {
public FileLogger(int level){
this.level = level;
}
@Override
protected void write(String message) {
System.out.println("File::Logger: " + message);
}
}
创建不同类型的记录器。赋予它们不同的错误级别,并在每个记录器中设置下一个记录器。每个记录器中的下一个记录器代表的是链的一部分。
public class ChainPatternDemo {
private static AbstractLogger getChainOfLoggers() {
AbstractLogger errorLogger = new ErrorLogger(AbstractLogger.ERROR);
AbstractLogger fileLogger = new FileLogger(AbstractLogger.DEBUG);
AbstractLogger consoleLogger = new ConsoleLogger(AbstractLogger.INFO);
errorLogger.setNextLogger(fileLogger);
fileLogger.setNextLogger(consoleLogger);
return errorLogger;
}
public static void main(String[] args) {
AbstractLogger loggerChain = getChainOfLoggers();
loggerChain.logMessage(AbstractLogger.INFO,
"This is an information.");
loggerChain.logMessage(AbstractLogger.DEBUG,
"This is an debug level information.");
loggerChain.logMessage(AbstractLogger.ERROR,
"This is an error information.");
}
}
使用场景: 1、有多个对象可以处理同一个请求,具体哪个对象处理该请求由运行时刻自动确定。 2、在不明确指定接收者的情况下,向多个对象中的一个提交一个请求。 3、可动态指定一组对象处理请求。
六、命令模式(Command)
命令模式是指“将一个请求封装成一个对象,从而使你用不同的请求对客户进行参数化,对请求排队或记录请求日志,以及支持可撤销的操作。”也就是说一个命令即一个操作,请求方与操作方各自独立操作,请求方无需了解接收方的接口以及执行细节。
客户端(Client)角色:创建一个具体命令(ConcreteCommand)对象并确定其接收者。
命令(Command)角色:声明了一个给所有具体命令类的抽象接口。
具体命令(ConcreteCommand)角色:定义一个接收者和行为之间的弱耦合;实现execute()方法,负责调用接收者的相应操作。execute()方法通常叫做执行方法。
调用者(Invoker)角色:要求该命令执行这个请求。
接收者(Receiver)角色:负责具体实施和执行一个请求。任何一个类都可以成为接收者,实施和执行请求的方法叫做行动方法。
我们这里用家电自动化遥控器做例子,它可以控制所有的家电,有多个开关。
这里的Control就相当于调用者,而Light等命令类就相当于接收者。
具体执行者(Receiver)
public class Light {//灯
String loc = "";
public Light(String loc) {
this.loc = loc;
}
public void On() {
System.out.println(loc + " On");//开
}
public void Off() {
System.out.println(loc + " Off");//关
}
}
public class Stereo {//播放器
static int volume = 0;
public void On() {
System.out.println("Stereo On");
}
public void Off() {
System.out.println("Stereo Off");
}
public void SetCd() {//设置cd模式
System.out.println("Stereo SetCd");
}
public void SetVol(int vol) {//设置音量
volume = vol;
System.out.println("Stereo volume=" + volume);
}
public int GetVol() {
return volume;
}
public void Start() {//开始
System.out.println("Stereo Start");
}
}
命令接口
public interface Command {
public void execute();//执行命令
public void undo();//回退
}
遥控器类
public class CommandModeControl implements Control{
private Command[] onCommands;
private Command[] offCommands;
private Stack<Command> stack = new Stack<Command>();//使用栈存储命令,用于回退
public CommandModeControl() {
onCommands=new Command[5];
offCommands=new Command[5];
Command noCommand = new NoCommand();//可以定义一个NoCommand类,这样无论有没有设置家电都可以直接使用。
for(int i=0,len=onCommands.length;i<len;i++) {
onCommands[i]=noCommand;
offCommands[i]=noCommand;
}
}
public void setCommand(int slot,Command onCommand,Command offCommand) {
onCommands[slot]=onCommand;
offCommands[slot]=offCommand;
}
@Override
public void onButton(int slot) {
onCommands[slot].execute();
stack.push(onCommands[slot]);
}
@Override
public void offButton(int slot) {
offCommands[slot].execute();
stack.push(offCommands[slot]);
}
@Override
public void undoButton() {
stack.pop().undo();
}
}
public class LightOnCommand implements Command {//每个命令一个类
private Light light;
public LightOnCommand(Light light) {
this.light=light;
}
@Override
public void execute() {
light.On();
}
@Override
public void undo() {
light.Off();
}
}
public class LightOffCommand implements Command {
private Light light;
public LightOffCommand(Light light) {
this.light=light;
}
@Override
public void execute() {
light.Off();
}
@Override
public void undo() {
light.On();
}
}
public class StereoOnCommand implements Command {
private Stereo setreo;
public StereoOnCommand(Stereo setreo) {
this.setreo=setreo;
}
@Override
public void execute() {
setreo.On();
setreo.SetCd();
setreo.start();
}
@Override
public void undo() {
setreo.Off();
}
}
public class StereoOffCommand implements Command {
private Stereo setreo;
public StereoOffCommand(Stereo setreo) {
this.setreo=setreo;
}
@Override
public void execute() {
setreo.Off();
}
@Override
public void undo() {
setreo.On();
setreo.SetCd();
setreo.start();
}
}
降低音量命令
public class StereoSubVolCommand implements Command {
private Stereo setreo;
public StereoSubVolCommand(Stereo setreo) {
this.setreo=setreo;
}
@Override
public void execute() {
int vol = setreo.GetVol();
if(vol>0) {
setreo.SetVol(--vol);
}
}
@Override
public void undo() {
int vol = setreo.GetVol();
if(vol<11) {
setreo.SetVol(++vol);
}
}
}
升高音量命令
public class StereoAddVolCommand implements Command {
private Stereo setreo;
public StereoAddVolCommand(Stereo setreo) {
this.setreo=setreo;
}
@Override
public void execute() {
int vol = setreo.GetVol();
if(vol<11) {
setreo.SetVol(++vol);
}
}
@Override
public void undo() {
int vol = setreo.GetVol();
if(vol>0) {
setreo.SetVol(--vol);
}
}
}
public class NoCommand implements Command {
@Override
public void execute() {}
@Override
public void undo() {}
}
如果需要处理一起处理多个命令,可以定义一个宏命令类
public class MarcoCommand implements Command {
private Command[] commands;
public MarcoCommand(Command[] commands) {
this.commands = commands;
}
@Override
public void execute() {
for (int i = 0, len = commands.length; i < len; i++) {
commands[i].execute();
}
}
@Override
public void undo() {
for (int i = commands.length - 1; i >= 0; i--) {
commands[i].undo();
}
}
}
测试类
public class ControlTest {
public static void main(String[] args) {
CommandModeControl control = new CommandModeControl();
MarcoCommand onmarco,offmarco;
Light bedroomlight = new Light("BedRoom");
Light kitchlight = new Light("Kitch");
Stereo stereo = new Stereo();
LightOnCommand bedroomlighton = new LightOnCommand(bedroomlight);
LightOffCommand bedroomlightoff = new LightOffCommand(bedroomlight);
LightOnCommand kitchlighton = new LightOnCommand(kitchlight);
LightOffCommand kitchlightoff = new LightOffCommand(kitchlight);
Command[] oncommands={bedroomlighton,kitchlighton};
Command[] offcommands={bedroomlightoff,kitchlightoff};
onmarco=new MarcoCommand(oncommands);
offmarco=new MarcoCommand(offcommands);
StereoOnCommand stereoOn = new StereoOnCommand(stereo);
StereoOffCommand stereoOff = new StereoOffCommand(stereo);
StereoAddVolCommand stereoaddvol = new StereoAddVolCommand(stereo);
StereoSubVolCommand stereosubvol = new StereoSubVolCommand(stereo);
control.setCommand(0, bedroomlighton, bedroomlightoff);
control.setCommand(1, kitchlighton, kitchlightoff);
control.setCommand(2, stereoOn, stereoOff);
control.setCommand(3, stereoaddvol, stereosubvol);
control.setCommand(4, onmarco, offmarco);
control.onButton(0);
control.undoButton();
//control.offButton(0);
control.onButton(1);
control.offButton(1);
control.onButton(2);
control.onButton(3);
control.offButton(3);
control.undoButton();
control.offButton(2);
control.undoButton();
control.onButton(4);
control.offButton(4);
}
}
使用场景:认为是命令的地方都可以使用命令模式,比如: 1、GUI 中每一个按钮都是一条命令。 2、模拟 CMD。