23种设计模式之行为模式篇

三、行为模式

这类模式主要关注对象之间的通信,尤其是它们之间进行通信的方式和时机。

包括:
  • 策略模式(Strategy)
  • 模板方法模式(Template Method)
  • 观察者模式(Observer)
  • 迭代器模式(Iterator)
  • 责任链模式(Chain of Responsibility)
  • 命令模式(Command)
  • 备忘录模式(Memento)
  • 状态模式(State)
  • 访问者模式(Visitor)
  • 中介者模式(Mediator)
  • 解释器模式(Interpreter)
1、观察者模式(Observer)
1.1.代码实现
//操作步骤一:观察者接口
public interface Observer {
    void update(String message);
}

//操作步骤二:主题接口
public interface Subject {

    void registerObserver(Observer observer);

    void removeObserver(Observer observer);

    void notifyObservers();

}

//操作步骤三:具体观察者类
public class ConcreteObserver implements Observer {

    //定义一个name字段
    private String name;
    
    //观察者类的有参构造
    public ConcreteObserver(String name) {
        this.name = name;
    }
    
    /**
     * 重写观察者类接口的update方法,自定义的操作
     * */
    @Override
    public void update(String message) {
        System.out.println(name + " received message: " + message);
    }
}


//操作步骤四:具体主题类
public class ConcreteSubject implements Subject{

    //定义一个集合
    private List<Observer> observers = new ArrayList<>();

    //定义一个消息字段
    private String message;

    /**
     * 注册观察者
     * */
    @Override
    public void registerObserver(Observer observer) {
        observers.add(observer);
    }

    /**
     * 删除观察者
     * */
    @Override
    public void removeObserver(Observer observer) {
        observers.remove(observer);
    }


    /**
     * 通知观察者
     * */
    @Override
    public void notifyObservers() {
        observers.stream().forEach(observer->observer.update(message));
    }

    //添加消息的方法
    public void setMessage(String message) {
        this.message = message;
        notifyObservers();      //调用观察者通知方法
    }
}

//代码测试
public class ObserverTest {

    //1.定义了两个接口Observer和Subject,分别表示观察者和主题
    //2.实现了一个具体的ConcreteSubject类和一个具体的ConcreteObserver类,它们分别实现了这两个接口
    public static void main(String[] args) {
        //1.创建主题对象
        ConcreteSubject subject = new ConcreteSubject();//具体主题类

        //2.创建观察者对象
        ConcreteObserver observer1 = new ConcreteObserver("Observer 1");
        ConcreteObserver observer2 = new ConcreteObserver("Observer 2");

        //3.注册观察者到主题中
        subject.registerObserver(observer1);
        subject.registerObserver(observer2);

        //4.更新主题状态并通知观察者
        subject.setMessage("Hello, World!");

        // 移除观察者并再次更新主题状态
        subject.removeObserver(observer1);
        subject.setMessage("Goodbye, World!");
    }
}
1.2.概念总结

​ 观察者模式是一种行为型设计模式,它定义了对象之间的一对多依赖关系,使得当一个对象改变状态时,所有依赖于它的对象都会得到通知并自动更新。

1.2.1.观察者模式主要包括以下角色:
  • 主题(Subject):也称为被观察者,它是观察者所依赖的对象,当其状态发生变化时需要通知所有观察者。
  • 观察者(Observer):也称为观察体,它依赖于主题对象,并在主题状态变化时接收通知。
1.2.2.观察者模式的优点:
  • **解耦观察者和主题:**观察者模式通过定义抽象的观察者和主题来解耦具体的实现,使得系统更加灵活和可扩展。
  • **动态新增观察者:**可以在运行时动态地添加或删除观察者,而不需要修改主题的代码。
  • **广播通信:**支持简单的广播通信机制,新的观察者可以简单地添加到系统中以接收通知。
1.2.3.观察者模式适用于以下场景:
  • 当一个对象的状态改变需要通知其他对象时。
  • 当需要实现事件处理系统时,如用户界面中的事件监听。
  • 当需要在不直接引用的情况下,让多个对象能够同时监听某个对象的变化。

在Java中,可以通过使用java.util.Observer接口和java.util.Observable类来实现观察者模式。不过,从Java 9开始,这两个类被标记为过时,推荐使用其他方式实现,如使用更现代的响应式编程库。

2、模版方法模式(Template Method)
2.1.代码实现
//操作步骤一:抽象类,定义算法骨架
public abstract class AbstractClass {
    /**
     * 1.定义了一个抽象类AbstractClass,它包含了一个模板方法templateMethod(),该方法定义了算法的执行顺序
     * */

    // 模板方法,定义算法的执行顺序
    public final void templateMethod() {
        step1();
        step2();
        step3();
    }

    // 具体步骤1
    protected abstract void step1();

    // 具体步骤2
    protected abstract void step2();

    // 具体步骤3
    protected abstract void step3();
}

//操作步骤二:具体子类1,实现具体步骤
public class ConcreteClass1 extends AbstractClass {
    @Override
    protected void step1() {
        System.out.println("ConcreteClass1: Step 1");
    }

    @Override
    protected void step2() {
        System.out.println("ConcreteClass1: Step 2");
    }

    @Override
    protected void step3() {
        System.out.println("ConcreteClass1: Step 3");
    }
}

//操作步骤二:具体子类1,实现具体步骤
public class ConcreteClass2 extends AbstractClass {
    @Override
    protected void step1() {
        System.out.println("ConcreteClass2: Step 1");
    }

    @Override
    protected void step2() {
        System.out.println("ConcreteClass2: Step 2");
    }

    @Override
    protected void step3() {
        System.out.println("ConcreteClass2: Step 3");
    }
}

//代码测试
public class TemplateTest {
    //1.定义了一个抽象类AbstractClass,它包含了一个模板方法templateMethod(),该方法定义了算法的执行顺序
    //2.创建了两个具体的子类ConcreteClass1和ConcreteClass2,它们分别实现了抽象类中的抽象方法step1()、step2()和step3()
    //结果对比:创建了两个对象obj1和obj2,并调用它们的templateMethod()方法来执行算法。
    //        由于每个子类都实现了相同的算法骨架,但具体步骤的实现不同,因此输出结果也不同。

