> 文档中心 > 面向对象的行为型

面向对象的行为型


1. 责任链(Chain Of Responsibility)

Intent

使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。 将这些对象连成一条链,并沿着这条链发送该请求,直到有一个对象处理它为止。

Class Diagram

  • Handler:定义处理请求的接口,并且实现后继链(successor)

Implementation

public abstract class Handler {    protected Handler successor;    public Handler(Handler successor) { this.successor = successor;    }    protected abstract void handleRequest(Request request);}
public class ConcreteHandler1 extends Handler {    public ConcreteHandler1(Handler successor) { super(successor);    }    @Override    protected void handleRequest(Request request) { if (request.getType() == RequestType.TYPE1) {     System.out.println(request.getName() + " is handle by ConcreteHandler1");     return; } if (successor != null) {     successor.handleRequest(request); }    }}
public class ConcreteHandler2 extends Handler {    public ConcreteHandler2(Handler successor) { super(successor);    }    @Override    protected void handleRequest(Request request) { if (request.getType() == RequestType.TYPE2) {     System.out.println(request.getName() + " is handle by ConcreteHandler2");     return; } if (successor != null) {     successor.handleRequest(request); }    }}
public class Request {    private RequestType type;    private String name;    public Request(RequestType type, String name) { this.type = type; this.name = name;    }    public RequestType getType() { return type;    }    public String getName() { return name;    }}
public enum RequestType {    TYPE1, TYPE2}
public class Client {    public static void main(String[] args) { Handler h1=new ConcreteHandler1(null); //h1是没有后继的 Handler h2=new ConcreteHandler2(h1);//h2的后继是h1 Request r1=new Request(RequestType.TYPE1,"request-1"); Request r2=new Request(RequestType.TYPE2,"request-2"); //使多个对象都有机会处理请求,从而 //TODO:避免请求的发送者和接受者之间的耦合关系。 //将这些对象连成一条链,并沿着这条链发送请求,一直到有一个对象处理它为止。 //h2-->h1,这里使用h2处理r1,最终交给h1处理 h2.handleRequest(r1); //h2-->h1,这里h2直接处理 h2.handleRequest(r2);    }}
request-1 is handle by ConcreteHandler1request-2 is handle by ConcreteHandler2

职责链模式会定义一个所有处理请求的对象都要继承实现的抽象类Handler,这样就有利于随时切换新的实现; 其次每个处理请求对象只实现业务流程中的一步业务处理,这样使其变得简单; 最后职责链模式会动态的来组合这些处理请求的对象, 把它们按照流程动态组合起来,并要求它们依次调用,这样就动态的实现了流程。

2.命令(Command)

Intent

将命令封装成对象中,具有以下作用:

  • 使用命令来参数化其它对象
  • 将命令放入队列中进行排队
  • 将命令的操作记录到日志中
  • 支持可撤销的操作

Class Diagram

