设计模式 行为篇

策略模式 Strategy Pattern

函数的参数是个接口 / 抽象类,那么这个函数就可以接收接口的不同实现类的实例。

对于接口定义的方法,这些不同的接口实现类中,不同的方法实现,就代表了不同的策略。

Andriod 中 使用动画时,你可以选择线性插值器 LinearInterpolator、加速减速插值器AccelerateDecelerateInterpolator、减速插值器 DecelerateInterpolator 等,这就是不同的策略

interface IShareStrategy{
    void share(String msg);
}

class WeiboStrategy implements IShareStrategy{
    void share(String){
        // do media things
    }
}

class WechatStrategy implements IShareStrategy{
    void share(String){
        // do greate wall things
    }
}
 
public class ShareModel{
    String msg;
    void setMsg(String msg){
        this.msg = msg ;
    }
    // 也可以让 ShareModel 持有一个 IShareStrategy 引用。
    void share(IShareStrategy strategy){
        strategy.share(msg);
    }
}

状态模式 State

用于 context 的不同状态时,同样的操作调用不同的行为。

与策略模式的本质区别是:状态模式的 context 存在一个持续存在的状态。而策略模式处理的行为是单一的,一次性的。

也就是 context 会持有一个 state (由 context 自己创建),同时 state 也可以持有 context (可以作为类的成员变量,也可以作为函数的参数)。

因为持有 context,所以 state 的函数里可以给 context 替换其他的 state。

interface Statelike {
    void writeName(StateContext context, String name);
}

class StateLowerCase implements Statelike {
    @Override
    public void writeName(final StateContext context, final String name) {
        System.out.println(name.toLowerCase());
        context.setState(new StateMultipleUpperCase());
    }
}

class StateMultipleUpperCase implements Statelike {
    /** Counter local to this state */
    private int count = 0;

    @Override
    public void writeName(final StateContext context, final String name) {
        System.out.println(name.toUpperCase());
        /* Change state after StateMultipleUpperCase's writeName() gets invoked twice */
        if(++count > 1) {
            context.setState(new StateLowerCase());
        }
    }
}

class StateContext {
    private Statelike myState;
    StateContext() {
        setState(new StateLowerCase());
    }

    /**
     * Setter method for the state.
     * Normally only called by classes implementing the State interface.
     * @param newState the new state of this context
     */
    void setState(final Statelike newState) {
        myState = newState;
    }

    public void writeName(final String name) {
        myState.writeName(this, name);
    }
}

命令模式 Command

三种角色:调用者 Invoker,命令 Command,接收者 Receiver 接收者 可以有多种,比如 电灯,电视, 每个接收可以有多种命令,比如打开电灯关闭电灯,电视换台电视音量增加 业务流程是 调用者 发送命令 给接受者执行。

public interface Command {
   void execute();
}

/** The Invoker class */
public class Switch {

   public void execute(final Command cmd) {
      cmd.execute();
   }
}

/** The Receiver class */
public class Light {

   public void turnOn() {
      System.out.println("The light is on");
   }

   public void turnOff() {
      System.out.println("The light is off");
   }
}

/** The Command for turning on the light - ConcreteCommand #1 */
public class LightOnCommand implements Command {
   private Light theLight;

   public LightOnCommand(final Light light) {
      this.theLight = light;
   }

   @Override  
   public void execute() {
      theLight.turnOn();
   }
}

/** The Command for turning off the light - ConcreteCommand #2 */
public class LightOffCommand implements Command {
   private Light theLight;

   public LightOffCommand(final Light light) {
      this.theLight = light;
   }

   @Override 
   public void execute() {
      theLight.turnOff();
   }
}

/* The test class or client */
public class PressSwitch {
   public static void main(final String[] arguments){
      final Light lamp = new Light();
      final Command switchUp = new LightOnCommand(lamp); 
      final Switch mySwitch = new Switch();
      mySwitch.execute(switchUp);
      
   }
}

中介者模式 mediator

也叫调停者模式。

系统中对象之间存在复杂的引用关系,类之间的交互很复杂时,通过一个中间类来切断类之间的交互,所有交互都通过中介者中转。

GUI 开发中的 MVC,MVP 之类的这些几乎都会用到中介者模式,下面的代码就很类似 View 之间的相互操作。

interface Mediator {
    void registerColleague1(Colleague c);
    void operationColleague1(String arg);

    void registerColleague2(Colleague c);
    void operationColleague2(String arg);
}

class ConcreteMediator implements Mediator {

    // 也可以使用 map 来存多个实例。
    Colleague c1 ;
    Colleague c2 ;

