【设计模式】设计模式之行为型模式(二)(备忘录、观察者、状态、策略、模板方法、访问者)

【设计模式】设计模式之行为型模式(二)(备忘录、观察者、状态、策略、模板方法、访问者)

  • 1、设计模式
  • 2、行为型设计模式之备忘录、观察者、状态、策略、模板方法、访问者模式
      • 2.1 备忘录模式
      • 2.2 观察者模式
      • 2.3 状态模式
      • 2.4 策略模式
      • 2.5 模板方法模式
      • 2.6 访问者模式

1、设计模式

  • 之前聊了设计模式的概念,分类、五种创建型模式、七种结构型模式和五种行为型模式,二十三种设计模式,还剩六种行为型模式,今天搞完它。
  • 创建型详见:【设计模式】设计模式之创建型模式(单例、原型、工厂、建造者)
  • 结构型详见:【设计模式】设计模式之结构型模式(适配器、桥接、组合、装饰、外观、享元、代理)
  • 行为型(一)详见:【设计模式】设计模式之行为型模式(一)(责任链、命令、解释器、迭代器、中介)

2、行为型设计模式之备忘录、观察者、状态、策略、模板方法、访问者模式

2.1 备忘录模式

2.1.1 概念

  • 备忘录模式(Memento Pattern)又叫做快照模式(Snapshot Pattern)或Token模式,指在不破坏封闭的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态。

  • 该模式用于保存对象当前状态,并且在之后可以再次恢复到此状态。备忘录模式实现的方式需要保证被保存的对象状态不能被对象从外部访问,

  • 目的是为了保护被保存的这些对象状态的完整性以及内部实现不向外暴露

  • 备忘录模式中的角色:

1.Originator(发起人):负责创建一个备忘录Memento,用以记录当前时刻自身的内部状态,并可使用备忘录恢复内部状态。Originator可以根据需要决定Memento存储自己的哪些内部状态。

2.Memento(备忘录):负责存储Originator对象的内部状态,并可以防止Originator以外的其他对象访问备忘录。备忘录有两个接口:Caretaker只能看到备忘录的窄接口,他只能将备忘录传递给其他对象。Originator却可看到备忘录的宽接口,允许它访问返回到先前状态所需要的所有数据。

3.Caretaker(管理者):负责备忘录Memento,不能对Memento的内容进行访问或者操作

2.1.2 备忘录模式代码实现

/**
 * 备忘录
 *
 * @author wangjie
 * @version V1.0
 * @date 2020/4/10
 */
public class Memento {
     
    private String state;

    public Memento(String state){
     
        this.state = state;
    }

    public String getState() {
     
        return state;
    }

    public void setState(String state) {
     
        this.state = state;
    }
}
/**
 * 管理者
 *
 * @author wangjie
 * @version V1.0
 * @date 2020/4/10
 */
public class Caretaker {
     

    private Memento mMemento;

    public Memento getMemento(){
     
        return mMemento;
    }

    public void setMemengto(Memento memento){
     
        this.mMemento = memento;
    }
}
/**
 * 发起者
 *
 * @author wangjie
 * @version V1.0
 * @date 2020/4/10
 */
public class Originator {
     

    private String state;

    /**
     * 创建一个新的备忘录对象
     * */
    public Memento createMemento(){
     
        return new Memento(state);
    }

    /**
     * 将发起者的状态恢复到备忘录的状态
     * */
    public void restore(Memento memento){
     
        this.state = memento.getState();
    }

}

2.1.3 备忘录模式总结

  • 备忘录模式实在不破坏封装的条件下,通过备忘录对象(Memento)存储另外一个对象内部状态的快照,在将来合适的时候把这个对象还原到存储起来的状态。

  • 优点:给用户提供了一种可以恢复状态的机制,可以是用户能够方便的回到某个历史的状态
    实现了信息的封装,是的用户不需要关心状态的保存细节

  • 缺点:消耗资源,如果累的成员变量过多,势必会占用比较大的资源,而且每一次保存都会消耗一定的内存。

2.2 观察者模式

2.2.1 概念

  • 观察者模式(Observer Pattern):定义对象间的一种一对多依赖关系,使得每当一个对象状态发生改变时,其相关依赖对象皆得到通知并被自动更新。观察者模式又叫做发布-订阅(Publish/Subscribe)模式、模型-视图(Model/View)模式、源-监听器(Source/Listener)模式或从属者(Dependents)模式。
  • 在观察者设计模式中,一般有四个角色:

抽象主题角色(Subject)
具体主题角色(ConcreteSubject)
抽象观察者角色(Observer)
具体观察者角色(ConcreteObserver)

2.2.2 观察者模式代码实现

//抽象观察者角色
public interface People {
     
    void update(News news);
}
//具体观察者角色A
public class PeopleA implements People {
     
    @Override
    public void update(News news) {
     
        System.out.println("这个新闻真好看");
    }
}
//具体观察者角色B
public class PeopleB implements People {
     
    @Override
    public void update(News news) {
     
        System.out.println("这个新闻真无语");
    }
}
//具体观察者角色C
public class PeopleC implements People {
     
    @Override
    public void update(News news) {
     
        System.out.println("这个新闻真逗");
    }
}
//存放事件的信息,非必须的类
public class News {
     
    private String title;
    private String content;

    public String getTitle() {
     
        return title;
    }

    public void setTitle(String title) {
     
        this.title = title;
    }

    public String getContent() {
     
        return content;
    }

    public void setContent(String content) {
     
        this.content = content;
    }
}
//抽象主题角色
public interface Subject {
     
    List<People> peopleList = new ArrayList<>();

    default void add(People people) {
     
        peopleList.add(people);
    }

    default void remove(People people) {
     
        peopleList.remove(people);
    }

    void update();
}
//具体主题角色
public class NewsSubject implements Subject{
     
    public void update() {
     
        for (People people : peopleList) {
     
            News news = new News();
            news.setContent("今日在大街上,有人躲在草丛中袭击路人,还大喊“德玛西亚万岁”");
            news.setTitle("德玛西亚出现了");
            people.update(news);
        }
    }
}
//测试
public class Main {
     
    public static void main(String[] args) {
     
        Subject subject = new NewsSubject();
        subject.add(new PeopleA());
        subject.add(new PeopleB());
        subject.add(new PeopleC());
        subject.update();
    }
}

2.2.3 观察者模式总结

  • 观察者模式定义对象间的一种一对多依赖关系,使得每当一个对象状态发生改变时,其相关依赖对象皆得到通知并被自动更新。观察者模式又叫做发布-订阅模式、模型-视图模式、源-监听器模式或从属者模式。观察者模式是一种对象行为型模式。
  • 观察者模式包含四个角色:1、目标又称为主题,它是指被观察的对象;2、具体目标是目标类的子类,通常它包含有经常发生改变的数据,当它的状态发生改变时,向它的各个观察者发出通知;3、观察者将对观察目标的改变做出反应;4、在具体观察者中维护一个指向具体目标对象的引用,它存储具体观察者的有关状态,这些状态需要和具体目标的状态保持一致。
  • 观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个目标对象,当这个目标对象的状态发生变化时,会通知所有观察者对象,使它们能够自动更新。
  • 观察者模式的主要优点在于可以实现表示层和数据逻辑层的分离,并在观察目标和观察者之间建立一个抽象的耦合,支持广播通信;其主要缺点在于如果一个观察目标对象有很多直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间,而且如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。
  • 观察者模式适用情况包括:1、一个抽象模型有两个方面,其中一个方面依赖于另一个方面;2、一个对象的改变将导致其他一个或多个对象也发生改变,而不知道具体有多少对象将发生改变;3、一个对象必须通知其他对象,而并不知道这些对象是谁;需要在系统中创建一个触发链。
  • 在JDK的java.util包中,提供了Observable类以及Observer接口,它们构成了Java语言对观察者模式的支持。

2.3 状态模式

2.3.1 概念

  • 状态模式(State Pattern) :允许一个对象在其内部状态改变时改变它的行为,对象看起来似乎修改了它的类。其别名为状态对象(Objects for States),状态模式是一种对象行为型模式。
  • 态模式包含如下角色:

Context: 环境类
State: 抽象状态类
ConcreteState: 具体状态类

2.3.2 状态模式代码实现

//抽象状态类
public abstract class FlowState {
     

    protected Context context;

    public void setContext(Context context) {
     
        this.context = context;
    }

    abstract void audit();

    abstract void pass();

    abstract void noPass();
}

//具体状态类Audit 
public class Audit extends FlowState{
     
    @Override
    void audit() {
     
        System.out.println("审核中...");
    }

    @Override
    void pass() {
     
        super.context.setState(Context.PASS);
        super.context.getState().pass();
    }