    public static void main(String[] args) {
        AbstractClass obj1 = new ConcreteClass1();
        obj1.templateMethod(); // 输出:ConcreteClass1: Step 1, ConcreteClass1: Step 2, ConcreteClass1: Step 3

        AbstractClass obj2 = new ConcreteClass2();
        obj2.templateMethod(); // 输出:ConcreteClass2: Step 1, ConcreteClass2: Step 2, ConcreteClass2: Step 3
    }

2.2.概念总结

模板方法模式是一种行为设计模式,它定义了一个操作中的算法骨架,将一些步骤的实现延迟到子类中。这种模式的目的是通过在超类中定义一个算法的框架,允许子类在不改变算法结构的情况下重定义算法的某些步骤。

  1. **代码复用:**模板方法模式通过在抽象类中定义算法的骨架,使得所有子类可以共享相同的算法结构。
  2. **扩展性:**子类可以通过重写某些步骤来改变或扩展算法的行为,而不需要修改算法的整体结构。
  3. **控制反转:**模板方法模式允许子类决定某些步骤的执行方式,从而实现控制反转,提高系统的灵活性。
2.2.2.模板方法模式适用于以下场景:
  • **复杂流程处理:**当一个复杂流程需要分解成多个步骤,并且某些步骤的具体实现可能需要变化时,可以使用模板方法模式。
  • **代码框架统一:**如果多个对象有相似的处理流程,但具体实现细节不同,可以通过模板方法模式提供一个统一的代码框架。
3、策略模式(Strategy)
3.1.代码实现
//操作步骤一:定义策略接口
public interface Strategy {
    void execute();
}

//操作步骤二:实现策略接口的具体类
public class ConcreteStrategyA implements Strategy {
    @Override
    public void execute() {
        System.out.println("执行策略A");
    }
}

//操作步骤二:实现策略接口的具体类
public class ConcreteStrategyB implements Strategy{
    @Override
    public void execute() {
        System.out.println("执行策略B");
    }
}

//操作步骤三:上下文类,用于选择和执行具体策略
public class Context {

    private Strategy strategy;

    /**
     * 上下文构造
     * */
    public Context(Strategy strategy) {
        this.strategy = strategy;
    }

    /**
     * 策略切换方法
     * */
    public void setStrategy(Strategy strategy) {
        this.strategy = strategy;
    }

    /**
     * 执行策略
     * */
    public void executeStrategy() {
        strategy.execute();
    }
}

//代码测试
public class StrategyTest {
    public static void main(String[] args) {
        //1.创建具体策略对象
        Strategy strategyA = new ConcreteStrategyA();
        Strategy strategyB = new ConcreteStrategyB();

        //2.创建上下文对象,并设置具体策略
        Context context = new Context(strategyA);

        //3.执行策略A
        context.executeStrategy();

        //4.切换到策略B
        context.setStrategy(strategyB);

        //5.执行策略B
        context.executeStrategy();
    }

}
3.2.概念总结

策略模式是一种行为型设计模式,它允许在运行时选择算法或行为的具体实现。策略模式的核心思想是将一系列算法或行为封装到独立的策略类中,使得这些算法或行为可以相互替换。

3.2.1.使用场景:
  1. 多个类只区别在表现上,即一个类的行为或其算法可以在运行时更改、转换的。
  2. 当想使用一个类的实例而无法确定使用哪种类的时候可以使用。
  3. 需要使用if…else语句来根据条件选择不同行为的场合,可以通过策略模式来替代多个条件选择语句。
3.2.2.策略模式的优点:
  1. 符合开闭原则:新的策略可以很容易地加入到系统中,不需要修改已有的代码。
  2. 避免使用多重条件转移语句:如if-else语句,使得系统更加灵活和易于扩展。
  3. 提供了管理相关的算法族的办法:使得算法的变化独立于使用算法的客户。
3.2.3.策略模式的缺点:
  1. 增加了对象的数量:每个策略都需要一个单独的类来实现,这可能导致类的数量增加。
  2. 客户端必须知道所有的策略:并理解它们的区别,以便选择合适的策略。

在实际使用中,策略模式可以用来实现不同的支付方式选择、促销策略的选择等。通过将具体的算法或行为封装在不同的策略类中,可以在运行时根据不同的情况选择不同的策略类来执行相应的逻辑。

4、迭代器模式(Observer)
4.1.代码实现
//操作步骤一:定义一个集合接口
public interface Aggregate {
    Iterator<Object> createIterator();
}
//操作步骤二:具体的迭代器类
public class ConcreteIterator implements Iterator<Object> {
    private Object[] items;
    private int position;

    public ConcreteIterator(Object[] items) {
        this.items = items;
        this.position = 0;
    }

    @Override
    public boolean hasNext() {
        return position < items.length;
    }

    @Override
    public Object next() {
        if (!hasNext()) {
            throw new NoSuchElementException();
        }
        return items[position++];
    }
}

//操作步骤三:具体的集合类
public class ConcreteAggregate implements Aggregate {
    private Object[] items;

    public ConcreteAggregate(Object[] items) {
        this.items = items;
    }