    void setColleague1(Colleague c){
        this.c1 = c ;
    }
    void operationColleague1(String arg){
        this.c1.operation(arg);
    }
    void setColleague2(Colleague c){
        this.c2 = c ;
    }
    void operationColleague2(String arg){
        this.c2.operation(arg);
    }
}

interface Colleague {
    void setMediator(Mediator mediator);
    void operation(String arg);
}

class ConcreteColleague1 implements Colleague {

    Mediator mediator;

    void void setMediator(Mediator mediator){
        this.mediator = mediator;
        this.mediator.setColleague1(this);
    }
    void operation(String arg) {
        System.out.println("Colleague1 "+arg);
    }
}
 
class ConcreteColleague2 implements Colleague {

    Mediator mediator;

    void void setMediator(Mediator mediator){
        this.mediator = mediator;
        this.mediator.setColleague2(this);
    }
    void operation(String arg) {
        System.out.println("Colleague2 "+arg);
        this.mediator.operationColleague1("called from Colleague2");
    }
}

public class Demo {
    public static void main(String[] args) {
        Mediator mediator = new ConcreteMediator();
        Colleague c1 = new ConcreteColleague1();
        Colleague c2 = new ConcreteColleague1();
        c1.setMediator(mediator);
        c2.setMediator(mediator);

        c2.operation("hahaha");
    }
}

访问者模式 Visitor

使用一个访问者类,改变了数据类的执行算法。这样,执行的算法可以随着访问者改变而改变。

可以在不改变数据结构的前提下,定义一些新的操作。

有这几个角色:Element 接口,Element 具体实现类(数据所在类)。Visitor 接口,Visitor 具体实现类。

Element 接口定义了 accept 方法。Visitor 接口定义了访问不同 Element(参数是不同的 Element) 的 visit 方法。

在使用时,Client 会调用 Element 的 accept,把 Element 具体实现类暴露给 Visitor 实现类,达到可以随访问者不同而操作不同的目的。访问者模式实际上违反了依赖倒置原则,依赖了具体类,没有依赖抽象。

Android中 中 APT 技术,就是在编译期使用访问者模式。

interface Element {
    void accept(Visitor visitor);
}

interface Visitor {
    void visit(Head head);
    void visit(Body body);
}

class Head implements Element {
    public void accept(final Visitor visitor) {
        visitor.visit(this);
    }
}

class Body implements Element {
    public void accept(final Visitor visitor) {
        visitor.visit(this);
    }
}

class Animal implements Element {
    Element[] elements;

    public Animal() {
        this.elements = new Element[] {
            new Head(), 
            new Body()
        };
    }

    public void accept(final Visitor visitor) {
        for (Element elem : elements) {
            elem.accept(visitor);
        }
        visitor.visit(this);
    }
}

class DoVisitor implements Visitor {
    public void visit(final Head head) {
        System.out.println("call some head function or something");
    }
    public void visit(final Body body) {
        System.out.println("change body type");
    }
}

class PrintVisitor implements Visitor {
    public void visit(final Head head) {
        System.out.println("print head status");
    }
    public void visit(final Body body) {
        System.out.println("print body status");
    }
}

public class Demo {
    public static void main(final String[] args) {
        final Animal animal = new Animal();
        animal.accept(new PrintVisitor());
        animal.accept(new DoVisitor());
    }
}

模版方法模式 template method

这个是 Android 应用开发天天打交道的内容,onCreate,onStart,onResume,都是模版方法。

public abstract class Game { abstract void initialize(); abstract void startPlay(); abstract void endPlay();

public final void play(){

  //初始化游戏
  initialize();

  //开始游戏
  startPlay();

  //结束游戏
  endPlay();    } }

public class Football extends Game { @Override void initialize() { System.out.println(“Football Game Initialized! Start playing.”); } @Override void startPlay() { System.out.println(“Football Game Started. Enjoy the game!”); } @Override void endPlay() { System.out.println(“Football Game Finished!”); } }

public class TemplatePatternDemo { public static void main(String[] args) {

  game = new Football();
  game.play();            } }

迭代器模式 Iterator

提供一种方法顺序访问一个容器对象中的各个元素,而不需要暴露该对象的内部表示。

Iterator 这个角色的思想与组合模式类似。

Java 中的迭代器 Iterator 类,就是一个迭代器模式的模版接口。Android 中,Cursor 用到的迭代器模式,SQLiteDatabase 的 query 返回的就是 Cursor对象

interface Aggregate{
    void Iterator getIterator();
}

interface Iterator{
    void Object next();
    void boolean hasNext();
}

class ConcreteAggregate implements Aggregate{
    private Object[] datas ;

    public Iterator getIterator(){
        return new ConcreteIterator();
    }