    @Override
    void noPass() {
     
        super.context.setState(Context.NO_PASS);
        super.context.getState().noPass();
    }
}
//具体状态类Pass 
public class Pass extends FlowState{
     

    @Override
    void audit() {
     
        super.context.setState(Context.AUDIT);
        super.context.getState().audit();
    }

    @Override
    void pass() {
     
        System.out.println("通过啦...");
    }

    @Override
    void noPass() {
     
        super.context.setState(Context.NO_PASS);
        super.context.getState().noPass();
    }
}
//具体状态类NoPass 
public class NoPass extends FlowState{
     
    @Override
    void audit() {
     
        super.context.setState(Context.AUDIT);
        super.context.getState().audit();
    }

    @Override
    void pass() {
     
        super.context.setState(Context.PASS);
        super.context.getState().pass();
    }

    @Override
    void noPass() {
     
        System.out.println("不通过...");
    }
}
//环境类
public class Context {
     

    protected FlowState state;
    protected static final Audit AUDIT = new Audit();
    protected static final NoPass NO_PASS = new NoPass();
    protected static final Pass PASS = new Pass();

    public Context(FlowState state) {
     
        setState(state);
    }

    public FlowState getState() {
     
        return state;
    }

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

    void audit(){
     
        this.getState().audit();
    }

    void pass(){
     
        this.getState().pass();
    }

    void noPass(){
     
        this.getState().noPass();
    }
}

//测试
public class Apply {
     
    public static void main(String[] args){
     
        try {
     
            //申请
            System.out.println("提交申请...");
            Context context  = new Context(new Audit());
            context.audit();
            Thread.sleep(100);
           
            context.pass();
        } catch (InterruptedException e) {
     
            e.printStackTrace();
        }
    }
}

2.3.3 状态模式总结

  • 状态模式允许一个对象在其内部状态改变时改变它的行为,对象看起来似乎修改了它的类。其别名为状态对象,状态模式是一种对象行为型模式。
  • 状态模式包含三个角色:1、环境类又称为上下文类,它是拥有状态的对象,在环境类中维护一个抽象状态类State的实例,这个实例定义当前状态,在具体实现时,它是一个State子类的对象,可以定义初始状态;2、抽象状态类用于定义一个接口以封装与环境类的一个特定状态相关的行为;3、具体状态类是抽象状态类的子类,每一个子类实现一个与环境类的一个状态相关的行为,每一个具体状态类对应环境的一个具体状态,不同的具体状态类其行为有所不同。
  • 状态模式描述了对象状态的变化以及对象如何在每一种状态下表现出不同的行为。
  • 状态模式的主要优点在于封装了转换规则,并枚举可能的状态,它将所有与某个状态有关的行为放到一个类中,并且可以方便地增加新的状态,只需要改变对象状态即可改变对象的行为,还可以让多个环境对象共享一个状态对象,从而减少系统中对象的个数;其缺点在于使用状态模式会增加系统类和对象的个数,且状态模式的结构与实现都较为复杂,如果使用不当将导致程序结构和代码的混乱,对于可以切换状态的状态模式不满足“开闭原则”的要求。
  • 状态模式适用情况包括:1、对象的行为依赖于它的状态(属性)并且可以根据它的状态改变而改变它的相关行为;2、代码中包含大量与对象状态有关的条件语句,这些条件语句的出现,会导致代码的可维护性和灵活性变差,不能方便地增加和删除状态,使客户类与类库之间的耦合增强。

2.4 策略模式

2.4.1 概念

  • 策略模式(Strategy Pattern):定义一系列算法,将每一个算法封装起来,并让它们可以相互替换。策略模式让算法独立于使用它的客户而变化,也称为政策模式(Policy)。
  • 策略模式包含如下角色:

Context: 环境类
Strategy: 抽象策略类
ConcreteStrategy: 具体策略类

2.4.2 策略模式代码实现


//抽象策略类
public interface Strategy {
     
    void doSomething();
}
//具体策略类
public class BlockingWaitStrategy implements Strategy{
     
    @Override
    public void doSomething() {
     
        System.out.println(this.getClass().getSimpleName() + "使用锁和条件变量。CPU资源的占用少,延迟大");
    }
}
public class BusySpinWaitStrategy  implements Strategy{
     
    @Override
    public void doSomething() {
     
        System.out.println(this.getClass().getSimpleName() + "自旋等待,类似Linux Kernel使用的自旋锁。低延迟但同时对CPU资源的占用也多");
    }
}
public class PhasedBackoffWaitStrategy implements Strategy {
     
