行为模式

责任链

使多个对象都有机会处理请求,避免发送者与接受者之间的耦合

classDiagram    direction LR    class Client {        handler1    }    class ConcreteHandler1 { next }    class ConcreteHandler2 { next }    class ConcreteHandler3 { next }    Client ..> ConcreteHandler1    ConcreteHandler1 --> ConcreteHandler2    ConcreteHandler2 --> ConcreteHandler3
public interface Filter {    void doFilter(ServletRequest var1, ServletResponse var2, FilterChain var3) throws IOException, ServletException;}public class LoggingFilter implements Filter {    @Override    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {        //...    }}

在GOF的书中描述的责任链每个节点都有一个后继节点,前驱节点可根据特定的逻辑来将请求传递到后继节点来进行处理,代表着一种责任的转移。

另外一种责任链的变体模式:

  1. 引入一个责任链实体,即责任链节点编排器:
public class Chain {    void process(){        for(var chainNode : chain) {            if (!chainNode.process()) {                break;            }        }    }}
  1. 责任链节点可通过传回一个布尔值或者抛出异常来终止整个流程。

这种方式的问题在于无法实现类似于Web Filter的对Response的过滤处理。标准的责任链模式 Filter 可以在后继Filter处理完毕后回来处理后继Filter处理过的Response,而通过引入一个第三方编排角色的方式则无法实现。

命令

将请求封装为对象

该模式解耦了调用请求的对象与知道如何处理该请求的对象

classDiagram    class Invoker {setCommand()}    class ICommand {execute()}    class Receiver {action()}    class ConcreteCommand {execute()}    Invoker o--> ICommand    ConcreteCommand --> Receiver    ConcreteCommand --|> ICommand    Client --> Receiver    Client .. ConcreteCommand
sequenceDiagram    Client -->> Receiver: new Receiver()    Client -->> ConcreteCommand: new ConcreteCommand(receiver)    Client -->> Invoker: new Invoker(command)    Client ->> Invoker: call()    Invoker ->> ConcreteCommand: execute()    ConcreteCommand ->> Receiver: action()
interface Command {    void execute();}class ConcreteCommand1 implements Command {    Reveiver reveiver;    void execute() {        reveiver.action(); // do something    }}class ConcreteCommand2 implements Command {...}class Invoker {    void invoke(){        Command command;        command.execute();    }}

在该模式的实现中,一个很重要的一点是可以考虑在Invoker记录该命令的操作历史,定义另外一个接口unexecute 来实现对命令撤销。

解释器

定义一个文法,定义一个解释器,解释器解释执行做一些操作

classDiagram    Context <.. AbstractExpression    class AbstractExpression {        +interpret(Context ctx)    }    class TerminalExpression {        +interpret(Context ctx)    }    class NoneTerminalExpression {        +interpret(Context ctx)    }    AbstractExpression <|-- TerminalExpression    AbstractExpression <|-- NoneTerminalExpression    Client ..> Context    Client ..> AbstractExpression

迭代器

提供一种顺序访问对象中的各个元素,并不暴露内部表示

classDiagram    class Container {        +getIterator()    }    class Iterator {        +next()        +hasNext()    }    Container <|-- ConcreteContainer    Iterator <|-- ConcreteIterator    ConcreteContainer --> ConcreteIterator: 拥有
interface Iterator{    hasNext();    next();}class ArrayListItr implements Iterator{...}

中介者

用一个中介对象封装一系列对象之间的交互

classDiagram    Mediator <-- Colleague    Mediator <|-- ConcreteMediator    Colleague <|-- ConcreteColleague    ConcreteMediator --> ConcreteColleague

备忘录

不破坏封装性的情况下,保存一个对象的内部状态

classDiagram    class Originator {        +State        +SetMemento(m: Memento)        +CreateMemento()    }    class Memento {        +State    }    class CareTaker {        -Memento: Memento    }    Originator ..> Memento    CareTaker o--> Memento

观察者

定义对象间一对多的依赖关系,依赖它的对象都会得到通知并自动更新

classDiagram    class Subject {        +add(ob Observer)        +del(ob Observer)        +notify()    }    class Observer {        +update()    }    Subject --> Observer    Subject <|-- ConcreteSubject    Observer <|-- ConcreteObserver
abstract class Subject{    add(Observer ob);    del(Observer ob);    notify();}interface Observer{    update();}class ConcreteSubject{    notify(){        obServerlist.forEachNotify();    }}class ConcreteObserver implements Observer{    ...}

状态

允许一个对象在其内部状态改变时改变它的行为

classDiagram    direction BT    class Context {        -state        +setState()        +request()    }    class State {        +handle()    }    class ConcreteStateA {        +handle()    }    class ConcreteStateB {        +handle()    }    ConcreteStateA --|> State    ConcreteStateB --|> State    Context o--> State
class LoginContext{    private UserState state;    login(){        state.login()    }}interface UserState{    login();}class UserNormalState{    login(){        if (loginFailCount == 5){            context.state = new UserBannedState();        }        print "login success";    }}class UserBannedState{    login(){        print "you are banned";    }}

策略模式

封装一系列算法,以使它们可以互相替换

classDiagram    direction BT    class Context {        +algorithm()        +setStrategy()    }    class Strategy {        +algorithm()    }    class ConcreteAlgorithmA {        +algorithm()    }    class ConcreteAlgorithmB {        +algorithm()    }    ConcreteAlgorithmA --|> Strategy    ConcreteAlgorithmB --|> Strategy    Context o--> Strategy
interface Strategy{    void execute(..);}class Context {    private Strategy strategy;    public Context(Strategy strategy) {        this.strategy = strategy;    }        void execute(){        strategy.execute(..);    }}class StrategyA implements Strategy{    // 具体实现}// 使用Context context = new Context(new StrategyA());context.execute();

策略模式要求客户代码必须了解不同策略模式的差异,也就是客户必须针对具体实现进行编码。

另外一个要考虑的是不同策略所需要的参数不同,虽然可以一股脑将全部参数传递给具体策略,但为了降低性能开销,某些策略并不需要用到那么多参数,此时可以传递一个Context的引用给具体策略,让具体策略按需索取,但这种方式则对Context接口的粒度做了过细的要求。

组合模式 + 策略模式

可以通过组合多个策略得到一个新的策略:

class CompositeStrategy implements Strategy{    void execute(){        StrategyA...        StrategyB...    }}

模板方法

在父类当中定以算法骨架,将一些步骤延迟到子类当中实现

实现一些操作时,整体步骤很固定,但是呢。就是其中一小部分容易变,这时候可以使用模板方法模式,将容易变的部分抽象出来,供子类实现

classDiagram    class AbstractClass {        +process1()        +process2()        +templateMethod()    }    class ConcreteClass1 {        +process1()        +process2()    }    class ConcreteClass2 {        +process1()        +process2()    }    AbstractClass <|-- ConcreteClass1    AbstractClass <|-- ConcreteClass2    Client --> AbstractClass
abstract class BaseAlgorithm{    void process(){        process1();        process2();    }    abstract void process1();    abstract void process2();}class ConcreteAlgorithm extends BaseAlgorithm{    // 实现方法}// 使用BaseAlgorithm algorithm = new ConcreteAlgorithm();algorithm.process();

访问者

表示一个作用于对象结构中的各元素的操作

识图避免新功能的引入造成接口的修改

classDiagram    class Visitor {        +visist(ElementA)        +visist(ElementB)    }    class VisitorA {        +visist(ElementA)        +visist(ElementB)    }    class VisitorB {        +visist(ElementA)        +visist(ElementB)    }    VisitorA --|> Visitor    VisitorB --|> Visitor    class Element {        +accept(Visitor)    }    class ElementA {        +accept(Visitor)        +operationA()    }    class ElementB {        +accept(Visitor)        +operationB()    }    ElementA --|> Element    ElementB --|> Element    Client ..> Visitor    Client ..> ObjectStructure    ObjectStructure o-- Element
class Visitor {    visit(ElementA);    visit(ElementB);}class VisitorA extends Visitor {    visit(ElementA){...}    visit(ElementB){...}}class VisitorB extends Visitor {    visit(ElementA){...}    visit(ElementB){...}}class Element{    accept(Vistor);}class ElementA extends Element {    accept(Vistor) {        Visitor.visit(this)    }}...

访问者设计模式提出的根本原因是为了在不修改类的前提下 增加类的行为。

在这里,当需要增加新的行为时,就将新增一个具体的Vistor 然后传递给相应的Element。

该模式适合在对象数据结构稳定,不会变更且经常需要定义新操作的场景下使用。一般在编译器领域比较常用