    class ConcreteIterator implements Iterator{
        int index;

        @Override
        public boolean hasNext() {
            if(index < datas.length){
                return true;
            }
            return false;
        }

        @Override
        public Object next() {
            if(this.hasNext()){
                return datas[index++];
            }
            return null;
        }
    }

}

责任链模式 Chain-of-responsibility

由一堆命令模式中的命令角色 和 一些处理对象 组成。

if else 的 OO 版本,可以在 runtime 动态的安排功能。

责任链模式与装饰者模式在结构上很相似,但是区别在于业务角色的功能不同,装饰者的所有装饰者角色都负责一个功能,责任链中只有一个特别的类负责处理功能。

OkHttp 中 chain 的调用,也是一种责任链模式

abstract class PurchasePower {
    protected static final double BASE = 500;

    protected PurchasePower successor;

    abstract protected double getAllowable();
    abstract protected String getRole();

    public void setSuccessor(PurchasePower successor) {
        this.successor = successor;
    }

    public void processRequest(PurchaseRequest request) {
        if (request.getAmount() < this.getAllowable()) {
            System.out.println(this.getRole() + " will approve $" + request.getAmount());
        } else if (successor != null) {
            successor.processRequest(request);
        }
    }
}

class ManagerPPower extends PurchasePower {
    protected double getAllowable() {
        return BASE * 10;
    }
    protected String getRole() {
        return "Manager";
    }
}

class DirectorPPower extends PurchasePower {
    protected double getAllowable() {
        return BASE * 20;
    }
    protected String getRole() {
        return "Director";
    }
}

class VicePresidentPPower extends PurchasePower {
    protected double getAllowable() {
        return BASE * 40;
    }
    protected String getRole() {
        return "Vice President";
    }
}

class PurchaseRequest {

    private double amount;

    public PurchaseRequest(double amount, String purpose) {
        this.amount = amount;
    }
    public double getAmount() {
        return this.amount;
    }
    public void setAmount(double amount)  {
        this.amount = amount;
    }
}

class CheckAuthority {
    public static void main(String[] args) {
        ManagerPPower manager = new ManagerPPower();
        DirectorPPower director = new DirectorPPower();
        VicePresidentPPower vp = new VicePresidentPPower();

        manager.setSuccessor(director);
        director.setSuccessor(vp);

        manager.processRequest(new PurchaseRequest(13000, "General"));
    }
}

备忘录模式 Memento

保存程序不同状态时的状态数据,可以用来实现存档,撤销,暂存,备份等操作。

主要特点是可以在不破坏封闭的前提下,保存一个对象的内部状态,并在对象之外保存这个状态。

Android 中 Activity 的 onSaveInstanceState 和 onRestoreInstanceState 就是运用了备忘录模式。

// 存放状态数据的类
public class Memento {
   private String state;
   public Memento(String state){
      this.state = state;
   }
   public String getState(){
      return state;
   }
}

// 业务逻辑所在的类
public class Originator {
   private String state;
   public void setState(String state){
      this.state = state;
   }
   public String getState(){
      return state;
   }
   public Memento saveStateToMemento(){
      return new Memento(state);
   }
   public void getStateFromMemento(Memento memento){
      state = memento.getState();
   }
}

// 管理状态,所有存档都在内存中。如果有需要,也可以在这里实现使用外部存储的机制。
public class CareTaker {
   private List<Memento> mementoList = new ArrayList<Memento>();
   public void add(Memento state){
      mementoList.add(state);
   }
   public Memento get(int index){
      return mementoList.get(index);
   }
}

public class MementoPatternDemo {
   public static void main(String[] args) {
      Originator originator = new Originator();
      CareTaker careTaker = new CareTaker();
      originator.setState("State #1");
      originator.setState("State #2");
      careTaker.add(originator.saveStateToMemento());
      originator.setState("State #3");

      System.out.println("Current State: " + originator.getState());        
      originator.getStateFromMemento(careTaker.get(0));
      System.out.println("First saved State: " + originator.getState());
   }
}

观察者模式 Observer Pattern

这也是一个与状态操作相关的模式,一个对象的状态发生改变时,所有依赖于它的对象都得到通知 并 可以自动更新