    @Override
    public Iterator<Object> createIterator() {
        return new ConcreteIterator(items);
    }
}

//测试代码
public class IteratorTest {
     /**
     * 1.首先定义了一个集合接口Aggregate,它包含一个创建迭代器的方法createIterator()。
     * 2.然后,我们实现了一个具体的集合类ConcreteAggregate,它包含了一个元素数组,
     			并实现了createIterator()方法来返回一个具体的迭代器对象。
     * 3.接着,我们定义了迭代器接口Iterator,它包含两个方法:hasNext()用于判断是否还有下一个元素,
     			next()用于获取当前元素并将指针后移。
     * 4.最后,我们实现了一个具体的迭代器类ConcreteIterator,它持有一个元素数组和当前位置指针,
     			并实现了hasNext()和next()方法。
    * */
    public static void main(String[] args) {
        Object[] items = {"item1", "item2", "item3"};
        Aggregate aggregate = new ConcreteAggregate(items);
        Iterator<Object> iterator = aggregate.createIterator();
        while (iterator.hasNext()) {
            System.out.println(iterator.next());
        }
    }
}

4.2.概念总结

迭代器模式是一种行为型设计模式,它提供了一种方法来顺序访问集合对象中的各个元素,同时不暴露该对象的内部表示

4.2.1迭代器模式的关键角色包括:
  • 迭代器(Iterator):这个接口负责定义按顺序遍历元素的接口,通常包含hasNextnext两个方法,前者用于判断是否还有下一个元素,后者用于获取当前元素并将指针后移。
  • 具体的迭代器(ConcreteIterator):实现迭代器接口的类,它包含遍历集合所必要的信息。
  • 集合(Aggregate):一个接口或抽象类,定义了创建迭代器的方法。
  • 具体的集合(ConcreteAggregate):实现集合接口的类,它包含了集合的元素,并实现了创建迭代器的方法。
4.2.2.优点:
  • 分离了集合对象的遍历行为:抽象出了迭代器负责集合对象的遍历,使得外部代码可以透明地访问集合内部的数据。
  • 提供了统一的接口:为遍历不同的集合结构提供了统一的接口,使得客户端代码不必关心集合的具体实现。

迭代器模式的缺点主要是增加了系统的复杂性,因为每次添加新的集合类时,都需要增加对应的迭代器类。

在实际应用中,迭代器模式广泛应用于各种集合类的遍历操作中,如Java中的ArrayListLinkedList等都提供了迭代器来进行元素的遍历。通过使用迭代器模式,我们可以避免直接暴露集合的内部结构,同时也为客户端代码提供了一个简洁的方式来访问集合中的元素。

5.责任链模式(Chain of Responsibility)
5.1.代码实现
//操作步骤一:定义处理器接口
public interface Handler {

    void setNext(Handler next);

    void handleRequest(String request);
}

//操作步骤二:具体处理器类1
public class ConcreteHandler1 implements Handler{

    private Handler next;

    @Override
    public void setNext(Handler next) {
        this.next = next;
    }

    @Override
    public void handleRequest(String request) {
        if (request.equals("request1")) {
            System.out.println("ConcreteHandler1处理了请求");
        } else if (next != null) {
            next.handleRequest(request);
        } else {
            System.out.println("没有处理器可以处理该请求");
        }
    }
}

//操作步骤二:具体处理器类2
public class ConcreteHandler2 implements Handler{

    private Handler next;

    @Override
    public void setNext(Handler next) {
        this.next = next;
    }

    @Override
    public void handleRequest(String request) {
        if (request.equals("request2")) {
            System.out.println("ConcreteHandler2处理了请求");
        } else if (next != null) {
            next.handleRequest(request);
        } else {
            System.out.println("没有处理器可以处理该请求");
        }
    }
}

//测试代码
public class ResponsibilityTest {
    /**
     * 1.首先定义了一个处理器接口Handler,它包含两个方法:setNext()用于设置下一个处理器,
     		handleRequest()用于处理请求。
     * 2.然后,我们实现了两个具体的处理器类ConcreteHandler1和ConcreteHandler2,
     		 它们分别实现了Handler接口,并实现了自己的处理逻辑。
     * 在handleRequest()方法中,如果当前处理器可以处理请求,则直接处理;否则,
     			将请求传递给链上的下一个处理器。
     * 如果没有处理器可以处理请求,则输出相应的提示信息
     * */
    public static void main(String[] args) {
        Handler handler1 = new ConcreteHandler1();
        Handler handler2 = new ConcreteHandler2();
        handler1.setNext(handler2);

        handler1.handleRequest("request1"); // 输出:ConcreteHandler1处理了请求
        handler1.handleRequest("request2"); // 输出:ConcreteHandler2处理了请求
        handler1.handleRequest("request3"); // 输出:没有处理器可以处理该请求
    }
}
5.2.概念总结

责任链模式是一种处理请求的设计模式,通过将多个对象连接成一条链,并沿着这条链传递请求,直到某个对象处理它为止

这种模式的主要目的是避免请求发送者和接收者之间的直接耦合,从而提高系统的灵活性和可扩展性。责任链模式通常用于实现一些需要多级审批或处理的业务流程,例如表单提交、权限验证等场景。

5.2.1.以下是责任链模式的一些关键点:
  • 降低耦合:责任链模式允许你将请求的发送者和接收者解耦,发送者不需要知道哪个接收者将处理请求。
  • 增强灵活性:可以动态地重新组织链或者在运行时修改处理请求的方式。
  • 简化逻辑:客户端只需将请求发送到责任链上,无需关心具体的处理细节。
  • 分摊责任:每个处理器负责自己的处理逻辑,并且可以选择是否将请求传递给链上的下一个处理器。
5.2.2.在实现责任链模式时,通常会有以下几种角色:
  • Handler(处理器):定义处理请求的接口,包含处理请求的方法和一个指向下一个处理器的引用。
  • ConcreteHandler(具体处理器):实现Handler接口的具体类,可以处理它负责的请求,也可以将请求传递给链上的下一个处理器。
  • Client(客户端):创建具体的处理器对象,并设置它们的链。

责任链模式的一个典型应用场景是企业的财务审批流程,不同的审批级别可以作为不同的处理器节点,请求在链上依次传递,由相应的审批人进行处理。如果某一级别的审批人批准了请求,则不再向下传递;如果否决,则传递给链上的下一个审批人。

此外,责任链模式还可以用于实现Web框架中的拦截器和过滤器,用于处理HTTP请求的不同阶段,如身份验证、日志记录等。

总的来说,责任链模式是一种非常有用的设计模式,它通过将请求的处理分散到一系列对象中,使得系统更加灵活和易于扩展。

6.命令模式(Command)
6.1.代码实现
//操作步骤一:命令接口
public interface Command {
    void execute();
}

//操作步骤二:具体命令类1
public class ConcreteCommand1 implements Command {

    /**
     * 执行命令
     * */
    @Override
    public void execute() {
        System.out.println("执行命令1");
    }
}

//操作步骤二:具体命令类2
public class ConcreteCommand2 implements Command {
    /**
     * 执行命令
     * */
    @Override
    public void execute() {
        System.out.println("执行命令2");
    }

}

//操作步骤三:请求者类
public class Invoker {
    private Command command;