    @Override
    public void doSomething() {
     
        System.out.println(this.getClass().getSimpleName() + "多种策略的综合,CPU资源的占用少,延迟大");
    }
}
public class SleepingWaitStrategy implements Strategy {
     
    @Override
    public void doSomething() {
     
        System.out.println(this.getClass().getSimpleName() + "在多次循环尝试不成功后,选择让出CPU,等待下次调度,多次调度后仍不成功,尝试前睡眠一个纳秒级别的时间再尝试。这种策略平衡了延迟和CPU资源占用,但延迟不均匀");
    }
}
public class YieldingWaitStrategy implements Strategy {
     
    @Override
    public void doSomething() {
     
        System.out.println(this.getClass().getSimpleName() + " 在多次循环尝试不成功后,选择让出CPU,等待下次调。平衡了延迟和CPU资源占用,但延迟也比较均匀");
    }
}

public class Context{
     
    private Strategy strategy;

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

    public void getStrategy(){
     
        strategy.doSomething();
    }
}
//测试
public class Test {
     
    public static void main(String[] args) {
     
        Context context= new Context(new BlockingWaitStrategy());
        context.getStrategy();
    }
}

2.4.3 策略模式总结

  • 在策略模式中定义了一系列算法,将每一个算法封装起来,并让它们可以相互替换。策略模式让算法独立于使用它的客户而变化,也称为政策模式。策略模式是一种对象行为型模式。
  • 策略模式包含三个角色:1、环境类在解决某个问题时可以采用多种策略,在环境类中维护一个对抽象策略类的引用实例;2、抽象策略类为所支持的算法声明了抽象方法,是所有策略类的父类;3、具体策略类实现了在抽象策略类中定义的算法。
  • 策略模式是对算法的封装,它把算法的责任和算法本身分割开,委派给不同的对象管理。策略模式通常把一个系列的算法封装到一系列的策略类里面,作为一个抽象策略类的子类。
  • 策略模式主要优点在于对“开闭原则”的完美支持,在不修改原有系统的基础上可以更换算法或者增加新的算法,它很好地管理算法族,提高了代码的复用性,是一种替换继承,避免多重条件转移语句的实现方式;其缺点在于客户端必须知道所有的策略类,并理解其区别,同时在一定程度上增加了系统中类的个数,可能会存在很多策略类。
  • 策略模式适用情况包括:1、在一个系统里面有许多类,它们之间的区别仅在于它们的行为,使用策略模式可以动态地让一个对象在许多行为中选择一种行为;2、一个系统需要动态地在几种算法中选择一种;3、避免使用难以维护的多重条件选择语句;4、希望在具体策略类中封装算法和与相关的数据结构。

2.5 模板方法模式

2.5.1 概念

  • 定义一个操作中的算法框架,而将一些步骤延迟到子类中. 使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤,通俗的讲,就是将子类相同的方法, 都放到其抽象父类中。
  • 模板方法模式中的角色

抽象类(AbstractClass):实现了模板方法,定义了算法的骨架。
具体类(ConcreteClass):实现抽象类中的抽象方法,已完成完整的算法。

2.5.2 模板方法模式代码实现

/**
 * 抽象基类,为其他子类提供一个算法框架 提神饮料
 */
public abstract class RefreshBeverage {
     

    /*
     * 制备饮料的模板方法 封装了所有子类所遵循的算法框架
     */
    public final void prepareBeverageTemplate() {
     
        // 步骤一 将水煮沸
        boilWater();
        // 步骤二 泡制饮料
        brew();
        // 步骤三 将饮料倒入杯中
        pourInCup();
        if (isCustomerWantsCondiments()) {
     
            // 步骤四 加入调味料
            addCondiments();
        }
    }

    /*
     * Hook 钩子函数,提供一个空的或者默认的实现 子类重写该方法,可以自行决定是否挂钩以及如何挂钩
     */
    protected boolean isCustomerWantsCondiments() {
     
        return true;
    }

    // 因为将水煮沸和把饮料倒入杯中对所有子类是共同的行为,所以没必要向子类过多开放,所以方法定义为private,这样我们在进行子类编码时可以减少复杂度。
    // 这样不需要关注细枝末节,我们只需要关注我们特定业务的实现,这就是模板方法模式的好处。可以封装变与不变,将不变的固化在高层,隐藏其细节。
    private void boilWater() {
     
        System.out.println("将水煮沸");
    }

