职责链模式——加薪

职责链模式(Chain of Responsibility):使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这个对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。

结构图

image-20210913150115072

代码实现

Handler

1
2
3
4
5
6
7
8
9
public abstract class Handler {
    Handler successor;

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

    abstract void request(int type);
}

ConcreteHandler

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
public class Handler1 extends Handler{

    @Override
    void request(int type) {
        if (type < 10){
            System.out.println("handler 1 print");
        }
        else if (super.successor != null) {
            super.successor.request(type);
        }
        else {
            System.out.println("can't solve");
        }
    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
public class Handler2 extends Handler{
    @Override
    void request(int type) {
        if (type < 20){
            System.out.println("handler 2 print");
        }
        else if (super.successor != null) {
            super.successor.request(type);
        }
        else {
            System.out.println("can't solve");
        }
    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
public class Handler3 extends Handler{
    @Override
    void request(int type) {
        if (type < 30){
            System.out.println("handler 2 print");
        }
        else if (super.successor != null) {
            super.successor.request(type);
        }
        else {
            System.out.println("can't solve");
        }
    }
}

Client

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
public static void main(String[] args) {
    Handler1 handler1 = new Handler1();
    Handler2 handler2 = new Handler2();
    Handler3 handler3 = new Handler3();
    handler1.setSuccessor(handler2);
    handler2.setSuccessor(handler3);
    int[] test = {5,15,25,35};
    for (int i : test) {
        handler1.request(i);
    }
}
1
2
3
4
handler 1 print
handler 2 print
handler 2 print
can't solve

职责链的好处

当客户提交一个请求时,请求是沿链传递直至有一个ConcreteHandler对象负责处理它。

这就使得接收者和发送者都没有对方的明确信息,且链中的对象自己也并不知道链的结构。结果是职责链可简化对象的相互连接,它们仅需保持一个指向其后继者的引用,而不需保持它所有的候选接受者的引用。这大大降低了耦合度。

由于是在客户端来定义链的结构,可以随时地增加或修改处理一个请求的结构。增强了给对象指派职责的灵活性。

命令模式——点菜

命令模式(Command),将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作。

结构图

image-20210913105412344

代码实现

Receiver

1
2
3
4
5
6
7
8
9
public class Receiver {
    void action1(){
        System.out.println("action1");
    }

    void action2(){
        System.out.println("action2");
    }
}

Command

1
2
3
4
5
6
7
8
9
public abstract class Commend {
    Receiver receiver;

    public Commend(Receiver receiver) {
        this.receiver = receiver;
    }

    abstract void execute();
}

ConcreteCommand

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
public class ConcreteCommend1 extends Commend{
    public ConcreteCommend1(Receiver receiver) {
        super(receiver);
    }

    @Override
    void execute() {
        receiver.action1();
    }
}

Invoker

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
public class Invoker {

    Commend commend;

    public Invoker(Commend commend) {
        this.commend = commend;
    }

    void execute(){
        commend.execute();
    }
}

Client

1
2
3
4
5
6
public static void main(String[] args) {
    Receiver receiver = new Receiver();
    ConcreteCommend1 concreteCommend1 = new ConcreteCommend1(receiver);
    Invoker invoker = new Invoker(concreteCommend1);
    invoker.execute();
}

命令模式作用

第一,它能较容易地设计一个命令队列;

第二,在需要的情况下,可以较容易地将命令记入日志;

第三,允许接收请求的一方决定是否要否决请求。

第四,可以容易地实现对请求的撤销和重做;

第五,由于加进新的具体命令类不影响其他的类,因此增加新的具体命令类很容易。

其实还有最关键的优点就是命令模式把请求一个操作的对象与知道怎么执行一个操作的对象分割开。

命令模式支持撤销/恢复操作功能,但还不清楚是否需要这个功能时,要不要实现命令模式?

应该是不要实现。敏捷开发原则告诉我们,不要为代码添加基于猜测的、实际不需要的功能。如果不清楚一个系统是否需要命令模式,一般就不要着急去实现它,事实上,在需要的时候通过重构实现这个模式并不困难,只有在真正需要如撤销/恢复操作等功能时,把原来的代码重构为命令模式才有意义。

迭代器模式——检票

迭代器模式(Iterator),提供一种方法顺序访问一个聚合对象中各个元素,而又不暴露该对象的内部表示。

image-20210913215321421

跳过…

中介者模式——联合国

中介者模式(Mediator),用一个中介对象来封装一系列的对象交互。中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。

结构图

image-20210913155157051

代码实现

Mediator

1
2
3
4
public abstract class Mediator {

    abstract void send(String msg, Colleague colleague);
}

Colleague

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
public abstract class Colleague {

    Mediator mediator;

    public Colleague(Mediator mediator) {
        this.mediator = mediator;
    }

    abstract void sendMessage(String msg);

    abstract void print(String msg);

}

ConcreteColleague

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
public class Colleague1 extends Colleague{


    public Colleague1(Mediator mediator) {
        super(mediator);
    }

    @Override
    void sendMessage(String msg) {
        super.mediator.send(msg, this);
    }

    @Override
    void print(String msg) {
        System.out.println("colleague 1 receive: " + msg);
    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
public class Colleague2 extends Colleague{

    public Colleague2(Mediator mediator) {
        super(mediator);
    }

    @Override
    void sendMessage(String msg) {
        super.mediator.send(msg, this);
    }

    @Override
    void print(String msg) {
        System.out.println("colleague 2 receive: " + msg);
    }
}

ConcreteMediator

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class ConcreteMediator extends Mediator{

    Colleague1 colleague1;
    Colleague2 colleague2;

    public void setColleague1(Colleague1 colleague1) {
        this.colleague1 = colleague1;
    }

    public void setColleague2(Colleague2 colleague2) {
        this.colleague2 = colleague2;
    }

    @Override
    void send(String msg, Colleague colleague) {
        if (colleague instanceof Colleague1){
            colleague2.print(msg);
        }
        else
            colleague1.print(msg);
    }
}
1
2
colleague 2 receive: hello man
colleague 1 receive: see you man

中介者模式优缺点

中介者模式很容易在系统中应用,也很容易在系统中误用。当系统出现了“多对多”交互复杂的对象群时,不要急于使用中介者模式,而要先反思系统在设计上是不是合理。

中介者模式的优点首先是Mediator的出现减少了各个Colleague的耦合,使得可以独立地改变和复用各个Colleague类和Mediator,比如任何国家的改变不会影响到其他国家,而只是与安理会发生变化。其次,由于把对象如何协作进行了抽象,将中介作为一个独立的概念并将其封装在一个对象中,这样关注的对象就从对象各自本身的行为转移到它们之间的交互上来,也就是站在一个更宏观的角度去看待系统。

由于ConcreteMediator 控制了集中化,于是就把交互复杂性变为了中介者的复杂性,这就使得中介者会变得比任何一个 ConcreteColleague都复杂。

中介者模式一般应用于一组对象以定义良好但是复杂的方式进行通信的场合,比如窗体Form对象(计算器),以及想定制一个分布在多个类中的行为,而又不想生成太多的子类的场合。

备忘录模式——存进度

备忘录(Memento):在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态。

结构图

image-20210910143733697

代码实现

发起人(Originator)类

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
public class Originator {

    // 需要保存的属性,可能有多个

    int hp = 100;
    int attack = 10;
    int defence = 10;

    public Memento save(){
        // 创建备忘录,将当前需要保存的信息导入并实例化出一个Memento对象
        return new Memento(attack,defence);
    }

    public void grow(){
        hp += 10;
        attack += 10;
        defence += 10;
        System.out.println("grow...");
    }

    public void reload(Memento memento){
        // 恢复备忘录,将 Memento导入并将相关数据恢复
        attack = memento.attack;
        defence = memento.defence;
        System.out.println("reload...");
    }

    @Override
    public String toString() {
        return "Originator{" +
            "hp=" + hp +
            ", attack=" + attack +
            ", defence=" + defence +
            '}';
    }
    
    // 需要保存的属性,可能有多个
    // getter and setter...
    
}

备忘录(Memento)类

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
public class Memento {

    int attack;
    int defence;

    public Memento(int attack, int defence) {
        this.attack = attack;
        this.defence = defence;
    }
}

管理者(Caretaker)类

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
public class CareTaker {

    Memento memento;

    public Memento getMemento() {
        return memento;
    }

    public void setMemento(Memento memento) {
        this.memento = memento;
    }
}

客户端程序

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
public static void main(String[] args) {
    Originator originator = new Originator();
    CareTaker careTaker = new CareTaker();
    System.out.println(originator);
    // 保存状态时,由于有了很好的封装,可以隐藏 Originator的实现细节
    careTaker.setMemento(originator.save());
    originator.grow();
    System.out.println(originator);
    originator.reload(careTaker.getMemento());
    System.out.println(originator);
}

应用

Memento模式比较适用于功能比较复杂的,但需要维护或记录属性历史的类,或者需要保存的属性只是众多属性中的一小部分时,Originator可以根据保存的Memento信息还原到前一状态。

**如果在某个系统中使用命令模式时,需要实现命令的撤销功能,那么命令模式可以使用备忘录模式来存储可撤销操作的状态。**有时一些对象的内部信息必须保存在对象以外的地方,但是必须要由对象自己读取,这时,使用备忘录可以把复杂的对象内部信息对其他的对象屏蔽起来,从而可以恰当地保持封装的边界。

当角色的状态改变的时候,有可能这个状态无效,这时候就可以使用暂时存储起来的备忘录将状态复原。

观察者模式——BOSS来了

观察者模式又叫做发布-订阅(Publish/Subscribe)模式。观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己。

示例结构图

image-20210820103345649

结构图

image-20210820103326508

代码实现

Observer

1
2
3
public interface Observer {
    void update();
}

Notifier

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class Notifier {
    List<Observer> observers = new ArrayList<>();
    String state;

    public void attach(Observer observer){
        observers.add(observer);
    }

    public void detach(Observer observer){
        observers.remove(observer);
    }

    public void notify2Observer(){
        for (Observer observer : observers) {
            observer.update();
        }
    }

    public String getState() {
        return state;
    }

    public void setState(String state) {
        this.state = state;
    }
}

ConcreteObserver

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
public class ConcreteObserver implements Observer{

    private String name;
    private Notifier notifier;

    public ConcreteObserver(String name, Notifier notifier) {
        this.name = name;
        this.notifier = notifier;
    }

    @Override
    public void update() {
        System.out.println(name + ":" + notifier.getState());
    }
}

ConcreteNotifier

1
2
3
public class ConcreteNotifier extends Notifier {

}

main

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
public class ObserverTest {
    public static void main(String[] args) {
        Notifier notifier = new ConcreteNotifier();
        Observer observer1 = new ConcreteObserver("a", notifier);
        Observer observer2 = new ConcreteObserver("b", notifier);
        notifier.attach(observer1);
        notifier.attach(observer2);
        notifier.setState("老板来啦");
        notifier.notify2Observer();
        notifier.detach(observer2);
        notifier.notify2Observer();
    }
}

观察者模式的特点

**将一个系统分割成一系列相互协作的类有一个很不好的副作用,那就是需要维护相关对象间的一致性。我们不希望为了维持一致性而使各类紧密耦合,这样会给维护、扩展和重用都带来不便。**而观察者模式的关键对象是主题Subject和观察者Observer,一个Subject可以有任意数目的依赖它的Observer,一旦Subject 的状态发生了改变,所有的Observer都可以得到通知。Subject 发出通知时并不需要知道谁是它的观察者,也就是说,具体观察者是谁,它根本不需要知道。而任何一个具体观察者不知道也不需要知道其他观察者的存在。

何时使用观察者

当一个对象的改变需要同时改变其他对象的时候,而且它不知道具体有多少对象有待改变时,应该考虑使用观察者模式。

当一个抽象模型有两个方面,其中一方面依赖于另一方面,这时用观察者模式可以将这两者封装在独立的对象中使它们各自独立地改变和复用。

观察者模式所做的工作其实就是在解除耦合。让耦合的双方都依赖于抽象,而不是依赖于具体。从而使得各自的变化都不会影响另一边的变化。

观察者模式的不足

尽管已经用了依赖倒转原则,但是”抽象通知者“还是依赖”抽象观察者“’,也就是说,万一没有了抽象观察者这样的接口,通知的功能就完不成了。另外就是每个具体观察者,它不一定是”更新“的方法要调用。举个例子:希望的是”工具箱“是隐藏,”自动窗口“是打开,这这些都不是同名的方法。

如果通知者和观察者之间根本就互相不知道,由客户端来决定通知谁,那就好了。

事件委托

委托是一种引用方法的类型。一旦为委托分配了方法,委托将与该方法具有完全相同的行为。委托方法的使用可以像其他任何方法一样,具有参数和返回值。委托可以看作是对函数的抽象,是函数的”类“,委托的实例将代表一个具体的函数。

一个委托可以搭载多个方法,所有方法被依次唤起。更重要的是,它可以使得委托对象所搭载的方法并不需要属于同一个类。

但委托也是有前提的,那就是委托对象所搭载的所有方法必须具有相同的原形和形式,也就是拥有相同的参数列表和返回值类型。

==“委托”在C#中是一个语言级特性,而在Java语言中没有直接的对应,但是java利用反射即可实现委托!==

https://blog.csdn.net/Seriousplus/article/details/80462722

委派和继承都是为了代码复用,只是方式不同。

  • 委托可以被看作是对象级别的重用机制,而继承是类级别的重用机制。
  • 此外,如果子类只需要复用父类中的一小部分方法,可以不需 要使用继承,而是通过委派机制来实现

状态模式——多分支判断

状态模式(State),当一个对象的内在状态改变时允许改变其行为,这个对象看起来像是改变了其类。

**状态模式主要解决的是当控制一个对象状态转换的条件表达式过于复杂时的情况。把状态的判断逻辑转移到表示不同状态的一系列类当中,可以把复杂的判断逻辑简化。**当然,如果这个状态判断很简单,那就没必要用”状态模式“了。

结构图

image-20210821101018906

状态模式好处与用处

将特定的状态相关的行为都放入一个对象中,由于所有与状态相关的代码都存在于某个ConcreteState中,所以通过定义新的子类可以很容易地增加新的状态和转换。

这样做的目的就是为了消除庞大的条件分支语句,大的分支判断会使得它们难以修改和扩展,任何改动和变化都是致命的。状态模式通过把各种状态转移逻辑分布到State 的子类之间,来减少相互间的依赖,此时就容易维护和扩展了。

何时使用状态模式

**当一个对象的行为取决于它的状态,并且它必须在运行时刻根据状态改变它的行为时,就可以考虑使用状态模式了。**另外如果业务需求某项业务有多个状态,通常都是一些枚举常量,状态的变化都是依靠大量的多分支判断语句来实现,此时应该考虑将每一种业务状态定义为一个State的子类。这样这些对象就可以不依赖于其他对象而独立变化了,某一天客户需要更改需求,增加或减少业务状态或改变状态流程,都是不困难的事。

代码实现

image-20210821102730182

State

1
2
3
public interface State {
    void handle(Context context);
}

FirstState

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
public class FirstState implements State{
    @Override
    public void handle(Context context) {
        if (context.getFlag() < 12)
            System.out.println("first state");
        else {
            context.setState(new SecondState());
            context.request();
        }
    }
}

SecondState

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
public class SecondState implements State{
    @Override
    public void handle(Context context) {
        if (context.getFlag() < 17)
            System.out.println("second state");
        else {
            context.setState(new ThirdState());
            context.request();
        }
    }
}

ThirdState

1
2
3
4
5
6
public class ThirdState implements State{
    @Override
    public void handle(Context context) {
        System.out.println("third state");
    }
}

Context

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
public class Context {

    private int flag;
    private State state;

    public Context(State state) {
        this.state = state;
    }

    public void request(){
        state.handle(this);
    }
    
    // ... get and set
}

main

1
2
3
4
5
6
7
8
9
public static void main(String[] args) {
    Context context = new Context(new FirstState());
    context.setFlag(11);
    context.request();
    context.setFlag(13);
    context.request();
    context.setFlag(18);
    context.request();
}

策略模式——商场促销

对于商场促销的例子,简单工厂模式虽然也能解决这个问题,但这个模式只是解决对象的创建问题,而且由于工厂本身包括了所有的收费方式,商场是可能经常性地更改打折额度和返利额度,每次维护或扩展收费方式都要改动这个工厂,以致代码需重新编译部署,所以用它不是最好的办法。面对算法的时常变动,有更好的办法。

策略模式(Strategy):它定义了算法家族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化,不会影响到使用算法的客户。

结构图

image-20210818151006167

image-20210818151117837

策略模式实现

Strategy接口,各种算法都要实现这个接口

1
2
3
public interface Strategy {
    double getResult(double money);
}

算法实现类

1
2
3
4
5
6
public class CashNormal implements Strategy{
    @Override
    public double getResult(double money) {
        return money;
    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
public class CashRebate implements Strategy{

    private double rebate = 1;

    public CashRebate(){

    }

    public CashRebate(double rebate){
        this.rebate = rebate;
    }

    @Override
    public double getResult(double money) {
        return money * rebate;
    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
public class CashReturn implements Strategy{

    private double moneyCondition;
    private double moneyReturn;

    public CashReturn(double moneyCondition, double moneyReturn){
        this.moneyCondition = moneyCondition;
        this.moneyReturn = moneyReturn;
    }

    @Override
    public double getResult(double money) {
        return money >= moneyCondition ? money - Math.floor(money / moneyCondition) * moneyReturn : money;
    }
}

Context

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
public class CashContext {

    private Strategy strategy;

    public CashContext(Strategy strategy){
        this.strategy = strategy;
    }

    public double getResult(double money){
        return strategy.getResult(money);
    }

}

主函数

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class StrategyTest {

    public static void main(String[] args) {
        System.out.println(StrategyTest.test("normal", 200));
        System.out.println(StrategyTest.test("rebate", 200));
        System.out.println(StrategyTest.test("return", 200));
    }

    public static double test(String op,double money){
        CashContext cashContext = null;
        switch (op){
            case "normal":{
                cashContext = new CashContext(new CashNormal());
                break;
            }
            case "rebate":{
                cashContext = new CashContext(new CashRebate(0.8));
                break;
            }
            case "return":{
                cashContext = new CashContext(new CashReturn(200, 20));
                break;
            }
        }

        return cashContext.getResult(money);
    }

}

这种方式是在客户端去判断用哪一个蒜贩,能否把这个判断的过程从客户端程序转移走? –> 简单工厂

策略与简单工厂结合

只需修改Context类即可

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class CashContextPro {

    private Strategy strategy;

    public CashContextPro(String op){
        switch (op){
            case "normal":{
                strategy = new CashNormal();
                break;
            }
            case "rebate":{
                strategy = new CashRebate(0.8);
                break;
            }
            case "return":{
                strategy = new CashReturn(200, 20);
                break;
            }
        }
    }

    public double getResult(double money){
        return strategy.getResult(money);
    }

}

主函数

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
public class StrategyTest {

    public static void main(String[] args) {
        System.out.println(StrategyTest.test2("normal", 200));
        System.out.println(StrategyTest.test2("rebate", 200));
        System.out.println(StrategyTest.test2("return", 200));
    }

    public static double test2(String op,double money){
        return new CashContextPro(op).getResult(money);
    }

}

策略模式解析

策略模式是一种定义一系列算法的方法,从概念上来看,所有这些算法完成的都是相同的工作,只是实现不同,它可以以相同的方式调用所有的算法,减少了各种算法类与使用算法类之间的耦合。

策略模式的Strategy类层次为Context定义了一系列的可供重用的算法或行为。继承有助于析取出这些算法中的公共功能。对于打折、返利或者其他的算法,其实都是对实际商品收费的一种计算方式,通过继承,可以得到它们的公共功能。

策略模式就是用来封装算法的,但在实践中,可以用它来封装几乎任何类型的规则,只要在分析过程中听到需要在不同时间应用不同的业务规则,就可以考虑使用策略模式处理这种变化的可能性。

但是这种方式还有一些不足:如果增加一种算法就必须更改switch的代码

更好的方法 :==反射==

注:在抽象工厂模式章节有对反射的讲解

模板方法模式——答题模板

模板方法模式,定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。

结构图

image-20210819155822961

image-20210819155903600

模板方法模式是通过把不变行为搬移到超类,去除子类中的重复代码来体现它的优势。

模板方法模式就是提供了一个很好的代码复用平台。

当不变的和可变的行为在方法的子类实现中混合在一起的时候,不变的行为就会在子类中重复出现。通过模板方法模式把这些行为搬移到单一的地方,这样就帮助子类摆脱重复的不变行为的纠缠。

模板方法模式代码实现

Templete class

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
public abstract class Template {

    public void test1(){
        System.out.println("test1: " + answer1());
    }

    public void test2(){
        System.out.println("test2: " + answer2());
    }

    public void test3(){
        System.out.println("test3: " + answer3());
    }

    public abstract String answer1();

    public abstract String answer2();

    public abstract String answer3();
}

Concrete class

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
public class Person1 extends Template{
    @Override
    public String answer1() {
        return "a";
    }

    @Override
    public String answer2() {
        return "b";
    }

    @Override
    public String answer3() {
        return "c";
    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
public class Person2 extends Template{
    @Override
    public String answer1() {
        return "c";
    }

    @Override
    public String answer2() {
        return "b";
    }

    @Override
    public String answer3() {
        return "a";
    }
}

test class

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
public class TemplateMethodTest {
    public static void main(String[] args) {
        Template person1 = new Person1();
        Template person2 = new Person2();
        person1.test1();
        person1.test2();
        person1.test3();
        person2.test1();
        person2.test2();
        person2.test3();
    }
}

访问者模式——男女对比

访问者模式(Visitor),表示一个作用于某对象结构中的各元素的操作。它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。

访问者模式适用于数据结构相对稳定的系统。

它把数据结构和作用于结构上的操作之间的耦合解脱开,使得操作集合可以相对自由地演化。

**访问者模式的目的是要把处理从数据结构分离出来。很多系统可以按照算法和数据结构分开,如果这样的系统有比较稳定的数据结构,又有易于变化的算法的话,使用访问者模式就是比较合适的,因为访问者模式使得算法操作的增加变得容易。**反之,如果这样的系统的数据结构对象易于变化,经常要有新的数据对象增加进来,就不适合使用访问者模式。

访问者模式的优点就是增加新的操作很容易,因为增加新的操作就意味着增加一个新的访问者。访问者模式将有关的行为集中到一个访问者对象中。

访问者的缺点其实也就是使增加新的数据结构变得困难了。

结构图

image-20210914095957448

代码实现

Element

1
2
3
public interface Element {
    void accept(Visitor visitor);
}

Visitor

1
2
3
4
public interface Visitor {
    void visitorElementA(ConcreteElementA element);
    void visitorElementB(ConcreteElementB element);
}

ConcreteElement

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
public class ConcreteElementA implements Element{
    String name;

    public ConcreteElementA(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    @Override
    public void accept(Visitor visitor) {
        visitor.visitorElementA(this);
    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
public class ConcreteElementB implements Element{
    String name;

    public ConcreteElementB(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    @Override
    public void accept(Visitor visitor) {
        visitor.visitorElementB(this);
    }
}

ConcreteVisitor

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
public class ConcreteVisitorA implements Visitor{

    String name;

    public ConcreteVisitorA(String name) {
        this.name = name;
    }

    @Override
    public void visitorElementA(ConcreteElementA element) {
        System.out.println(this.name + " visit " + element.getName());
    }

    @Override
    public void visitorElementB(ConcreteElementB element) {
        System.out.println(this.name + " visit " + element.getName());
    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
public class ConcreteVisitorB implements Visitor{

    String name;

    public ConcreteVisitorB(String name) {
        this.name = name;
    }

    @Override
    public void visitorElementA(ConcreteElementA element) {
        System.out.println(this.name + " visit " + element.getName());
    }

    @Override
    public void visitorElementB(ConcreteElementB element) {
        System.out.println(this.name + " visit " + element.getName());
    }
}

ObjectStructure

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
public class ObjectStructure {
    List<Element> elements = new ArrayList<>();

    public void attach(Element element){
        elements.add(element);
    }
    public void detach(Element element){
        elements.remove(element);
    }
    public void display(Visitor visitor){
        for (Element element : elements) {
            element.accept(visitor);
        }
    }
}

Client

1
2
3
4
5
6
7
8
public static void main(String[] args) {
    ObjectStructure objectStructure = new ObjectStructure();
    ConcreteElementA elementA = new ConcreteElementA("element a");
    ConcreteElementB elementB = new ConcreteElementB("element b");
    objectStructure.attach(elementA);
    objectStructure.attach(elementB);
    objectStructure.display(new ConcreteVisitorA("visitor a"));
}