    /**
     * 设置执行命令的对象
     * */
    public void setCommand(Command command) {
        this.command = command;
    }

    /**
     * 执行命令
     * */
    public void executeCommand() {
        if (command != null) {
            command.execute();
        } else {
            System.out.println("没有设置命令");
        }
    }
}

//测试代码
public class CommandTest {

    /**
     * 1.首先定义了一个命令接口Command,它包含一个execute()方法用于执行命令。
     * 2.然后,我们实现了两个具体的命令类ConcreteCommand1和ConcreteCommand2,它们分别实现了Command接口,并在execute()方法中输出相应的信息。
     * 3.接着,我们定义了一个请求者类Invoker,它持有一个命令对象,并提供了setCommand()方法用于设置命令对象,以及executeCommand()方法用于执行命令。
     * 4.最后,我们在客户端代码中使用Scanner类读取用户输入的命令编号,并根据编号创建相应的命令对象,并将其设置到请求者对象中,
     *   然后调用executeCommand()方法执行命令。如果用户输入的不是1或2,则退出程序。
     * */
    public static void main(String[] args) {
        Invoker invoker = new Invoker();
        Scanner scanner = new Scanner(System.in);

        while (true) {
            System.out.println("请输入命令(1或2),输入其他数字退出程序:");
            int commandNumber = scanner.nextInt();
            switch (commandNumber) {
                case 1:
                    invoker.setCommand(new ConcreteCommand1());     //执行命令1
                    break;
                case 2:
                    invoker.setCommand(new ConcreteCommand2());//执行命令2
                    break;
                default:
                    System.out.println("退出程序");
                    scanner.close();
                    return;
            }
            invoker.executeCommand();
        }
    }
}

6.2.概念总结

命令模式是一种行为型设计模式,它通过将请求封装成对象,实现了请求的发送者和接收者之间的解耦

命令模式的核心在于将一个请求封装成一个对象,这样可以使用不同的请求对客户端进行参数化。这种模式提供了极高的灵活性,因为它允许将命令的执行延迟到后面的时间点,或者在不同的上下文中执行。

6.2.1.命令模式通常涉及以下三个关键角色:
  • 命令(Command)角色:这是接口或抽象类,定义了执行操作的方法。
  • 具体命令(ConcreteCommand)角色:实现命令接口的类,它将一个接收者对象与一个动作绑定。调用接收者相应的操作以实现 Execute。
  • 请求者(Invoker)角色:负责调用命令对象执行请求,它可以持有一个或多个命令对象,并在需要时触发它们的执行。
  • 接收者(Receiver)角色:负责具体实施和执行一个请求。任何类都可能作为一个接收者。
6.2.2.命令模式的优点:
  • 高内聚低耦合:由于命令模式将发起请求的对象和执行请求的对象解耦,因此增加了系统的灵活性和可扩展性。
  • 可扩展性强:可以很容易地添加新的命令类来扩展系统的功能。
  • 支持撤销操作:命令模式可以提供命令的撤销和恢复功能,这对于需要事务性操作的系统来说非常有用。
  • 可以实现复杂的控制逻辑:如命令队列、批处理命令等。

在实际应用中,命令模式被广泛用于实现事务性操作(如数据库操作),在软件界面设计中用于实现用户操作的撤销和重做,以及在工作流管理系统中用于管理和调度任务。此外,命令模式也被应用于日志记录、事务处理等领域,因为它可以将操作和操作的执行者分离,使得操作的记录和回放成为可能。

7.备忘录模式(Memento)
7.1.代码实现
//操作步骤一:备忘录类
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<>();

    public void add(Memento memento) {
        mementoList.add(memento);
    }

    public Memento get(int index) {
        return mementoList.get(index);
    }
}

//测试代码
public class MementoTest {

    /**
     *首先定义了三个类:发起人类Originator、备忘录类Memento和管理者类Caretaker。
     * 发起人类负责保存和恢复其内部状态,备忘录类用于存储发起人的状态信息,而管理者类则负责管理备忘录对象。
     * 在客户端代码中,我们创建了一个发起人对象和一个管理者对象,并依次设置发起人的状态,并将每个状态保存为一个备忘录对象。
     * 然后,我们通过调用发起人的getStateFromMemento()方法来恢复到之前保存的状态。最后,我们输出恢复到的状态信息。
     * */
    public static void main(String[] args) {
        //1.发起人
        Originator originator = new Originator();
        //2.管理者
        Caretaker caretaker = new Caretaker();

        originator.setState("状态1");
        caretaker.add(originator.saveStateToMemento());

        originator.setState("状态2");
        caretaker.add(originator.saveStateToMemento());

        originator.setState("状态3");
        caretaker.add(originator.saveStateToMemento());

        originator.getStateFromMemento(caretaker.get(0));
        System.out.println("恢复到状态1:" + originator.getState());
        originator.getStateFromMemento(caretaker.get(1));
        System.out.println("恢复到状态2:" + originator.getState());
    }
}

7.2.概念总结

备忘录模式是一种行为型设计模式,用于保存对象的某个状态,以便在适当的时候恢复对象

备忘录模式的核心在于它能够在不破坏对象封装性的前提下,捕获并外部化存储一个对象的内部状态,使得该对象可以在将来的某个时刻恢复到之前的状态。这种模式通常用在需要记录历史状态以便后续可以回滚到这些状态的场景中。例如,在文本编辑器中实现撤销操作时,每个编辑操作都被保存为一个备忘录,用户执行撤销操作时,文本编辑器就会恢复到最近一次保存的状态。

7.2.1以下是一些关键点:
  • Originator(发起人):这是需要保存状态的对象。它负责创建一个备忘录对象来记录自己的当前内部状态,并且可以使用备忘录来恢复到某个之前的状态。
  • Memento(备忘录):这是一个值对象,它存储了发起人的某个状态。备忘录应该具备防止发起人以外的其他对象对其状态进行修改的能力,通常通过私有化其实现细节并提供有限的访问权限来实现这一点。