    private void pourInCup() {
     
        System.out.println("将饮料倒入杯中");
    }

    /*
     * 泡制饮料brew()和加入调料品addCondiments()这两个方法我们不知道它们在算法框架中的具体实现,因此定义为抽象方法,
     * 我们用protected进行修饰, 在子类中可见便于进行重写。
     */
    protected abstract void brew();

    protected abstract void addCondiments();

}

/**
 * 提供制备咖啡的具体实现子类。 具体子类实现延迟步骤,满足特定的业务需求。
 */
public class Coffee extends RefreshBeverage {
     

    protected void brew() {
     
        System.out.println("步骤二 用沸水冲泡咖啡");
    }

    protected void addCondiments() {
     
        System.out.println("步骤四 加入糖和牛奶");
    }

}

public class Tea extends RefreshBeverage {
     

    protected void brew() {
     
        System.out.println("步骤二 用80度热水浸泡茶叶5分钟");
    }

    protected void addCondiments() {
     
        System.out.println("步骤四 加入柠檬");
    }

    protected boolean isCustomerWantsCondiments() {
     
        return false;
    }

}

public class Test {
     

    public static void main(String[] args) {
     
        System.out.println("制备咖啡中······");
        RefreshBeverage b1 = new Coffee();
        b1.prepareBeverageTemplate();
        System.out.println("咖啡好了········");

        // 制备茶的测试代码
        System.out.println("\n*********************************");
        System.out.println("制备茶水中······");
        RefreshBeverage b2 = new Tea();
        b2.prepareBeverageTemplate();
        System.out.println("茶水好了······");
    }

}

2.5.3 模板方法模式总结

  • 优点:1、模板方法模式通过把不变的行为搬移到超类,去除了子类中的重复代码。
    子类实现算法的某些细节,有助于算法的扩展;2、通过一个父类调用子类实现的操作,通过子类扩展增加新的行为,符合“开放-封闭原则”。

  • 缺点:每个不同的实现都需要定义一个子类,这会导致类的个数的增加,设计更加抽象。

  • 适用场景:1、在某些类的算法中,用了相同的方法,造成代码的重复;2、控制子类扩展,子类必须遵守算法规则。

2.6 访问者模式

2.6.1 概念

  • 访问者模式:表示一个作用于其对象结构中的各元素的操作,它使你可以在不改变各元素类的前提下定义作用于这些元素的新操作。
  • 访问者模式是一种将数据操作与数据结构分离的设计模式,它可以算是 23 中设计模式中最复杂的一个,但它的使用频率并不是很高,大多数情况下,你并不需要使用访问者模式,但是当你一旦需要使用它时,那你就是需要使用它了。
  • 访问者模式中的角色:

1、Visitor 抽象访问者角色:为该对象结构中具体元素角色声明一个访问操作接口。该操作接口的名字和参数标识了发送访问请求给具体访问者的具体元素角色,这样访问者就可以通过该元素角色的特定接口直接访问它。

2、ConcreteVisitor具体访问者角色:实现Visitor声明的接口。

3、Element抽象受访元素:定义一个接受访问操作(accept()),它以一个访问者(Visitor)作为参数。

4、ConcreteElement 具体受访元素:实现了抽象元素(Element)所定义的接受操作接口。

5、ObjectStructure 结构对象角色:这是使用访问者模式必备的角色。它具备以下特性:能枚举它的元素;可以提供一个高层接口以允许访问者访问它的元素;如有需要,可以设计成一个复合对象或者一个聚集(如一个列表或无序集合)。

2.6.2 访问者模式代码实现


//我们都知道财务都是有账本的,这个账本就可以作为一个对象结构,而它其中的元素有两种,收入和支出,这满足我
//们访问者模式的要求,即元素的个数是稳定的,因为账本中的元素只能是收入和支出。
//而查看账本的人可能有这样几种,比如老板,会计事务所的注会,财务主管,等等。而这些人在看账本的时候显然目
//的和行为是不同的。


//单个单子的接口(相当于Element)
public interface Bill {
     
 
    void accept(AccountBookViewer viewer);
 
}


//消费的单子
public class ConsumeBill implements Bill{
     
 
    private double amount;
 
    private String item;
 
    public ConsumeBill(double amount, String item) {
     
        super();
        this.amount = amount;
        this.item = item;
    }
 
    public void accept(AccountBookViewer viewer) {
     
        viewer.view(this);
    }
 
    public double getAmount() {
     
        return amount;
    }
 