Java 中有专门的 java.util.Observable 类 和 java.util.Observer 接口,其中 Observable 与下面的代码的区别只是多加了一个 setChanged() 方法,标记 Observable 对象的数据已经发生变化。

  • 抽象主题(Subject)角色 / Observable 抽象主题角色 把 所有对观察者对象 的引用 保存在一个集合(比如 ArrayList 对象)里,每个 Subject 都可以有任何数量的观察者。抽象主题提供一个接口,可以增加和删除观察者对象,Subject 角色又叫做 抽象 Observable

  • 具体主题(ConcreteSubject)角色 / Concrete Observable 将有关状态存入具体观察者对象;在具体主题的内部状态改变时,给所有登记过的观察者发出通知。具体主题角色又叫做 Concrete Observable

  • 抽象观察者(Observer)角色: 为所有的具体观察者定义一个接口,在得到主题的通知时更新自己,这个接口叫做更新接口。

  • 具体观察者(ConcreteObserver)角色: 存储与主题的状态自恰的状态。具体观察者角色实现抽象观察者角色所要求的更新接口,以便使本身的状态与 Subject 的状态相同。如果需要,ConcreteObserver 可以持有一个指向具体主题对象的引用。

public abstract class Subject {
  private List<Observer> list = new ArrayList<Observer>();
  public void attach(Observer observer){   
  list.add(observer);
  }
  public void detach(Observer observer){
  list.remove(observer);
  }
  public void nodifyObservers(String newState){
  for(Observer observer : list){
  observer.update(newState);
  }
  }
}
public class ConcreteSubject extends Subject{
  private String state;
  public String getState() {
  return state;
  }
  public void setState(String newState){
  state = newState;
  //状态发生改变,通知各个观察者
  this.nodifyObservers(state);
  }
}
public interface Observer {
  public void update(String state);
}
public class ConcreteObserver implements Observer {
  private String observerState;

  @Override
  public void update(String state) {
  observerState = state;
  }
}
public class Client {
  public static void main(String[] args) {
  ConcreteSubject subject = new ConcreteSubject();
  Observer observer = new ConcreteObserver();
  subject.attach(observer);
  subject.setState("new state");
  }
}

解释器模式 Interpreter

对于一种固定文法语句或者说一种表达式,定义一个解释器,用来解释语言中的句子。

如果还能记得编译原理的课程,这个解释器模式跟编译原理中的解释器是一种东西。

解释器模式并不负责解析出语法树。从代码来说,解释器模式只是一个典型的面向对象多态。

主要角色是 Expression 声明一个抽象的解释操作,该接口为抽象语法树中所有的节点共享。

Expression 有两个子类,TerminalExpression 和 NonterminalExpression。分别是终结符表达式和非终结符表达式。终结符通常就是一些运算符和控制符,所以终结符表达式通常是一个小运算单元。另外还有 context / client 角色。

通常来说,各种 parser,比如 Android 开发中常用的布局解析 XML parser 都会使用解释器模式。

public interface Expression {
    int interpret();
}

public class NonterminalExpression implements Expression {
    private int value;

    public NonterminalExpression(int value) {
        this.value = value;
    }

    public int interpret() {
        return this.value;
    }
}

public abstract class TerminalExpression implements Expression {
    protected Expression left;
    protected Expression right;

    public TerminalExpression(Expression left, Expression right) {
        this.left = left;
        this.right = right;
    }
}

public class Plus extends TerminalExpression {
    public Plus(Expression left, Expression right) {
        super(left, right);
    }

    public int interpret() {
        return left.interpret() + right.interpret();
    }
}

public class Minus extends TerminalExpression {
    public Minus(Expression left, Expression right) {
        super(left, right);
    }

    public int interpret() {
        return super.left.interpret() - super.right.interpret();
    }
}

public class Evaluator {
    private String statement;
    private Expression expression;

    public Evaluator(String statement) {
        Expression left = null, right = null;
        Stack stack = new Stack();

        String[] statementArr = statement.split(" ");

        for (int i = 0; i < statementArr.length; i=i+1) {
            if (statementArr[i].equalsIgnoreCase("+")) {
                left = (Expression) stack.pop();
                int val = Integer.parseInt(statementArr[i+1]);
                i=i+1
                right = new NonterminalExpression(val);
                stack.push(new Plus(left, right));
            } else if (statementArr[i].equalsIgnoreCase("-")) {
                left = (Expression) stack.pop();
                int val = Integer.parseInt(statementArr[i+1]);
                i=i+1
                right = new NonterminalExpression(val);
                stack.push(new Minus(left, right));
            } else {
                stack.push(new NonterminalExpression(Integer.parseInt(statementArr[i])));
            }
        }
        this.expression = (Expression) stack.pop();
    }

    public int compute()
        return expression.interpret();
}

public class Client {
    public static void main(String args[]) {
        String statement = "11 + 6 - 1";
        Evaluator evaluator = new Evaluator(statement);
        int result = evaluator.compute();
        System.out.println(statement + " = " + result);
    }
}

参考

https://en.wikipedia.org/wiki/Design_Patterns