7.2.2.备忘录模式的优点:
  • 提供了一种可以安全保持和恢复对象状态的机制:这对于许多应用程序来说是非常有用的,尤其是那些需要支持撤销操作的应用程序。
  • 实现了信息的封装:因为备忘录仅对外提供有限的访问方式,所以可以保护其存储的状态信息不被外部错误地修改。
7.2.3.备忘录模式也有其缺点:
  • 如果需要保存的状态信息量大或者非常复杂,那么使用备忘录模式可能会消耗大量的系统资源
  • 频繁地创建和恢复备忘录可能会影响系统性能

总的来说,备忘录模式是实现对象状态管理的有效手段,特别适用于那些需要记录、恢复对象状态的应用场景。

8.状态模式(State)
8.1.代码实现
//操作步骤一:状态接口
public interface State {
    void doAction(Context context);
}

//操作步骤二:上下文类
public class Context {
    private State state;

    public Context() {
        state = null;
    }

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

    public State getState() {
        return state;
    }
}

//操作步骤三:具体状态类1
public class ConcreteState1 implements State {

    public void doAction(Context context) {
        System.out.println("当前状态是状态1");
        context.setState(this);
    }

    public String toString() {
        return "状态1";
    }
}

//操作步骤三:具体状态类2
public class ConcreteState2 implements State {

    public void doAction(Context context) {
        System.out.println("当前状态是状态2");
        context.setState(this);
    }

    public String toString() {
        return "状态2";
    }
}

public class StateTest {
    /**
     * 1.首先定义了一个状态接口State,它包含一个doAction()方法用于执行状态相关的操作。
     * 2.然后,我们分别实现了两个具体的状态类ConcreteState1和ConcreteState2,
     *   它们都实现了State接口,并在doAction()方法中输出当前状态信息,并设置上下文对象的状态为当前状态。
     * 3.接着,我们定义了一个上下文类Context,它持有一个指向当前状态对象的引用,
     			并提供了一些方法来设置和获取状态。
     * 4.最后,在客户端代码中,我们创建了一个上下文对象和两个具体状态对象,
     		并通过调用状态对象的doAction()方法来改变上下文对象的状态,并输出当前状态信息
     * */
    public static void main(String[] args) {
        Context context = new Context();

        State state1 = new ConcreteState1();
        State state2 = new ConcreteState2();

        state1.doAction(context);
        System.out.println("当前状态:" + context.getState().toString());

        state2.doAction(context);
        System.out.println("当前状态:" + context.getState().toString());
    }
}

8.2.概念总结

状态模式(State Pattern)是一种行为设计模式,允许一个对象在其内部状态改变时改变它的行为。这种模式通过把不同状态下的行为封装到不同的类中,来提高系统的灵活性和可维护性

8.2.1以下是状态模式的一些关键特点:
  • 封装性:状态模式将每个状态的行为封装在各自的类中,这样新增状态时不会影响到其他状态的类。
  • 可扩展性:当需要增加新的状态时,只需添加相应的状态类即可,无需修改其他代码。
  • 避免条件语句:使用状态模式可以减少条件语句的使用,因为状态的转换是由上下文(Context)对象和状态对象之间的交互来完成的。
  • 符合开闭原则:状态模式符合面向对象设计的开闭原则,即对扩展开放,对修改封闭。
8.2.2.适用场景:
  • 一个对象的行为取决于它的状态,并且它必须在运行时刻根据状态改变其行为。
  • 一个操作的实现部分散乱于多个条件语句中,可以使用状态模式将这些操作分离出来并封装到对应的状态对象中。
8.2.3.在具体实现上,通常涉及三种角色:
  • Context(上下文):持有一个指向当前状态对象的引用,并将与状态相关的操作委托给当前状态对象来处理。
  • State(状态):定义一个接口,用以声明所有具体状态类共有的方法,同时也可能提供一些默认的实现。
  • Concrete State(具体状态):实现 State 接口的具体类,每一个类对应一个具体的状态,并在其中实现该状态下的行为。

总的来说,状态模式适用于那些需要根据对象状态频繁切换行为的场景,它通过将每个状态的行为封装到独立的类中,使得状态转换更加清晰和易于管理。

9.访问者模式(Visitor)
9.1.代码实现
//操作步骤一:具体元素类A
public class ElementA {
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }

    public String operationA() {
        return "ElementA";
    }
}
//操作步骤一:具体元素类B
public class ElementB {
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }

    public String operationB() {
        return "ElementB";
    }
}

//操作步骤二:访问者接口
public interface Visitor {
    void visit(ElementA element);

    void visit(ElementB element);

}

//操作步骤三:具体访问者类
public class ConcreteVisitor implements Visitor {
    public void visit(ElementA element) {
        System.out.println("Visiting ElementA: " + element.operationA());
    }

    public void visit(ElementB element) {
        System.out.println("Visiting ElementB: " + element.operationB());
    }
}

//测试代码
public class VisitorTest {
    /**
     * 1.首先定义了一个访问者接口Visitor,它包含两个方法visit(),分别用于访问不同的元素。
     * 2.然后,我们分别实现了两个具体元素类ElementA和ElementB,它们都实现了一个accept()方法,
     			用于接受访问者的访问操作。
     * 3.接着,我们定义了一个具体访问者类ConcreteVisitor,它实现了Visitor接口,
     		并在visit()方法中输出了对不同元素的访问操作。
     * 4.最后,在客户端代码中,我们创建了一个元素对象和一个访问者对象,
     			并通过调用元素的accept()方法来让访问者进行访问操作
     * */
    public static void main(String[] args) {

        //1.具体元素类
        ElementA elementA = new ElementA();
        ElementB elementB = new ElementB();

        //2.具体访问者类
        Visitor visitor = new ConcreteVisitor();

        elementA.accept(visitor);
        elementB.accept(visitor);
    }
}
9.2.概念总结

访问者模式是一种设计模式,用于在不改变数据结构的前提下,定义新的操作

访问者模式的核心思想是将数据结构和对数据的操作分离,使得可以在不修改数据结构的情况下添加新的操作。这种模式通常用于实现对一组对象进行操作的场景,它允许新增不同的访问者类来定义新的操作,而不需要改变对象的定义。