    public String getItem() {
     
        return item;
    }
 
}
//收入单子
public class IncomeBill implements Bill{
     
 
    private double amount;
 
    private String item;
 
    public IncomeBill(double amount, String item) {
     
        super();
        this.amount = amount;
        this.item = item;
    }
 
    public void accept(AccountBookViewer viewer) {
     
        viewer.view(this);
    }
 
    public double getAmount() {
     
        return amount;
    }
 
    public String getItem() {
     
        return item;
    }
 
}


//账单查看者接口(相当于Visitor)
public interface AccountBookViewer {
     
 
    //查看消费的单子
    void view(ConsumeBill bill);
 
    //查看收入的单子
    void view(IncomeBill bill);
 
}


//老板类,查看账本的类之一
public class Boss implements AccountBookViewer{
     
 
    private double totalIncome;
 
    private double totalConsume;
 
    //老板只关注一共花了多少钱以及一共收入多少钱,其余并不关心
    public void view(ConsumeBill bill) {
     
        totalConsume += bill.getAmount();
    }
 
    public void view(IncomeBill bill) {
     
        totalIncome += bill.getAmount();
    }
 
    public double getTotalIncome() {
     
        System.out.println("老板查看一共收入多少,数目是:" + totalIncome);
        return totalIncome;
    }
 
    public double getTotalConsume() {
     
        System.out.println("老板查看一共花费多少,数目是:" + totalConsume);
        return totalConsume;
    }
 
}
//注册会计师类,查看账本的类之一
public class CPA implements AccountBookViewer{
     
 
    //注会在看账本时,如果是支出,则如果支出是工资,则需要看应该交的税交了没
    public void view(ConsumeBill bill) {
     
        if (bill.getItem().equals("工资")) {
     
            System.out.println("注会查看工资是否交个人所得税。");
        }
    }
    //如果是收入,则所有的收入都要交税
    public void view(IncomeBill bill) {
     
        System.out.println("注会查看收入交税了没。");
    }
 
}
//账本类(相当于ObjectStruture)
public class AccountBook {
     
    //单子列表
    private List<Bill> billList = new ArrayList<Bill>();
    //添加单子
    public void addBill(Bill bill){
     
        billList.add(bill);
    }
    //供账本的查看者查看账本
    public void show(AccountBookViewer viewer){
     
        for (Bill bill : billList) {
     
            bill.accept(viewer);
        }
    }
}

public class Client {
     
 
    public static void main(String[] args) {
     
        AccountBook accountBook = new AccountBook();
        //添加两条收入
        accountBook.addBill(new IncomeBill(10000, "卖商品"));
        accountBook.addBill(new IncomeBill(12000, "卖广告位"));
        //添加两条支出
        accountBook.addBill(new ConsumeBill(1000, "工资"));
        accountBook.addBill(new ConsumeBill(2000, "材料费"));
 
        AccountBookViewer boss = new Boss();
        AccountBookViewer cpa = new CPA();
 
        //两个访问者分别访问账本
        accountBook.show(cpa);
        accountBook.show(boss);
 
        ((Boss) boss).getTotalConsume();
        ((Boss) boss).getTotalIncome();
    }
}

2.6.3 访问者模式总结:

  • 优点:1、使得数据结构和作用于结构上的操作解耦,使得操作集合可以独立变化;2、添加新的操作或者说访问者会非常容易;3、将对各个元素的一组操作集中在一个访问者类当中;4、使得类层次结构不改变的情况下,可以针对各个层次做出不同的操作,而不影响类层次结构的完整性;5、可以跨越类层次结构,访问不同层次的元素类,做出相应的操作。

  • 缺点:1、增加新的元素会非常困难;2、实现起来比较复杂,会增加系统的复杂性;3、破坏封装,如果将访问行为放在各个元素中,则可以不暴露元素的内部结构和状态,但使用访问者模式的时候,为了让访问者能获取到所关心的信息,元素类不得不暴露出一些内部的状态和结构。

  • 适用性:1、数据结构稳定,作用于数据结构的操作经常变化的时候;2、当一个数据结构中,一些元素类需要负责与其不相关的操作的时候,为了将这些操作分离出去,以减少这些元素类的职责时,可以使用访问者模式;3、有时在对数据结构上的元素进行操作的时候,需要区分具体的类型,这时使用访问者模式可以针对不同的类型,在访问者类中定义不同的操作,从而去除掉类型判断。

你可能感兴趣的:(java,设计模式,设计模式,java)