  • Command:命令
  • Receiver:命令接收者,也就是命令真正的执行者
  • Invoker:通过它来调用命令
  • Client:可以设置命令与命令的接收者

Implementation1

/** * 接收者,其实是命令的真正执行者 */public class Receiver {    /**     * 真正执行命令操作     */    public void action(){ //真正执行命令操作的功能代码 System.out.println("真正执行命令操作的功能代码");    }}
public interface Command {    //执行命令的对应操作    public abstract void execute();}
public class ConcreteCommand implements Command{    private Receiver receiver;    public ConcreteCommand(Receiver receiver){ this.receiver=receiver;    }    @Override    public void execute() { receiver.action();    }}
/** * 命令的调用者 */public class Invoker {    private Command command;    public Invoker(Command command){ this.command=command;    }    //执行命令    public void executeCommand(){ command.execute();    }}
public class Client {    public static void main(String[] args) { //接收者,其实是命令的真正执行者 Receiver receiver=new Receiver(); Command c=new ConcreteCommand(receiver); //命令的调用者 Invoker invoker=new Invoker(c); invoker.executeCommand();    }}

lmplementation2

设计一个遥控器,可以控制电灯开关。

public interface Command {    void execute();}
public class LightOnCommand implements Command {    Light light;    public LightOnCommand(Light light) { this.light = light;    }    @Override    public void execute() { light.on();    }}
public class LightOffCommand implements Command {    Light light;    public LightOffCommand(Light light) { this.light = light;    }    @Override    public void execute() { light.off();    }}
public class Light {    public void on() { System.out.println("Light is on!");    }    public void off() { System.out.println("Light is off!");    }}
/** * 遥控器,也就是命令的调用者 */public class RemoteContol {    private Command[] onCommands;    private Command[] offCommands;    private final int slotNum = 7;    public RemoteContol(){ onCommands=new LightOnCommand[slotNum]; offCommands=new LightOffCommand[slotNum];    }    public void setOnCommand(Command command, int slot) { onCommands[slot] = command;    }    public void setOffCommand(Command command, int slot) { offCommands[slot] = command;    }    public void onButtonWasPushed(int slot) { onCommands[slot].execute();    }    public void offButtonWasPushed(int slot) { offCommands[slot].execute();    }}
public class Client {    public static void main(String[] args) { Light light = new Light(); Command lightOnCommand = new LightOnCommand(light); Command lightOffCommand = new LightOffCommand(light); RemoteContol remoteContol=new RemoteContol(); remoteContol.setOnCommand(lightOnCommand, 0); remoteContol.setOffCommand(lightOffCommand, 0); //执行命令 remoteContol.onButtonWasPushed(0); remoteContol.offButtonWasPushed(0);    }}​​

3.解释器(lnterpreter)

lntent

定义一个语法,定义一个解释器,该解释器处理该语法句子。

Class Diagram

  • TerminalExpression:终结符表达式,每个终结符都需要一个 TerminalExpression。
  • Context:上下文,包含解释器之外的一些全局信息。

lmplementation

以下是一个规则检验器实现,具有 and 和 or 规则,通过规则可以构建一颗解析树,用来检验一个文本是否满足解析树定义的规则。

例如一颗解析树为 D And (A Or (B C)),文本 "D A" 满足该解析树定义的规则。

这里的 Context 指的是 String。

public abstract class Expression {    public abstract boolean interpret(String str);}
public class TerminalExpression extends Expression {    private String literal = null;    public TerminalExpression(String str) { literal = str;    }    public boolean interpret(String str) { StringTokenizer st = new StringTokenizer(str); while (st.hasMoreTokens()) {     String test = st.nextToken();     if (test.equals(literal)) {  return true;     } } return false;    }}
public class AndExpression extends Expression {    private Expression expression1 = null;    private Expression expression2 = null;    public AndExpression(Expression expression1, Expression expression2) { this.expression1 = expression1; this.expression2 = expression2;    }    public boolean interpret(String str) { return expression1.interpret(str) && expression2.interpret(str);    }}
public class OrExpression extends Expression {    private Expression expression1 = null;    private Expression expression2 = null;    public OrExpression(Expression expression1, Expression expression2) { this.expression1 = expression1; this.expression2 = expression2;    }    public boolean interpret(String str) { return expression1.interpret(str) || expression2.interpret(str);    }}
public class Client {    /**     * 构建解析树     */    public static Expression buildInterpreterTree() { // Literal Expression terminal1 = new TerminalExpression("A"); Expression terminal2 = new TerminalExpression("B"); Expression terminal3 = new TerminalExpression("C"); Expression terminal4 = new TerminalExpression("D"); // B C Expression alternation1 = new OrExpression(terminal2, terminal3); // A Or (B C) Expression alternation2 = new OrExpression(terminal1, alternation1); // D And (A Or (B C)) return new AndExpression(terminal4, alternation2);    }    public static void main(String[] args) { Expression define = buildInterpreterTree(); String context1 = "D A"; String context2 = "A B"; System.out.println(define.interpret(context1)); System.out.println(define.interpret(context2));    }}
truefalse
  • 注意:

1.尽量不要在重要的模块中使用解释器模式

2.解释器在实际系统开发中使用较少

3.可以考虑使用开源的Expression4J、Jep等开源的解析工具包

4. 迭代器(Iterator)

lntent

提供一种顺序访问聚合对象元素的方法,并且不暴露聚合对象的内部表示。

Class Diagram

  • Aggregate 是聚合类,其中 createIterator() 方法可以产生一个 Iterator;
  • Iterator 主要定义了 hasNext() 和 next() 方法。
  • Client 组合了 Aggregate,为了迭代遍历 Aggregate,也需要组合 Iterator。

lmplementation

public interface Aggregate {    Iterator createIterator();}
public class ConcreteAggregate implements Aggregate {    private Integer[] items;    public ConcreteAggregate() { items = new Integer[10]; for (int i = 0; i < items.length; i++) {     items[i] = i; }    }    @Override    public Iterator createIterator() { return new ConcreteIterator(items);    }}
public interface Iterator {    Item next();    boolean hasNext();}
public class ConcreteIterator implements Iterator {    private Item[] items;    private int position = 0;    public ConcreteIterator(Item[] items) { this.items = items;    }    @Override    public Object next() { return items[position++];    }    @Override    public boolean hasNext() { return position < items.length;    }}
public class Client {    public static void main(String[] args) { Aggregate aggregate = new ConcreteAggregate(); Iterator iterator = aggregate.createIterator(); while (iterator.hasNext()) {     System.out.println(iterator.next()); }    }}

5.中介者(Mediator)

lntent

集中相关对象之间复杂的沟通和控制方式。

Class Diagram

  • Mediator:中介者,定义一个接口用于与各同事(Colleague)对象通信。
  • Colleague:同事,相关对象

lmplementation

Alarm(闹钟)、CoffeePot(咖啡壶)、Calendar(日历)、Sprinkler(喷头)是一组相关的对象,在某个对象的事件产生时需要去操作其它对象,形成了下面这种依赖结构:

使用中介者模式可以将复杂的依赖结构变成星形结构:

public abstract class Colleague {    public abstract void onEvent(Mediator mediator);}
public class Alarm extends Colleague {    @Override    public void onEvent(Mediator mediator) { mediator.doEvent("alarm");    }    public void doAlarm() { System.out.println("doAlarm()");    }}
public class CoffeePot extends Colleague {    @Override    public void onEvent(Mediator mediator) { mediator.doEvent("coffeePot");    }    public void doCoffeePot() { System.out.println("doCoffeePot()");    }}
public class Calendar extends Colleague{    @Override    public void onEvent(Mediator mediator) { mediator.doEvent("calendar");    }    public void doCalendar(){ System.out.println("doCalendar()");    }}
public class Sprinkler extends Colleague {    @Override    public void onEvent(Mediator mediator) { mediator.doEvent("sprinkler");    }    public void doSprinkler() { System.out.println("doSprinkler()");    }}
public abstract class Mediator {    public abstract void doEvent(String eventType);}
public class ConcreteMediator extends Mediator{    private Alarm alarm;    private CoffeePot coffeePot;    private Calendar calendar;    private Sprinkler sprinkler;    public ConcreteMediator(Alarm alarm, CoffeePot coffeePot, Calendar calendar, Sprinkler sprinkler) { this.alarm = alarm; this.coffeePot = coffeePot; this.calendar = calendar; this.sprinkler = sprinkler;    }    @Override    public void doEvent(String eventType) { switch (eventType) {     case "alarm":  doAlarmEvent();  break;     case "coffeePot":  doCoffeePotEvent();  break;     case "calendar":  doCalenderEvent();  break;     default:  doSprinklerEvent(); }    }    public void doAlarmEvent() { alarm.doAlarm(); coffeePot.doCoffeePot(); calendar.doCalendar(); sprinkler.doSprinkler();    }    public void doCoffeePotEvent() { // ...    }    public void doCalenderEvent() { // ...    }    public void doSprinklerEvent() { // ...    }}
public class Client {    public static void main(String[] args) { Alarm alarm = new Alarm(); CoffeePot coffeePot = new CoffeePot(); Calendar calender = new Calendar(); Sprinkler sprinkler = new Sprinkler(); Mediator mediator = new ConcreteMediator(alarm, coffeePot, calender, sprinkler); // 闹钟事件到达,调用中介者就可以操作相关对象 alarm.onEvent(mediator);    }}
doAlarm()doCoffeePot()doCalendar()doSprinkler(

6.备忘录(Memento)

lntent

在不违反封装的情况下获得对象的内部状态,从而在需要时可以将对象恢复到最初状态

Class Diagram

  • Originator:原始对象
  • Caretaker:负责保存好备忘录
  • Menento:备忘录,存储原始对象的的状态。 备忘录实际上有两个接口,一个是提供给 Caretaker 的窄接口:它只能将备忘录传递给其它对象; 一个是提供给 Originator 的宽接口,允许它访问到先前状态所需的所有数据。理想情况是只允许 Originator 访问本备忘录的内部状态。

lmplementation

以下实现了一个简单计算器程序,可以输入两个值,然后计算这两个值的和。 备忘录模式允许将这两个值存储起来,然后在某个时刻用存储的状态进行恢复。

/** * Originator Interface */public interface Calculator {    // Create Memento    PreviousCalculationToCareTaker backupLastCalculation();    // setMemento    void restorePreviousCalculation(PreviousCalculationToCareTaker memento);    int getCalculationResult();    void setFirstNumber(int firstNumber);    void setSecondNumber(int secondNumber);}
/** * Originator Implementation */public class CalculatorImp implements Calculator {    private int firstNumber;    private int secondNumber;    @Override    public PreviousCalculationToCareTaker backupLastCalculation() { // create a memento object used for restoring two numbers return new PreviousCalculationImp(firstNumber, secondNumber);    }    @Override    public void restorePreviousCalculation(PreviousCalculationToCareTaker memento) { this.firstNumber = ((PreviousCalculationToOriginator) memento).getFirstNumber(); this.secondNumber = ((PreviousCalculationToOriginator) memento).getSecondNumber();    }    @Override    public int getCalculationResult() { // result is adding two numbers return firstNumber + secondNumber;    }    @Override    public void setFirstNumber(int firstNumber) { this.firstNumber = firstNumber;    }    @Override    public void setSecondNumber(int secondNumber) { this.secondNumber = secondNumber;    }}
/** * Memento Interface to Originator * * This interface allows the originator to restore its state */public interface PreviousCalculationToOriginator {    int getFirstNumber();    int getSecondNumber();}
/** *  Memento interface to CalculatorOperator (Caretaker) */public interface PreviousCalculationToCareTaker {    // no operations permitted for the caretaker}
/** * Memento Object Implementation * 

* Note that this object implements both interfaces to Originator and CareTaker */public class PreviousCalculationImp implements PreviousCalculationToCareTaker, PreviousCalculationToOriginator { private int firstNumber; private int secondNumber; public PreviousCalculationImp(int firstNumber, int secondNumber) { this.firstNumber = firstNumber; this.secondNumber = secondNumber; } @Override public int getFirstNumber() { return firstNumber; } @Override public int getSecondNumber() { return secondNumber; }}

/** * CareTaker object */public class Client {    public static void main(String[] args) { // program starts Calculator calculator = new CalculatorImp(); // assume user enters two numbers calculator.setFirstNumber(10); calculator.setSecondNumber(100); // find result System.out.println(calculator.getCalculationResult()); // Store result of this calculation in case of error PreviousCalculationToCareTaker memento = calculator.backupLastCalculation(); // user enters a number calculator.setFirstNumber(17); // user enters a wrong second number and calculates result calculator.setSecondNumber(-290); // calculate result System.out.println(calculator.getCalculationResult()); // user hits CTRL + Z to undo last operation and see last result calculator.restorePreviousCalculation(memento); // result restored System.out.println(calculator.getCalculationResult());    }}
110-273110

7.观察者(Observer)

lntent

定义对象之间的一对多依赖,当一个对象状态改变时,它的所有依赖都会收到通知并且自动更新状态。

主题(Subject)是被观察的对象,而其所有依赖者(Observer)称为观察者。

Class Diagram

主题(Subject)具有注册和移除观察者、并通知所有观察者的功能,主题是通过维护一张观察者列表来实现这些操作的。

观察者(Observer)的注册功能需要调用主题的 registerObserver() 方法。

lmplementation

天气数据布告板会在天气信息发生改变时更新其内容,布告板有多个,并且在将来会继续增加。

public interface Subject {    void registerObserver(Observer o);    void removeObserver(Observer o);    void notifyObserver();}
public class WeatherData implements Subject {    private List observers;    private float temperature;    private float humidity;    private float pressure;    public WeatherData() { observers = new ArrayList();    }    public void setMeasurements(float temperature, float humidity, float pressure) { this.temperature = temperature; this.humidity = humidity; this.pressure = pressure; notifyObserver();    }    @Override    public void registerObserver(Observer o) { observers.add(o);    }    @Override    public void removeObserver(Observer o) { int i = observers.indexOf(o); if (i >= 0) {     observers.remove(i); }    }    @Override    public void notifyObserver() { for (Observer o : observers) {     o.update(temperature, humidity, pressure); }    }}
public interface Observer {    void update(float temp, float humidity, float pressure);}
public class StatisticsDisplay implements Observer {    public StatisticsDisplay(Subject weatherData) { weatherData.reisterObserver(this);    }    @Override    public void update(float temp, float humidity, float pressure) { System.out.println("StatisticsDisplay.update: " + temp + " " + humidity + " " + pressure);    }}
public class CurrentConditionsDisplay implements Observer {    public CurrentConditionsDisplay(Subject weatherData) { weatherData.registerObserver(this);    }    @Override    public void update(float temp, float humidity, float pressure) { System.out.println("CurrentConditionsDisplay.update: " + temp + " " + humidity + " " + pressure);    }}
/** * 天气预报 */public class WeatherForecast {    public static void main(String[] args) { WeatherData weatherData=new WeatherData(); Observer observer=new StatisticsDisplay(weatherData); Observer observer2=new CurrentConditionsDisplay(weatherData); weatherData.setMeasurements(0.0f,0.0f,0.0f); weatherData.setMeasurements(1.0f,1.0f,1.0f);    }}
CurrentConditionsDisplay.update: 0.0 0.0 0.0StatisticsDisplay.update: 0.0 0.0 0.0CurrentConditionsDisplay.update: 1.0 1.0 1.0StatisticsDisplay.update: 1.0 1.0 1.0​​​​​

8.状态(State)

lntent

允许对象在内部状态改变时改变它的行为,对象看起来好像修改了它所属的类。

Class Diagram

Context:环境,也称上下文,通常用来定义客户感兴趣的接口,同时维护一个来具体处理当前状态的实例对象。

State:状态接口,用来封装与上下文的一个特定状态所对应的行为

ConcreteState:具体实现状态处理的类,每个类实现一个跟上下文相关的状态的具体处理。

lmplementation1

实现在线投票:

一个在线投票的应用,要实现控制同一个用户只能投一票,如果一个用户反复投票,而且投票次数超过5次, 则判定为恶意刷票,要取消该用户投票的资格,当然同时也要取消他所投的票。 如果一个用户的投票次数超过8次,将进入黑名单,禁止再登录和使用系统。

在投票的过程中,又有四种情况:

  • 用户是正常投票

  • 用户正常投票过后,有意或者无意的重复投票

  • 用户恶意投票

  • 黑名单用户

程序结构如下图:

/** * 封装一个投票状态相关的行为 */public interface VoteState {    /**     * TODO:处理状态对应的行为     * @param voter 投票人     * @param voteManager 投票上下文,用来在实现状态对应的功能处理的时候,可以回调上下文的数据     */    void vote(String voter, VoteManager voteManager);}
/** * 投票管理器 */public class VoteManager {    //持有状态处理对象    private VoteState state = null;    //统计用户投票数    private Map mapVoteCount = new HashMap();    public Map getMapVoteCount(){ return mapVoteCount;    }    /**     * 投票     * @param user 投票人,为了简单,就是用户名称     */    public void vote(String user){ //1:先为该用户增加投票的次数 int voteCount=mapVoteCount.getOrDefault(user,0); mapVoteCount.put(user, ++voteCount); //2:判断该用户投票的类型,就相当于是判断对应的状态 //到底是正常投票、重复投票、恶意投票还是上黑名单的状态 if(voteCount==1){     state = new NormalVoteState(); }else if(voteCount>1 && voteCount= 5 && voteCount=8){     state = new BlackVoteState(); } //然后转调状态对象来进行相应的操作 state.vote(user, this);    }}
/** *  正常投票 */public class NormalVoteState implements VoteState{    @Override    public void vote(String voter, VoteManager voteManager) { System.out.println("恭喜你投票成功");    }}
/** * 重复投票 */public class RepeatVoteState implements VoteState{    @Override    public void vote(String voter, VoteManager voteManager) { System.out.println("请不要重复投票");    }}
/** * 恶意投票 */public class SpiteVoteState implements VoteState{    @Override    public void vote(String voter, VoteManager voteManager) { System.out.println("你有恶意刷票行为,取消投票资格");    }}
/** * 黑名单 * 记入黑名单中,禁止登录系统了 */public class BlackVoteState implements VoteState{    @Override    public void vote(String voter, VoteManager voteManager) { System.out.println("进入黑名单,将禁止登录和使用本系统");    }}
public class Client {    public static void main(String[] args) { VoteManager vm = new VoteManager(); for (int i = 0; i < 9; i++) {     vm.vote("u1"); }    }}

输出结果:

恭喜你投票成功请不要重复投票请不要重复投票请不要重复投票你有恶意刷票行为,取消投票资格你有恶意刷票行为,取消投票资格你有恶意刷票行为,取消投票资格进入黑名单,将禁止登录和使用本系统进入黑名单,将禁止登录和使用本系统

lmplementation2

糖果销售机有多种状态,每种状态下销售机有不同的行为,状态可以发生转移,使得销售机的行为也发生改变。

public interface State {    /**     * 投入 25 分钱     */    void insertQuarter();    /**     * 退回 25 分钱     */    void ejectQuarter();    /**     * 转动曲柄     */    void turnCrank();    /**     * 发放糖果     */    void dispense();}
public class HasQuarterState implements State {    private GumballMachine gumballMachine;    public HasQuarterState(GumballMachine gumballMachine) { this.gumballMachine = gumballMachine;    }    @Override    public void insertQuarter() { System.out.println("You can't insert another quarter");    }    @Override    public void ejectQuarter() { System.out.println("Quarter returned"); gumballMachine.setState(gumballMachine.getNoQuarterState());    }    @Override    public void turnCrank() { System.out.println("You turned..."); gumballMachine.setState(gumballMachine.getSoldState());    }    @Override    public void dispense() { System.out.println("No gumball dispensed");    }}
public class NoQuarterState implements State {    GumballMachine gumballMachine;    public NoQuarterState(GumballMachine gumballMachine) { this.gumballMachine = gumballMachine;    }    @Override    public void insertQuarter() { System.out.println("You insert a quarter"); gumballMachine.setState(gumballMachine.getHasQuarterState());    }    @Override    public void ejectQuarter() { System.out.println("You haven't insert a quarter");    }    @Override    public void turnCrank() { System.out.println("You turned, but there's no quarter");    }    @Override    public void dispense() { System.out.println("You need to pay first");    }}
public class SoldOutState implements State {    GumballMachine gumballMachine;    public SoldOutState(GumballMachine gumballMachine) { this.gumballMachine = gumballMachine;    }    @Override    public void insertQuarter() { System.out.println("You can't insert a quarter, the machine is sold out");    }    @Override    public void ejectQuarter() { System.out.println("You can't eject, you haven't inserted a quarter yet");    }    @Override    public void turnCrank() { System.out.println("You turned, but there are no gumballs");    }    @Override    public void dispense() { System.out.println("No gumball dispensed");    }}
public class SoldState implements State {    GumballMachine gumballMachine;    public SoldState(GumballMachine gumballMachine) { this.gumballMachine = gumballMachine;    }    @Override    public void insertQuarter() { System.out.println("Please wait, we're already giving you a gumball");    }    @Override    public void ejectQuarter() { System.out.println("Sorry, you already turned the crank");    }    @Override    public void turnCrank() { System.out.println("Turning twice doesn't get you another gumball!");    }    @Override    public void dispense() { gumballMachine.releaseBall(); if (gumballMachine.getCount() > 0) {     gumballMachine.setState(gumballMachine.getNoQuarterState()); } else {     System.out.println("Oops, out of gumballs");     gumballMachine.setState(gumballMachine.getSoldOutState()); }    }}
public class GumballMachine {    private State soldOutState;    private State noQuarterState;    private State hasQuarterState;    private State soldState;    private State state;    private int count = 0;    public GumballMachine(int numberGumballs) { count = numberGumballs; soldOutState = new SoldOutState(this); noQuarterState = new NoQuarterState(this); hasQuarterState = new HasQuarterState(this); soldState = new SoldState(this); if (numberGumballs > 0) {     state = noQuarterState; } else {     state = soldOutState; }    }    public void insertQuarter() { state.insertQuarter();    }    public void ejectQuarter() { state.ejectQuarter();    }    public void turnCrank() { state.turnCrank(); state.dispense();    }    public void setState(State state) { this.state = state;    }    public void releaseBall() { System.out.println("A gumball comes rolling out the slot..."); if (count != 0) {     count -= 1; }    }    public State getSoldOutState() { return soldOutState;    }    public State getNoQuarterState() { return noQuarterState;    }    public State getHasQuarterState() { return hasQuarterState;    }    public State getSoldState() { return soldState;    }    public int getCount() { return count;    }}Copy to clipboardErrorCopied
public class Client {    public static void main(String[] args) { GumballMachine gumballMachine = new GumballMachine(5); gumballMachine.insertQuarter(); gumballMachine.turnCrank(); gumballMachine.insertQuarter(); gumballMachine.ejectQuarter(); gumballMachine.turnCrank(); gumballMachine.insertQuarter(); gumballMachine.turnCrank(); gumballMachine.insertQuarter(); gumballMachine.turnCrank(); gumballMachine.ejectQuarter(); gumballMachine.insertQuarter(); gumballMachine.insertQuarter(); gumballMachine.turnCrank(); gumballMachine.insertQuarter(); gumballMachine.turnCrank(); gumballMachine.insertQuarter(); gumballMachine.turnCrank();    }}
You insert a quarterYou turned...A gumball comes rolling out the slot...You insert a quarterQuarter returnedYou turned, but there's no quarterYou need to pay firstYou insert a quarterYou turned...A gumball comes rolling out the slot...You insert a quarterYou turned...A gumball comes rolling out the slot...You haven't insert a quarterYou insert a quarterYou can't insert another quarterYou turned...A gumball comes rolling out the slot...You insert a quarterYou turned...A gumball comes rolling out the slot...Oops, out of gumballsYou can't insert a quarter, the machine is sold outYou turned, but there are no gumballsNo gumball dispensed

9.策略(Strategy)

lntent

定义一系列算法,封装每个算法,并使它们可以互换。

策略模式可以让算法独立于使用它的客户端。

Class Diagram

  • Strategy 接口定义了一个算法族,它们都实现了 behavior() 方法。
  • Context 是使用到该算法族的类,其中的 doSomething() 方法会调用 behavior(),setStrategy(Strategy) 方法可以动态地改变 strategy 对象,也就是说能动态地改变 Context 所使用的算法。

与状态模式的比较

状态模式的类图和策略模式类似,并且都是能够动态改变对象的行为。 但是状态模式是通过状态转移来改变 Context 所组合的 State 对象, 而策略模式是通过 Context 本身的决策来改变组合的 Strategy 对象。 所谓的状态转移,是指 Context 在运行过程中由于一些条件发生改变而使得 State 对象发生改变, 注意必须要是在运行过程中。

状态模式主要是用来解决状态转移的问题,当状态发生转移了,那么 Context 对象就会改变它的行为; 而策略模式主要是用来封装一组可以互相替代的算法族,并且可以根据需要动态地去替换 Context 使用的算法。

lmplementation1

报价管理:向客户报价,对于销售部门的人来讲,这是一个非常重大、非常复杂的问题,对不同的客户要报不同的价格,比如:

  1. 对普通客户或者是新客户报的是全价
  2. 对老客户报的价格,根据客户年限,给予一定的折扣
  3. 对大客户报的价格,根据大客户的累计消费金额,给予一定的折扣

/** * 策略,定义计算报价算法的接口 */public interface Strategy {    /**     * 计算报价     * @param goodsPrice 商品原价     */   void calcPrice(double goodsPrice);}
public class NormalCustomerStrategy implements Strategy{    @Override    public void calcPrice(double goodsPrice) { System.out.println("对于新客户或者是普通客户,没有折扣:"+goodsPrice);    }}
public class OldCustomerStrategy implements Strategy{    @Override    public void calcPrice(double goodsPrice) { System.out.println("对于老客户,统一折扣5%:"+goodsPrice*(1-0.05));    }}
public class LargeCustomerStrategy implements Strategy{    @Override    public void calcPrice(double goodsPrice) { System.out.println("对于大客户,统一折扣10%:"+goodsPrice*(1-0.1));    }}
/** * Context 是使用到该算法族的类,其中的 doSomething()方法会调用 behavior(), * setStrategy(Strategy) 方法可以动态地改变 strategy 对象, * 也就是说能动态地改变 Context 所使用的算法。 */public class Price {    private Strategy strategy;    public void price(double goodsPrice){strategy.calcPrice(goodsPrice);    }    public void setStrategy(Strategy strategy) { this.strategy = strategy;    }}
public class Client {    public static void main(String[] args) { Price price = new Price(); price.setStrategy(new NormalCustomerStrategy()); price.price(1000.0); price.setStrategy(new OldCustomerStrategy()); price.price(1000.0); price.setStrategy(new LargeCustomerStrategy()); price.price(1000.0);    }}
对于新客户或者是普通客户,没有折扣:1000.0对于老客户,统一折扣5%:950.0对于大客户,统一折扣10%:900.0

lmplementation2

设计一个鸭子,它可以动态地改变叫声。这里的算法族是鸭子的叫声行为。

/** * 策略 */public interface Strategy {    void behavior();}
public class QuackStrategy implements Strategy{    @Override    public void behavior() { System.out.println("quack!");    }}
public class SqueakStrategy implements Strategy{    @Override    public void behavior() { System.out.println("squeak!");    }}
/** * Context 是使用到该算法族的类,其中的 doSomething()方法会调用 behavior(), * setStrategy(Strategy) 方法可以动态地改变 strategy 对象, * 也就是说能动态地改变 Context 所使用的算法。 */public class Duck {    private Strategy strategy;    public void perform(){ if(strategy!=null){     strategy.behavior(); }    }    public void setStrategy(Strategy strategy) { this.strategy = strategy;    }}
public class Client {    public static void main(String[] args) { Duck duck = new Duck(); duck.setStrategy(new QuackStrategy()); duck.perform(); duck.setStrategy(new SqueakStrategy()); duck.perform();    }}
quack!squeak!

10.模板方法(Template Method)

lntent

定义算法框架,并将一些步骤的实现延迟到子类。

通过模板方法,子类可以重新定义算法的某些步骤,而不用改变算法的结构。

Class Diagram

lmplementation

冲咖啡和冲茶都有类似的流程,但是某些步骤会有点不一样,要求复用那些相同步骤的代码。

public abstract class CaffeineBeverage {    final void prepareRecipe() { boilWater(); brew(); pourInCup(); addCondiments();    }    abstract void brew();    abstract void addCondiments();    void boilWater() { System.out.println("boilWater");    }    void pourInCup() { System.out.println("pourInCup");    }}
public class Coffee extends CaffeineBeverage {    @Override    void brew() { System.out.println("Coffee.brew");    }    @Override    void addCondiments() { System.out.println("Coffee.addCondiments");    }}
public class Tea extends CaffeineBeverage {    @Override    void brew() { System.out.println("Tea.brew");    }    @Override    void addCondiments() { System.out.println("Tea.addCondiments");    }}
public class Client {    public static void main(String[] args) { CaffeineBeverage caffeineBeverage = new Coffee(); caffeineBeverage.prepareRecipe(); System.out.println("-----------"); caffeineBeverage = new Tea(); caffeineBeverage.prepareRecipe();    }}
boilWaterCoffee.brewpourInCupCoffee.addCondiments-----------boilWaterTea.brewpourInCupTea.addCondiments

11.访问者(Visitor)

lntent

为一个对象结构(比如组合结构)增加新能力。

Class Diagram

  • Visitor:访问者,为每一个 ConcreteElement 声明一个 visit 操作
  • ConcreteVisitor:具体访问者,存储遍历过程中的累计结果
  • ObjectStructure:对象结构,可以是组合结构,或者是一个集合。

lmplementation

public interface Element {    void accept(Visitor visitor);}
class CustomerGroup {    private List customers = new ArrayList();    void accept(Visitor visitor) { for (Customer customer : customers) {     customer.accept(visitor); }    }    void addCustomer(Customer customer) { customers.add(customer);    }}
public class Customer implements Element {    private String name;    private List orders = new ArrayList();    Customer(String name) { this.name = name;    }    String getName() { return name;    }    void addOrder(Order order) { orders.add(order);    }    public void accept(Visitor visitor) { visitor.visit(this); for (Order order : orders) {     order.accept(visitor); }    }}
public class Order implements Element {    private String name;    private List items = new ArrayList();    Order(String name) { this.name = name;    }    Order(String name, String itemName) { this.name = name; this.addItem(new Item(itemName));    }    String getName() { return name;    }    void addItem(Item item) { items.add(item);    }    public void accept(Visitor visitor) { visitor.visit(this); for (Item item : items) {     item.accept(visitor); }    }}
public class Item implements Element {    private String name;    Item(String name) { this.name = name;    }    String getName() { return name;    }    public void accept(Visitor visitor) { visitor.visit(this);    }}
public interface Visitor {    void visit(Customer customer);    void visit(Order order);    void visit(Item item);}
public class GeneralReport implements Visitor {    private int customersNo;    private int ordersNo;    private int itemsNo;    public void visit(Customer customer) { System.out.println(customer.getName()); customersNo++;    }    public void visit(Order order) { System.out.println(order.getName()); ordersNo++;    }    public void visit(Item item) { System.out.println(item.getName()); itemsNo++;    }    public void displayResults() { System.out.println("Number of customers: " + customersNo); System.out.println("Number of orders:    " + ordersNo); System.out.println("Number of items:     " + itemsNo);    }}
public class Client {    public static void main(String[] args) { Customer customer1 = new Customer("customer1"); customer1.addOrder(new Order("order1", "item1")); customer1.addOrder(new Order("order2", "item1")); customer1.addOrder(new Order("order3", "item1")); Order order = new Order("order_a"); order.addItem(new Item("item_a1")); order.addItem(new Item("item_a2")); order.addItem(new Item("item_a3")); Customer customer2 = new Customer("customer2"); customer2.addOrder(order); CustomerGroup customers = new CustomerGroup(); customers.addCustomer(customer1); customers.addCustomer(customer2); GeneralReport visitor = new GeneralReport(); customers.accept(visitor); visitor.displayResults();    }}
customer1order1item1order2item1order3item1customer2order_aitem_a1item_a2item_a3Number of customers: 2Number of orders:    4Number of items:     6

12.空对象(Null)

lntent

使用什么都不做的空对象来代替 NULL。

一个方法返回 NULL,意味着方法的调用端需要去检查返回值是否是 NULL,这么做会导致非常多的冗余的检查代码。并且如果某一个调用端忘记了做这个检查返回值,而直接使用返回的对象,那么就有可能抛出空指针异常。

Class Diagram

lmplementation

public abstract class AbstractOperation {    abstract void request();}
public class RealOperation extends AbstractOperation {    @Override    void request() { System.out.println("do something");    }}
public class NullOperation extends AbstractOperation{    @Override    void request() { // do nothing    }}
public class Client {    public static void main(String[] args) { AbstractOperation abstractOperation = func(-1); abstractOperation.request();    }    public static AbstractOperation func(int para) { if (para < 0) {     return new NullOperation(); } return new RealOperation();    }}