9.2.1.在访问者模式中,通常会有以下几种角色:
  • Visitor(访问者):定义了一个访问具体元素(Element)的接口,为每一个具体元素类声明一个访问操作。
  • ConcreteVisitor(具体访问者):实现Visitor接口,定义对每个具体元素的访问操作。
  • Element(元素):定义一个接口,声明接受访问操作的方法。
  • ConcreteElement(具体元素):实现Element接口,通常是一些具有相同属性和方法的对象,它们接受访问者访问。
  • ObjectStructure(对象结构):有元素集合组成,提供了让访问者访问元素的方法。
9.2.2.访问者模式的优点包括:
  • 增加新的操作时,无需修改对象结构:这有助于保持现有系统的稳定性,并符合开闭原则。
  • 将相关的行为集中到一个类中:这有助于组织和维护代码,特别是当操作复杂且数量众多时。
9.2.3.缺点可能包括:
  • 增加新的元素类型时,可能需要修改访问者和元素接口:这违反了开闭原则,但可以通过使用反射等技术来减少这种影响。
  • 可能导致代码量的增加:因为每种新的操作都需要一个新的访问者类来实现。

在实际使用中,访问者模式适用于对象结构稳定,但需要定义新操作的情况。例如,在一个编译器中,访问者模式可以用来处理不同类型的语法树节点,或者在一个GUI系统中,用来处理不同类型的组件事件。在实现时,可以通过创建一个Visitor类和多个Element类来实现访问者模式,从而定义出不同的访问操作。

10.中介者模式(Mediator)
10.1.代码实现
//操作步骤一:中介者接口
public interface Mediator {
    void send(String message, Colleague colleague);
}

//操作步骤二:同事类接口
public interface Colleague {
    void receive(String message);
}

//操作步骤三:具体同事类A
public class ConcreteColleagueA implements Colleague {
    private Mediator mediator;

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

    public void send(String message) {
        mediator.send(message, this);
    }

    public void receive(String message) {
        System.out.println("ConcreteColleagueA received: " + message);
    }

    public void setMediator(ConcreteMediator mediator) {
        this.mediator=mediator;
    }
}

//操作步骤三:具体同事类B
public class ConcreteColleagueB implements Colleague {
    private Mediator mediator;

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

    public void send(String message) {
        mediator.send(message, this);
    }

    public void receive(String message) {
        System.out.println("ConcreteColleagueB received: " + message);
    }

    public void setMediator(ConcreteMediator mediator) {
        this.mediator=mediator;
    }
}

//操作步骤四:具体中介者类
public class ConcreteMediator implements Mediator {
    private ConcreteColleagueA colleagueA;
    private ConcreteColleagueB colleagueB;

    public ConcreteMediator(ConcreteColleagueA colleagueA, ConcreteColleagueB colleagueB) {
        this.colleagueA = colleagueA;
        this.colleagueB = colleagueB;
    }

    public void send(String message, Colleague colleague) {
        if (colleague == colleagueA) {
            colleagueB.receive(message);
        } else {
            colleagueA.receive(message);
        }
    }
}
public class MediatorTest {
    
    /**
     * 1.首先定义了中介者接口Mediator和同事类接口Colleague。
     * 2.然后,我们分别实现了两个具体的同事类ConcreteColleagueA和ConcreteColleagueB,
     			它们都实现了Colleague接口,并持有一个对中介者的引用。
     * 3.接下来,我们实现了一个具体的中介者类ConcreteMediator,它持有对两个同事类的引用,
     			并在接收到消息时将消息转发给另一个同事类。
     * 4.最后,在客户端代码中,我们创建了两个同事类对象和一个中介者对象,并将它们相互关联起来,
     * 然后通过调用同事类对象的send()方法来发送消息,并通过中介者对象进行转发
     * */
    public static void main(String[] args) {
        ConcreteColleagueA colleagueA = new ConcreteColleagueA(null);
        ConcreteColleagueB colleagueB = new ConcreteColleagueB(null);
        ConcreteMediator mediator = new ConcreteMediator(colleagueA, colleagueB);
        colleagueA.setMediator(mediator);
        colleagueB.setMediator(mediator);

        colleagueA.send("Hello from A");
        colleagueB.send("Hi from B");
    }
}
10.2.概念总结

中介者模式(Mediator)是一种行为设计模式,它的核心思想是通过一个中介者对象来协调多个对象之间的交互,从而降低这些对象之间的耦合度

.以下是对中介者模式的详细阐述:
10.2.1.核心角色:
  • Mediator(中介者):中介者定义了一个统一的接口,通过这个接口各个同事类对象之间进行通信,从而使得同事类对象自身不需要直接引用其他同事类的实例。
  • Colleague(同事类):每一个同事类对象都只与中介者对象发生交互,它们彼此之间并不直接通信。
10.2.2.优点:
  • 降低耦合性:同事类对象之间不直接交互,从而降低了它们之间的依赖关系,增加了系统的可维护性和可扩展性。
  • 简化通信:减少了对象间的直接通信,简化了对象之间的关系,降低了系统的复杂性。
  • 增加独立性:各对象可以独立变化和复用,符合单一职责原则和开闭原则。
10.2.3.缺点:
  • 可能效率较低:由于所有的通信都需要经过中介者,这可能会在一定程度上降低系统的效率。
  • 中介者责任大:如果中介者的职责过于复杂,可能导致中介者对象变得庞大且难以维护。
10.2.4.应用场景:
  • 当对象间的关系复杂且难以管理时,使用中介者模式可以帮助简化这些依赖关系。
  • 在前端开发中,例如处理表单提交和输入框之间的复杂交互,中介者模式可以有效避免对象之间的直接依赖和循环引用。

总的来说,中介者模式适用于那些需要协调多个对象之间的复杂交互,同时又希望保持这些对象之间松耦合的设计场景。

11.解释器模式(Interpreter)
11.1.代码实现
//操作步骤一:抽象表达式接口
public interface Expression {
    int interpret();
}

//操作步骤二:终结符表达式类
public class Number implements Expression {
    private int number;

    public Number(int number) {
        this.number = number;
    }

    @Override
    public int interpret() {
        return number;
    }
}
//操作步骤三:非终结符表达式类
public abstract class Operator implements Expression {
    protected Expression left, right;

    public Operator(Expression left, Expression right) {
        this.left = left;
        this.right = right;
    }
}
//操作步骤四:加法表达式类
public class Addition extends Operator {
    public Addition(Expression left, Expression right) {
        super(left, right);
    }

    @Override
    public int interpret() {
        return left.interpret() + right.interpret();
    }
}
//操作步骤四:减法表达式类
public class Subtraction extends Operator {
    public Subtraction(Expression left, Expression right) {
        super(left, right);
    }

    @Override
    public int interpret() {
        return left.interpret() - right.interpret();
    }
}
//操作步骤四:乘法表达式类
public class Multiplication extends Operator {
    public Multiplication(Expression left, Expression right) {
        super(left, right);
    }

    @Override
    public int interpret() {
        return left.interpret() * right.interpret();
    }
}

//操作步骤四:除法表达式类
public class Division extends Operator {
    public Division(Expression left, Expression right) {
        super(left, right);
    }

    @Override
    public int interpret() {
        return left.interpret() / right.interpret();
    }
}
//测试代码:
public class InterpreterTest {
    /**
     * 1.首先定义了一个抽象表达式接口Expression,它声明了解释操作的方法。
     * 2.然后,我们分别实现了终结符表达式类Number和非终结符表达式类Operator,其中Operator是一个抽象类,
     *     它包含了左右两个子表达式,并提供了解释操作的实现。
     * 3.接下来,我们分别实现了加法、减法、乘法和除法等四种非终结符表达式类,它们都继承自Operator类,
     			并重写了解释操作的方法。
     * 4.最后,在客户端代码中,我们创建了一个解释器对象,并通过调用其解释方法来得到最终的结果。
     * */
    public static void main(String[] args) {
        // 创建解释器对象
        Expression expression = new Addition(new Number(10), new Subtraction(new Number(5), 
                                                                             new Number(2)));
        // 解释并输出结果
        System.out.println("Result: " + expression.interpret());
    }
}


11.2.概念总结

解释器模式(Interpreter)是一种行为型设计模式,用于定义一种语言的文法,并构建一个解释器来解释该语言中的句子。

这种模式通常用于设计领域特定的语言或解析复杂文本文件时。它的核心思想是将语言的语法规则表示为一个解析树,然后通过这个解析树来解释和执行语言中的句子。

11.2.1.解释器模式的主要组成部分包括:
  1. 抽象表达式(Abstract Expression):定义解释器的接口,声明解释操作的方法。
  2. 终结符表达式(Terminal Expression):实现与文法中的终结符相关的解释操作。
  3. 非终结符表达式(Nonterminal Expression):为文法中的非终结符实现解释操作。
  4. 上下文(Context):包含解释器之外的一些全局信息,这些信息可能对解释操作有影响。

解释器模式的优点在于其良好的扩展性,因为使用类来表示语言的文法规则,可以通过继承等机制来改变或扩展文法。此外,由于在语法树中的每个表达式节点类都是相似的,所以实现其文法较为容易。然而,这种模式的缺点是可能会增加系统的复杂性,因为需要为每种文法规则实现具体的类。

总之,解释器模式适用于那些需要解释特定类型语言或表达式的场合,如编程语言解析器、工具或框架中的表达式求值等。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/560276.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

使用PHP开发体育赛事直播平台,有这些缺点和优点

"东莞梦幻网络科技"作为体育直播平台开发领域的领导者&#xff0c;选择使用PHP开发体育赛事直播平台的现成源码&#xff0c;为什么会选择该语言&#xff0c;背后的选择理由可以从该技术的优点和缺点中找到答案。 一、优点1、易学易用与快速开发&#xff1a;PHP语言语…

为电路提供参考电压(基准电压) - 齐纳二极管的使用

在电路中通常需要用到参考电压&#xff0c;即提供一个恒定的精确的电压值。比如稳压电路、比较器电路、微控制器的Vref&#xff0c;这些电路都需要提供参考电压。很多厂家都提供了参考电压芯片&#xff0c;不过最简单最省钱的方式是使用齐纳二极管。 齐纳二极管 齐纳二极管也是…

OSI网络七层协议 ——(随手笔记)

1.OSI OSI&#xff08;Open System Interconnect&#xff09;&#xff0c;即开放式系统互连。 一般都叫OSI参考模型&#xff0c;是ISO组织在1985年研究的网络互连模型。该体系结构标准定义了网络互连的七层框架&#xff08;物理层、数据链路层、网络层、传输层、会话层、表示层…

【MATLAB基础绘图第21棒】绘制比例弦图 (Chord Diagram)

MATLAB绘制比例弦图 Chord Diagram 1 简介1.1 弦图简介1.2 比例弦图简介 2 MATLAB绘制比例弦图2.1 数据准备2.2 基本绘制2.3 添加方向箭头2.4 添加绘图间隙2.5 添加刻度2.6 修改标签2.7 颜色设置2.8 弧块及弦属性设置2.8.1 弧块属性设置2.8.2 弦属性设置 2.9 字体设置 参考 1 简…

python数据分析pyecharts【饼状图、直方图、词云、地图】

目录 饼状图 直方图 词云 地图 饼状图 from pyecharts.charts import Pie from pyecharts import options as optsdata {神农架林区: 2.6016,恩施州: 3.0729,十堰市: 3.4300,宜昌市: 3.4555,襄阳市: 4.0543,咸宁市: 4.1145,荆门市: 4.1777,潜江市: 4.2574,黄冈市: 4.40…

C++智能指针(二十)

一.RAII&#xff08;Resource Acquisition Is Initialization&#xff09; RAII资源获取即初始化&#xff0c;RAII的思想就是在构造时初始化资源&#xff0c;或者托管已经构造的资源。在析构的时候释放资源。一般不允许复制或赋值&#xff0c;并且提供若干的资源访问的方法。比…

OpenHarmony其他工具类—lua

简介 Lua是一种功能强大、高效、轻量级、可嵌入的脚本语言。 支持过程编程、面向对象编程、函数编程、数据驱动编程和数据描述。 下载安装 直接在OpenHarmony-SIG仓中搜索lua并下载。 使用说明 以OpenHarmony 3.1 Beta的rk3568版本为例 将下载的lua库代码存在以下路径&#…

C# 将 TextBox 绑定为 KindEditor 富文本

目录 关于 KindEditor 绑定设计 部署 KindEditor 实现代码 小结 关于 KindEditor KindEditor 基于JavaScript 编写&#xff0c;可以与众多WEB应用程序结合。KindEditor 依靠出色的用户体验和领先的技术提供富文本编辑功能&#xff0c;是一款非常受欢迎的HTML在线编辑器。…

【FreeRTOS】使用CubeMX快速移植FreeRTOS工程到蓝桥杯开发板(STM32G431RBT6)

使用CubeMX快速创建FreeRTOS工程到蓝桥杯开发板&#xff08;STM32G431RBT6&#xff09; CubeMX配置CubeMX基础工程的配置☆FreeRTOS相关配置FreeRTOS配置选项卡的解释 软件工程架构与程序设计小综合&#xff1a;☆任务的创建删除、挂起与恢复设计cubexMX配置创建任务软件程序设…

高频前端面试题汇总之JavaScript篇(上)

一、数据类型 1. JavaScript有哪些数据类型&#xff0c;它们的区别&#xff1f; JavaScript共有八种数据类型&#xff0c;分别是 Undefined、Null、Boolean、Number、String、Object、Symbol、BigInt。 其中 Symbol 和 BigInt 是ES6 中新增的数据类型&#xff1a; Symbol 代…

关于 Windows10 计算机丢失 MSVCP120.dll 的解决方法

今天学长跟平时一样打开电脑开始发布文章需要用到Adobe Photoshop CC 2018的时候居然给我来个Photoshop.exe-系统错误、无法启动此程序&#xff0c;因为计算机中丢失MSVCP120.dll 尝试重新安装该程序以解决此问题&#xff0c;安装上面的说明重新安装了我的Photoshop CC 打开还是…

关于CAS

什么是CAS: CAS:Compare And Swap&#xff0c;比较且交换。 CAS中有三个参数&#xff1a;1.内存中原数据的值V 2.预期值A 3.修改后的数据B Compare&#xff1a;V与A会先比较是否一样 Swap&#xff1a;如果V与A一致&#xff0c;那么就将B写入V 返回操作是否成功 伪代码&…

椋鸟数据结构笔记#10:排序·中

文章目录 四、归并排序时间复杂度实现递归实现非递归实现 测试稳定性 五、非比较排序5.1 计数排序时间复杂度实现测试局限性 5.2 桶排序时间复杂度实现测试 5.3 基数排序时间复杂度实现测试局限性 萌新的学习笔记&#xff0c;写错了恳请斧正。 四、归并排序 归并排序是一种非常…

微服务使用SockJs+Stomp实现Websocket 前后端实例 | Vuex形式断开重连、跨域等等问题踩坑(一)

大家好&#xff0c;我是程序员大猩猩。 之前几篇文章&#xff0c;我们讲了Spring Cloud Gateway的轻量级实现&#xff0c;Nginx的配置概念与实现&#xff0c;如以下往期文章。 轻量级的Spring Cloud Gateway实践&#xff0c;实现api和websocket转发轻松实现Nginx的HTTP与WebS…

新产品成功的七大关键要素:理论解析与案例探讨

在激烈的市场竞争中&#xff0c;新产品的成功推出不仅关乎企业的生死存亡&#xff0c;更是企业持续发展的核心动力。那么&#xff0c;新产品如何能够脱颖而出&#xff0c;赢得市场的青睐呢&#xff1f;本文将深入探讨新产品成功的七大关键要素&#xff0c;并结合实际案例进行解…

中颖51芯片学习8. ADC模数转换

中颖51芯片学习8. ADC模数转换 一、ADC工作原理简介1. 概念2. ADC实现方式3. 基准电压 二、中颖芯片ADC功能介绍1. 中颖芯片ADC特性2. ADC触发源&#xff08;1&#xff09;**软件触发**&#xff08;2&#xff09;**TIMER4定时器触发**&#xff08;3&#xff09;**外部中断2触发…

洛谷P1057 [NOIP2008 普及组] 传球游戏

#include<iostream> using namespace std; int n;// n个人传球游戏 默认开始球在编号为1的位置 int m;// 传递m次球 int main(){cin>>n>>m;// 动态转方程&#xff1a;// 球传递到编号为k人的手中// 种类总数 传递到k-1编号种类总数 传递到k1编号种类总数//…

如何查看微信公众号发布文章的主图,如何看微信文章的主图,怎么才能拿到主图

如何查看&#xff0c;微信公众号发布文章的主图&#xff0c;如何看微信文章的主图 起因是这样的&#xff0c;当我看到一篇文章的时候&#xff0c;他的主图很漂亮&#xff0c;但是&#xff0c;正文里没有&#xff0c;而我又想看到&#xff0c;并且使用这张图片&#xff0c;该怎么…

代码随想录训练营Day 27|Python|Leetcode|122.买卖股票的最佳时机II ● 55. 跳跃游戏 ● 45.跳跃游戏II

122.买卖股票的最佳时机II 给你一个整数数组 prices &#xff0c;其中 prices[i] 表示某支股票第 i 天的价格。 在每一天&#xff0c;你可以决定是否购买和/或出售股票。你在任何时候 最多 只能持有 一股 股票。你也可以先购买&#xff0c;然后在 同一天 出售。 返回 你能获…

同旺科技 USB TO SPI / I2C适配器读写24LC256--页写

所需设备&#xff1a; 1、USB 转 SPI I2C 适配器&#xff1b;内附链接 2、24LC256芯片 适应于同旺科技 USB TO SPI / I2C适配器升级版、专业版&#xff1b; 从00地址开始写入64个字节&#xff0c;然后再将64个字节读回&#xff1b; 页写时序&#xff1a; 读时序&#xff1a…
最新文章