11种创造型设计模式(下)

观察者模式

我们可以比喻观察者模式是一种类似广播的设计模式

介绍

观察者模式:对象之间多对一依赖的一种设计方案,被依赖的对象是Subject,依赖的对象是Observer,Subject通知Observer变化。

image-20240314134453836

代码

说明:

WeatherStation 充当主题,WeatherDisplay 充当观察者。WeatherStation 维护了一个观察者列表,提供了注册、移除和通知观察者的方法。当天气更新时,调用 updateWeather() 方法来模拟更新天气并通知观察者。观察者收到通知后会调用 update() 方法来更新天气信息。

image-20240314135021001

import java.util.ArrayList;
import java.util.List;
​
// 主题接口
interface WeatherSubject {
    void registerObserver(WeatherObserver observer);
    void removeObserver(WeatherObserver observer);
    void notifyObservers();
}
​
// 观察者接口
interface WeatherObserver {
    void update(String weather);
}
​
// 具体主题类
class WeatherStation implements WeatherSubject {
    private List<WeatherObserver> observers = new ArrayList<>();
    private String weather;
​
    @Override
    public void registerObserver(WeatherObserver observer) {
        observers.add(observer);
    }
​
    @Override
    public void removeObserver(WeatherObserver observer) {
        observers.remove(observer);
    }
​
    @Override
    public void notifyObservers() {
        for (WeatherObserver observer : observers) {
            observer.update(weather);
        }
    }
​
    // 模拟天气更新,并通知观察者
    public void updateWeather(String newWeather) {
        this.weather = newWeather;
        notifyObservers();
    }
}
​
// 具体观察者类
class WeatherDisplay implements WeatherObserver {
    private String observerName;
​
    public WeatherDisplay(String observerName) {
        this.observerName = observerName;
    }
​
    @Override
    public void update(String weather) {
        System.out.println(observerName + " 收到天气更新: " + weather);
    }
}
​
public class WeatherObserverExample {
    public static void main(String[] args) {
        // 创建天气主题
        WeatherStation weatherStation = new WeatherStation();
​
        // 创建天气显示观察者
        WeatherObserver observer1 = new WeatherDisplay("观察者1");
        WeatherObserver observer2 = new WeatherDisplay("观察者2");
​
        // 注册观察者
        weatherStation.registerObserver(observer1);
        weatherStation.registerObserver(observer2);
​
        // 模拟天气更新
        weatherStation.updateWeather("晴天");
​
        // 移除观察者2
        weatherStation.removeObserver(observer2);
​
        // 再次模拟天气更新
        weatherStation.updateWeather("下雨");
    }
}

观察者莫斯在JDK中的使用分析

JDK中的Observable使用了观察者模式。

直接追源码,看它的结构:

1、addObserver添加
2、deleteObserver:删除
3、notifyObserver:通知
4、Vector<Observer>观察者列表

image-20240314135417488

image-20240314135547827

总结

个人理解:观察者模式就是一种似广播的模式,一个站对多个对象。

观察者模式(Observer Pattern)是一种软件设计模式,它定义了一种一对多的依赖关系,使得多个观察者对象同时监听某一个主题对象。当主题对象的状态发生变化时,所有依赖于它的观察者都会得到通知并自动更新。

结构

观察者模式通常包含以下角色:

  1. Subject(主题):它是被观察的对象,当其状态发生变化时会通知观察者。主题可以是一个接口或抽象类,定义了注册、删除和通知观察者的方法。

  2. ConcreteSubject(具体主题):实现了主题接口或抽象类,它存储了一系列观察者对象,状态发生变化时会通知这些观察者。

  3. Observer(观察者):定义了一个更新接口,以便主题在状态发生变化时能够通知到它。

  4. ConcreteObserver(具体观察者):实现了观察者接口,它需要注册到具体主题对象中,以便在主题状态发生变化时接收通知并进行相应的处理。

工作原理

  1. 当主题对象的状态发生变化时,会调用主题对象的通知方法。

  2. 主题对象的通知方法会遍历注册在其中的所有观察者对象,并调用它们的更新方法。

  3. 观察者对象在接收到通知后会执行相应的更新操作,以使自身状态与主题对象保持同步。

优点

  • 松耦合:观察者模式将主题对象和观察者对象之间的耦合度降到了最低,主题和观察者之间彼此独立地变化。

  • 扩展性好:可以根据需要随时增加新的观察者或主题,使系统更易于扩展。

  • 符合开闭原则:系统中新增加观察者或主题不需要修改原有的代码。

适用场景

  • 当一个对象的改变需要同时影响其他对象,而且不知道具体有多少对象需要被通知时,可以考虑使用观察者模式。

  • 当一个抽象模型有两个方面,其中一个方面依赖于另一个方面,但又需要独立于其变化时,可以考虑使用观察者模式。

  • 当一个对象的改变需要同时影响到其他对象,而且你不希望知道具体有多少对象需要被通知时,可以考虑使用观察者模式。

观察者模式在软件开发中被广泛应用,例如 GUI 开发中的事件监听器、Android 中的广播机制等。

中介者模式

可以将中介者模式理解成一种通过中间类来进行通信和协调的设计模式

介绍

1、中介者模式是一种行为型设计模式,用来减少对象间的直接耦合
2、在中介者模式中,对象之间不再相互通信,而是通过一个中介者对象来进行通信。这有助于降低系统的复杂度,提高系统的可维护性和可扩展性

中介者模式中的角色:

1、中介者(Mediator):负责协调对象之间的交互关系,通过中介者对象来通知其他对象
2、同事(Colleague):维护一个对中介者对象的引用,通过中介者对象进行通信

代码

image-20240316174202282

import java.util.ArrayList;
import java.util.List;
​
// 中介者接口
interface Mediator {
    void sendMessage(String message, Colleague colleague);
}
​
// 具体中介者
class ConcreteMediator implements Mediator {
    private List<Colleague> colleagues;
​
    public ConcreteMediator() {
        colleagues = new ArrayList<>();
    }
​
    public void addColleague(Colleague colleague) {
        colleagues.add(colleague);
    }
​
    @Override
    public void sendMessage(String message, Colleague colleague) {
        for (Colleague col : colleagues) {
            // 排除发送者自身
            if (col != colleague) {
                col.receiveMessage(message);
            }
        }
    }
}
​
// 同事接口
interface Colleague {
    void sendMessage(String message);
​
    void receiveMessage(String message);
}
​
// 具体同事类
class ConcreteColleague implements Colleague {
    private Mediator mediator;
​
    public ConcreteColleague(Mediator mediator) {
        this.mediator = mediator;
    }
​
    @Override
    public void sendMessage(String message) {
        mediator.sendMessage(message, this);
    }
​
    @Override
    public void receiveMessage(String message) {
        System.out.println("Received message: " + message);
    }
}
​
public class Main {
    public static void main(String[] args) {
        ConcreteMediator mediator = new ConcreteMediator();
​
        ConcreteColleague colleague1 = new ConcreteColleague(mediator);
        ConcreteColleague colleague2 = new ConcreteColleague(mediator);
​
        mediator.addColleague(colleague1);
        mediator.addColleague(colleague2);
​
        colleague1.sendMessage("Hello, colleague2!");
        colleague2.sendMessage("Hi, colleague1!");
    }
}
 

中介者模式和观察者模式对比

学完这两种设计模式,我们会发现两种设计模式,其实是有很多相似的地方的。

1、中介者模式发消息其实是针对的发的,类似QQ聊天中的单聊。

2、观察者模式发消息是广播式的发,类似QQ聊天中的群聊

这里又可以放入消息队列中:

  1. 观察者模式类似于fanout交换机:在观察者模式中,一个被观察者对象(通常称为主题或者被观察者)可以同时通知多个观察者对象,就像fanout交换机一样,它会将消息广播给所有与之绑定的队列。每个观察者对象都会收到相同的消息,并且它们之间的关系是松散耦合的。

  2. 中介者模式类似于direct交换机:在中介者模式中,对象之间的通信通过一个中介者进行,中介者根据消息的内容将消息传递给特定的对象,就像direct交换机一样,它会根据消息的路由键将消息发送到与之匹配的队列。这种方式下,对象之间的关系是通过中介者来进行管理和协调的。

总结

要点总结:

  1. 中介者模式通过引入中介者对象来减少对象之间的直接耦合。

  2. 中介者模式可以降低系统的复杂性,提高系统的可维护性和可扩展性。

  3. 中介者模式的核心在于中介者对象,它负责协调和管理对象之间的交互关系。

  4. 同事对象通过中介者对象来进行通信,不再直接相互依赖。

使用场景:

中介者模式适用于多个对象之间存在复杂的交互关系,但是又不希望它们之间相互耦合的场景,例如,图形用户界面中的控件之间的交互、多人协作系统中的用户之间的通信等。

备忘录模式

可以将备忘录模式理解为一种通过存储对象状态并能够改变状态的设计模式

介绍

1. 备忘录模式在不破坏封装性的前提下,捕获一个对象内部状态,并在对象之外报错这个状态。这样就可以将该对象恢复到原先保存状态
2. 备忘录对象主要用来记录一个对象的某种状态/某些数据,当要回退时,可以从备忘录对象获取原来的数据进行恢复操作
3. 属于行为型模式

 角色

1. 发起人(Originator):负责创建备忘录对象,将其状态保存到备忘录中,并从备忘录中恢复状态。
2. 备忘录(Memento):用于存储发起人对象的内部状态,通常提供对状态的访问方法。
3. 管理者(Caretaker):负责保存备忘录对象,但不会对备忘录的内容进行操作或者检查。

 使用流程

1. 发起人创建备忘录对象,并将其内部状态保存到备忘录中。
2. 管理者保存备忘录对象。
3. 在需要时,发起人从备忘录中恢复状态。

 代码

结合备忘录模式和代理模式,实现一个简单的文档编辑器。编辑器包含一个文本输入框,用户可以在文本输入框中输入内容。我们将使用代理模式来控制对文本输入框的访问,并使用备忘录模式来保存文本输入框的历史状态,以便用户可以撤销操作。


import java.util.Stack;

// 备忘录类
class TextMemento {
    private String text;

    public TextMemento(String text) {
        this.text = text;
    }

    public String getText() {
        return text;
    }
}

// 发起人类
class TextEditor {
    private String text;
    private Stack<TextMemento> history;

    public TextEditor() {
        this.text = "";
        this.history = new Stack<>();
    }

    public void setText(String text) {
        this.text = text;
    }

    public String getText() {
        return text;
    }

    public void save() {
        history.push(new TextMemento(text));
    }

    public void undo() {
        if (!history.isEmpty()) {
            text = history.pop().getText();
        }
    }
}

// 代理类
class TextEditorProxy {
    private TextEditor editor;

    public TextEditorProxy() {
        this.editor = new TextEditor();
    }

    public void setText(String text) {
        editor.setText(text);
    }

    public String getText() {
        return editor.getText();
    }

    public void save() {
        editor.save();
    }

    public void undo() {
        editor.undo();
    }
}

public class Main {
    public static void main(String[] args) {
        TextEditorProxy editor = new TextEditorProxy();

        // 用户输入文本
        editor.setText("Hello, World!");

        // 用户保存当前文本状态
        editor.save();

        // 用户继续输入文本
        editor.setText("This is a text editor.");

        // 用户再次保存当前文本状态
        editor.save();

        // 用户进行撤销操作
        editor.undo();

        System.out.println("Current text: " + editor.getText());
    }
}

 小结

个人理解:备忘录模式就是一种用来保存某类对象某个状态的模式。


备忘录模式的主要目的是允许在不暴露对象内部结构的情况下,捕获和恢复对象的状态。这种模式常用于需要在某个时间点保存对象状态,并在需要时将其恢复的情况下。
 

1. 优点

  •    - 备忘录模式使得发起人对象的状态保存与恢复得以分离,提高了系统的封装性和灵活性。
  •    - 可以通过备忘录模式实现撤销操作,使得用户可以在操作错误时进行恢复。

2. 缺点
   - 如果备忘录对象过多或者状态信息过大,可能会导致内存消耗较大。
   - 备忘录模式可能会增加系统的复杂性,特别是在需要管理多个备忘录对象时。

3. 为了节约内存,备忘录模式可以和原型模式配合使用

解释器模式(Interpreter模式)

可以理解解释器模式是一种定义一种语言文法的表示,并提供一个解释器来解释这种语言的语句的设计模式

介绍

  1. 是一种行为型设计模式。用于定义一个语言的文法,并提供解释器来解释该语言中的句子

  2. 目的:用于解释一种特定语言的语法规则,并提供方法来解释和执行该语言中的矩阵。

  3. 使用例子:编译器、运算表达式计算、正则表达式、机器人等

角色

  1. 抽象表达式(Abstract Expression):定义一个解释器的接口,其中包括一个解释方法,用于解释语言中的句子。 (抽象表达式,声明一个抽象的解释操作,为抽象语法树中所有节点所共享)

  2. 终结符表达式(Terminal Expression):实现抽象表达式接口的类,用于解释语言中的终结符或基本元素。 (实现文法中的终结符相关的解释操作)

  3. 非终结符表达式(Non-terminal Expression):实现抽象表达式接口的类,用于解释语言中的非终结符或复合元素。 (为文法中的非终结符实现解释操作)

  4. 上下文(Context):包含了需要解释的语句或表达式的信息,提供给解释器进行解释。 (环境角色,含有解释器之外的全局信息)

使用流程

  • 客户端创建一个上下文对象,并在上下文中设置需要解释的语句或表达式。

  • 客户端创建解释器对象,并将上下文对象传递给解释器。

  • 解释器根据语言的文法规则逐步解释并执行语句或表达式,生成最终的结果。

输入Context he TerminalExpression 信息通过Client输入即可。

代码

image-20240317152640259

package com.pxl.test.Designpattern;
​
import java.util.Stack;
​
// 抽象表达式接口
interface Expression {
    int interpret();
}
​
// 终结符表达式:数字
class Number implements Expression {
    private int value;
​
    public Number(int value) {
        this.value = value;
    }
​
    @Override
    public int interpret() {
        return value;
    }
}
​
// 非终结符表达式:算术运算符表达式
class ArithmeticOperator implements Expression {
    private Expression leftOperand;
    private Expression rightOperand;
    private char operator;
​
    public ArithmeticOperator(Expression leftOperand, Expression rightOperand, char operator) {
        this.leftOperand = leftOperand;
        this.rightOperand = rightOperand;
        this.operator = operator;
    }
​
    @Override
    public int interpret() {
        if (operator == '+') {
            return leftOperand.interpret() + rightOperand.interpret();
        } else if (operator == '-') {
            return leftOperand.interpret() - rightOperand.interpret();
        }
        // Handle other operators if needed
        return 0;
    }
}
​
// 上下文类
class Context {
    public Stack<Expression> stack = new Stack<>();
​
    public void pushExpression(Expression expression) {
        stack.push(expression);
    }
​
    public Expression popExpression() {
        return stack.pop();
    }
​
    public boolean isOperator(Expression expression) {
        return expression instanceof ArithmeticOperator;
    }
}
​
// 解释器
class Parser {
    private Context context;
​
    public Parser(Context context) {
        this.context = context;
    }
​
    public int parse(String input) {
        String[] tokens = input.split("\\s+");
        for (String token : tokens) {
            if ("+".equals(token)) {
                Expression rightOperand = context.popExpression();
                Expression leftOperand = context.popExpression();
                context.pushExpression(new ArithmeticOperator(leftOperand, rightOperand, '+'));
            } else if ("-".equals(token)) {
                Expression rightOperand = context.popExpression();
                Expression leftOperand = context.popExpression();
                context.pushExpression(new ArithmeticOperator(leftOperand, rightOperand, '-'));
            } else {
                context.pushExpression(new Number(Integer.parseInt(token)));
            }
        }
​
        return context.popExpression().interpret();
    }
}
​
public class ExpressionMain {
    public static void main(String[] args) {
        String input = "3 4 - 5 +";
        Context context = new Context();
        Parser parser = new Parser(context);
        int result = parser.parse(input);
        System.out.println("Result: " + result); // 输出
    }
}

在SpelExpressionParser类中的使用

image-20240317153515523

image-20240317153542880

image-20240317153719066

public class Test {
    public static void main(String[] args) {
        SpelExpressionParser parser = new SpelExpressionParser();
        Expression expression = parser.parseExpression("10 *(2+1)");
        int value = (int) expression.getValue();
        System.out.println(value);
    }
}

小结

个人理解:解释器模式通过定义一些基本元素和操作(终结符表达式)和一些复杂规则(非终结符表达式),让解释器进行解释,获得想要的结果

解释器模式确实通过定义基本元素和操作(终结符表达式)以及复杂规则(非终结符表达式)来构建解释器,从而实现对给定语言或语法的解释和执行。终结符表达式代表了文法中的最基本的元素或操作,而非终结符表达式则表示了文法中的复杂规则或组合规则。通过将这些表达式组合起来,解释器能够解释和执行整个语言表达式,从而得到最终的结果。

  1. 优点

    • 简化了语言的解释和执行过程,使得添加新的语法规则或扩展现有规则变得容易。

    • 易于实现和理解,对于特定领域的语言解释非常高效。

  2. 缺点

    • 难以维护复杂的文法规则,特别是文法变化频繁或文法非常复杂时。

    • 可能会导致类的数量急剧增加,增加了系统的复杂性。

状态模式

可以理解状态模式是一种不断改变状态改变类本质的设计模式,如一个人一样,小时候和长大后是不同的状态了,但是还是同一个人。

介绍

1、状态模式主要来解决对象在多种状态转换时,需要对外输出不同行为的问题。状态和行为是一一对应的,状态之间可以互相转换

2、当一个对象的内在状态改变时,允许改变其行为,这个对象看起来(可以举例称一个人,小时候和长大后状态不同了,但是还是同一个人)

原理类图:

image-20240318154315850

说明:

1、Context类为环境角色,用于维护State实例,这个实例定义当前状态
2、State是抽象状态角色,定义一个接口封装与Context的一个特点接口相关行为
3、ConcreteState具体的状态角色,每个子类实现一个与Context的一个状态相关行为

代码

image-20240318155537417

状态模式用于管理抽奖的不同状态,包括初始状态、中奖状态和未中奖状态。每个状态都实现了LotteryState接口,并实现了对应的抽奖逻辑。LotteryContext类用于维护当前的状态,并委托给当前状态处理抽奖操作。在测试类中,进行了5次抽奖操作,每次抽奖都会根据当前状态执行相应的逻辑。

下面代码中,不断的在setState(),将它的类在进行转变,从初始化状态类,转变成未中奖/中奖类

package com.pxl.test.Designpattern;
​
// 定义抽奖状态接口
interface LotteryState {
    void draw(LotteryContext context);
}
​
// 初始状态
class InitialState implements LotteryState {
    @Override
    public void draw(LotteryContext context) {
        // 在初始状态下,可以进行抽奖
        System.out.println("正在抽奖,请稍候...(第一次初始化状态。。。)");
        // 模拟抽奖逻辑
        int luckyNumber = (int) (Math.random() * 100);
        if (luckyNumber < 50) {
            System.out.println("恭喜你中奖了!");
            context.setState(new WinnerState()); // 切换到中奖状态
        } else {
            System.out.println("很遗憾,你没有中奖。");
            context.setState(new NotWinnerState()); // 切换到未中奖状态
        }
    }
}
​
// 中奖状态
class WinnerState implements LotteryState {
    @Override
    public void draw(LotteryContext context) {
        // 已经中奖了,不能再抽奖
        System.out.println("你已经中过奖了,请等待下一次抽奖。");
    }
}
​
// 未中奖状态
class NotWinnerState implements LotteryState {
    @Override
    public void draw(LotteryContext context) {
        // 在未中奖状态下,可以进行抽奖
        System.out.println("正在抽奖,请稍候...");
        // 模拟抽奖逻辑
        int luckyNumber = (int) (Math.random() * 100);
        if (luckyNumber < 50) {
            System.out.println("恭喜你中奖了!");
            context.setState(new WinnerState()); // 切换到中奖状态
        } else {
            System.out.println("很遗憾,你没有中奖。");
        }
    }
}
​
// 抽奖上下文
class LotteryContext {
    private LotteryState state;
​
    public LotteryContext() {
        this.state = new InitialState(); // 初始状态为初始状态
    }
​
    public void setState(LotteryState state) {
        this.state = state;
    }
​
    public void draw() {
        state.draw(this); // 委托状态处理抽奖操作
    }
}
​
// 测试类
public class StateMain {
    public static void main(String[] args) {
        LotteryContext context = new LotteryContext();
​
        // 进行5次抽奖
        for (int i = 0; i < 5; i++) {
            context.draw();
            System.out.println("---------------------------------");
        }
    }
}

小结

个人理解:

状态模式是一种不断改变自己状态(就是不断通过接口实现类进行转变),实现系统灵活性的模式。

优化点:将枚举类代替状态类,避免类爆炸。(但是要注意的是这种会违反开闭原则)

状态模式通过定义不同的状态类以及相应的状态转换规则,使得对象能够根据内部状态的改变而改变自身的行为。这种动态的状态转换使得系统具有了更大的灵活性和可扩展性。通过接口实现类之间的转变,状态模式使得系统可以在运行时根据需求动态地改变对象的状态,而不需要修改对象的代码。这种灵活性使得状态模式在需要对象根据不同条件改变行为的场景中非常有用。

状态模式是一种强大的设计模式,但它也有其优点和缺点。

优点:

  1. 清晰的状态转换:状态模式将每个状态封装到一个类中,使得状态之间的转换变得清晰明确,易于理解和维护。

  2. 简化条件判断:通过将状态的行为抽象为独立的类,状态模式消除了长串的条件判断语句,使得代码更加简洁清晰。

  3. 符合开闭原则:状态模式通过封装每个状态和状态之间的转换规则,使得新增状态或者修改现有状态的行为变得容易,符合开闭原则。

  4. 提高可扩展性:状态模式使得系统在不同状态下可以拥有不同的行为,新增状态只需要添加新的状态类而不需要修改现有代码,提高了系统的可扩展性。

  5. 促进了模块化设计:每个状态都被封装成一个独立的类,使得系统更加模块化,每个模块负责一个特定的状态,易于理解和维护

缺点:

  1. 可能导致类爆炸:当系统中存在大量的状态时,可能会导致类的数量急剧增加,使得系统变得复杂。因此,在设计状态模式时需要合理把握状态的数量。

  2. 增加了系统复杂性:状态模式引入了多个状态类和状态之间的转换规则,可能会增加系统的复杂性,使得理解和维护变得困难。

  3. 不适用于简单状态转换:如果系统中仅有少量的状态且状态之间的转换比较简单,使用状态模式可能会显得过于繁琐,不适合于简单的状态转换场景。

  4. 可能导致对象间的依赖增加:当状态对象需要访问其他对象的状态或者信息时,可能会导致状态对象之间的依赖增加,使得系统变得复杂。

使用场景

  1. 对象行为随状态变化而变化

  2. 条件语句导致代码复杂(避免大量条件判断)

  3. 各个状态类之间相互独立

  4. 例如说在借阅平台中,状态从审核 -> 借阅 ->归还

策略模式

策略模式是一种通过改变策略,改变执行方式的设计模式

介绍

1、策略模式(Strategy Pattern)是一种行为设计模式,它允许在运行时选择算法的行为。它将算法封装成独立的类,使得它们可以相互替换,而不会影响客户端使用算法的方式。

2、把变化的代码从不变的代码中分离开;针对接口编程而不是具体类(定义了策略接口);多用组合/聚合,少用继承(客户通过组合方法实现策略)

代码

image-20240318163254403

DiscountStrategy 是策略接口,定义了抽象的折扣计算方法。然后,我们有三个具体的策略类实现了这个接口,分别是NoDiscountStrategyFixedDiscountStrategyThresholdDiscountStrategy,分别代表无折扣、固定折扣和满减折扣。ShoppingCart 类作为上下文类,接受一个折扣策略作为参数,然后根据具体的策略计算最终的价格。通过这种方式,客户端可以轻松地切换不同的折扣策略,而不需要修改ShoppingCart类的代码。

package com.pxl.test.Designpattern;
​
// 策略接口
interface DiscountStrategy {
    double applyDiscount(double totalPrice);
}
​
// 第一个具体策略类:无折扣
class NoDiscountStrategy implements DiscountStrategy {
    @Override
    public double applyDiscount(double totalPrice) {
        return totalPrice;
    }
}
​
// 第二个具体策略类:固定折扣
class FixedDiscountStrategy implements DiscountStrategy {
    private double discountRate;
​
    public FixedDiscountStrategy(double discountRate) {
        this.discountRate = discountRate;
    }
​
    @Override
    public double applyDiscount(double totalPrice) {
        return totalPrice - (totalPrice * discountRate);
    }
}
​
// 第三个具体策略类:满减折扣
class ThresholdDiscountStrategy implements DiscountStrategy {
    private double threshold;
    private double discountAmount;
​
    public ThresholdDiscountStrategy(double threshold, double discountAmount) {
        this.threshold = threshold;
        this.discountAmount = discountAmount;
    }
​
    @Override
    public double applyDiscount(double totalPrice) {
        if (totalPrice >= threshold) {
            return totalPrice - discountAmount;
        }
        return totalPrice;
    }
}
​
// 上下文类,用于选择和应用具体的策略
class ShoppingCart {
    private DiscountStrategy discountStrategy;
​
    public ShoppingCart(DiscountStrategy discountStrategy) {
        this.discountStrategy = discountStrategy;
    }
​
    public double checkout(double totalPrice) {
        return discountStrategy.applyDiscount(totalPrice);
    }
}
​
// 客户端代码
public class StrategyMain {
    public static void main(String[] args) {
        ShoppingCart cart1 = new ShoppingCart(new NoDiscountStrategy());
        ShoppingCart cart2 = new ShoppingCart(new FixedDiscountStrategy(0.1));
        ShoppingCart cart3 = new ShoppingCart(new ThresholdDiscountStrategy(100, 20));
​
        double totalPrice1 = 100;
        double totalPrice2 = 200;
        double totalPrice3 = 150;
​
        System.out.println("Cart 1 Total Price: " + cart1.checkout(totalPrice1));
        System.out.println("Cart 2 Total Price: " + cart2.checkout(totalPrice2));
        System.out.println("Cart 3 Total Price: " + cart3.checkout(totalPrice3));
    }
}
​

策略模式在Arrays中的使用

策略模式在Arraysp排序的时候使用到了策略模式。

分析:

Integer[] data = new Integer[]{3,2,1,5,4};
        Comparator<Integer> comparator = new Comparator<>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return o1 >= o2 ? 1 : -1;
            }
        };
        Arrays.sort(data,comparator);
        for (Integer datum : data) {
            System.out.print(datum + " ");
        }

上述代码就用到了策略,Arrays.sort(data,comparator);中的comparator采用了升序策略。

我们直接点击sort的源码

image-20240318165211304

小结

个人理解:策略模式是通过一个上下文类来聚合一个策略接口,并灵活的调用想要的策略。

策略模式通过将算法封装在独立的策略类中,并让上下文类持有一个策略接口的引用,实现了算法的灵活组合和调用。这种设计使得客户端可以根据需求选择并切换不同的策略,而不需要修改上下文类的代码,从而提高了代码的灵活性和可维护性。

作用:

  • 允许在运行时动态地选择算法。

  • 将算法的实现与使用它的客户端代码分离,提高了代码的灵活性和可维护性。

  • 通过使用接口和多态,实现了松耦合。

符合的原则:

  • 开闭原则(Open/Closed Principle):策略模式通过定义算法族,并使它们之间可以相互替换,从而在不修改原有代码的情况下添加新的算法。

  • 单一责任原则(Single Responsibility Principle):每个策略类只负责一个特定的算法或行为。

优点:

  1. 提供了更好的扩展性,可以轻松地添加新的算法或修改现有的算法。

  2. 提高了代码的可读性和可维护性,因为每个算法都被封装在一个独立的类中。

  3. 可以在运行时动态地选择算法,使得客户端代码更加灵活。

缺点:

  1. 增加了类的数量,可能会导致代码变得复杂。

  2. 客户端需要了解所有的策略类,可能会增加学习和理解的成本。

职责链模式(责任链模式)

可以理解职责链设计模式理解成一种链式处理请求的设计模式

介绍

1、职责链模式为请求创建一个接收者对象的链。这种模式对请求的发送者和接收者进行解耦

2、职责链模式通常每个接收者都包含对另一个接收者的引用。如果一个对象不能处理该请求,那么它会把相同的请求传给下一个接收者,依次类推。

image-20240318170701699

代码

image-20240318171547534

报销请求首先传递给经理(Manager),如果金额小于等于1000,则经理会直接批准;否则,请求会继续传递给部门主管(Department Supervisor),如果金额小于等于5000,则部门主管批准;否则,请求会继续传递给财务主管(Financial Manager)。

package com.pxl.test.Designpattern;
​
// 请求类
class ReimbursementRequest {
    private double amount;
    
    public ReimbursementRequest(double amount) {
        this.amount = amount;
    }
    
    public double getAmount() {
        return amount;
    }
}
​
// 处理者接口
interface Handler {
    void processRequest(ReimbursementRequest request);
}
​
// 具体处理者类
class Manager implements Handler {
    private Handler nextHandler;
​
    public Manager(Handler nextHandler) {
        this.nextHandler = nextHandler;
    }
​
    @Override
    public void processRequest(ReimbursementRequest request) {
        if (request.getAmount() <= 1000) {
            System.out.println("Manager approves the reimbursement request.");
        } else {
            nextHandler.processRequest(request);
        }
    }
}
​
class DepartmentSupervisor implements Handler {
    private Handler nextHandler;
​
    public DepartmentSupervisor(Handler nextHandler) {
        this.nextHandler = nextHandler;
    }
​
    @Override
    public void processRequest(ReimbursementRequest request) {
        if (request.getAmount() <= 5000) {
            System.out.println("Department Supervisor approves the reimbursement request.");
        } else {
            nextHandler.processRequest(request);
        }
    }
}
​
class FinancialManager implements Handler {
    @Override
    public void processRequest(ReimbursementRequest request) {
        System.out.println("Financial Manager approves the reimbursement request.");
    }
}
​
// 客户端
public class ChainOfResponsibilityClient {
    public static void main(String[] args) {
        // 构建处理者链
        Handler financialManager = new FinancialManager();
        Handler departmentSupervisor = new DepartmentSupervisor(financialManager);
        Handler manager = new Manager(departmentSupervisor);
​
        // 创建报销请求
        ReimbursementRequest request1 = new ReimbursementRequest(800);
        ReimbursementRequest request2 = new ReimbursementRequest(3500);
        ReimbursementRequest request3 = new ReimbursementRequest(10000);
​
        // 发送请求
        manager.processRequest(request1);
        manager.processRequest(request2);
        manager.processRequest(request3);
    }
}
 

职责链模式在SpringMVC框架中的应用

在拦截器中使用到了。从核心类DispatcherServlet追。

职责链模式可以被理解为一种链式处理问题的解决方案。在职责链模式中,请求被传递到处理者链上的每个处理者,直到有一个处理者能够处理该请求为止。这种处理方式形成了一种链式的结构,因此被称为职责链模式。

总结

优点:

  1. 解耦性:请求的发送者和接收者之间解耦,发送者不需要知道具体的接收者。

  2. 可扩展性:可以方便地新增或调整处理者链,以满足不同的需求。

  3. 灵活性:可以动态地改变处理者链的顺序或组成,以满足不同的请求处理流程。

  4. 单一职责原则:每个处理者只需要关注自己能够处理的请求,不需要关注其他请求。

缺点:

  1. 请求可能未被处理:如果整条处理者链都不能处理请求,那么请求可能会未被处理。

  2. 性能问题:当处理者链过长时,可能会影响性能。

  3. 调试困难:由于请求的处理流程是动态的,因此调试起来可能会比较困难

 

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

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

相关文章

手撕算法-判断是不是二叉搜索树

题目描述 分析 二叉搜索树的特性就是中序遍历是递增序。既然是判断是否是二叉搜索树&#xff0c;那我们可以使用中序递归遍历。只要之前的节点是二叉树搜索树&#xff0c;那么如果当前的节点大于上一个节点值那么就可以向下判断。 如果有出现当前的节点小于上一个节点值&…

Host xxx1 has more disk space than database expected (xxx2 GB > xxx3 GB)

在nova-compute.log中有时会看到日志“Host xxx1 has more disk space than database expected (xxx2 GB &#xff1e; xxx3 GB)”类似日志。 查看下源码&#xff0c;如下&#xff1a; 分析&#xff1a; 定时任务更新主机资源到内存或者对象中&#xff0c;当执行检测的定时任务…

颠覆传统:Web3如何塑造未来的数字经济

引言 近年来&#xff0c;随着数字化时代的到来&#xff0c;互联网已经成为人们生活中不可或缺的一部分。然而&#xff0c;随着技术的不断发展和社会的不断变迁&#xff0c;传统的Web2模式逐渐显露出一些弊端&#xff0c;如数据垄断、隐私泄露等问题&#xff0c;这促使人们寻求…

Linux账号管理与ACL权限设置

文章目录 Linux的账户和用户组用户标识符&#xff1a;UID与GID用户账号用户组&#xff1a;有效与初始用户组groups&#xff0c;newgrp 账号管理新增与删除用户&#xff1a;useradd、相关配置文件、passwd、usermod、userdel用户功能&#xff1a;id、finger、chfn、chsh新增与删…

【文件操作和IO】

文件操作和IO 1.文件2. 硬盘上文件的目录结构3. 文件路径4. 文件重要分类&#xff1a;5. Java中操作文件5.1 Java对于文件操作的API5.2 Java中使用File类来进行文件操作5.3 File类属性5.4 构造方法5.5 方法&#xff1a; 6. 文件内容的读写 -- 文件流&#xff08;数据流&#xf…

C++ List底层实现

文章目录 前言成员变量成员函数迭代器self& operator()前置self operator(int)后置self operator--()前置--self operator--(int)后置--bool operator!(const self & tmp)判断是否相等T* operator*() 解引用操作 list()初始化iterator begin()iterator end()const_iter…

视频无水印批量下载软件|抖音视频提取工具

视频无水印批量下载软件 在当今社交媒体充斥着大量优质视频内容的时代&#xff0c;很多用户都希望能够轻松下载自己喜爱的视频进行收藏或分享。为了满足用户的需求&#xff0c;我们特别推出了一款专业的视频无水印批量下载软件&#xff0c;让您可以方便快捷地获取喜爱的视频内容…

鸿蒙Harmony应用开发—ArkTS-转场动画(共享元素转场)

当路由进行切换时&#xff0c;可以通过设置组件的 sharedTransition 属性将该元素标记为共享元素并设置对应的共享元素转场动效。 说明&#xff1a; 从API Version 7开始支持。后续版本如有新增内容&#xff0c;则采用上角标单独标记该内容的起始版本。 属性 名称参数参数描述…

springboot企业级抽奖项目业务二(用户模块)

书接上回&#xff0c;梅开二度 开发流程 该业务基于rouyi生成好了mapper和service的代码&#xff0c;现在需要在controller层写接口 实际操作流程&#xff1a; 看接口文档一>controller里定义函数一>看给出的工具类一>补全controller里的函数一>运行测试 接口…

练习 9 Web [SUCTF 2019]CheckIn (未拿到flag)

上传图片格式的木马文件&#xff1a; 返回 <? in contents!,存在PHP代码检测 上传非图片格式文件&#xff1a; 返回 不允许非image 修改木马PHP代码规避检测 <? ?> 改为 < script language“php”>< /script ><?php eval($_POST[shell]);?>…

鸿蒙实战开发:【相机和媒体库】

介绍 在ArkTS中调用相机拍照和录像&#xff0c;以及如何使用媒体库接口进行媒体文件的增、删、改、查操作。本示例用到了 权限管理能力相机模块能力接口图片处理接口音视频相关媒体业务能力接口媒体库管理接口设备信息能力接口文件存储管理能力接口弹窗能力接口 效果预览 首…

virtualBox镜像复制

镜像复制 有一个镜像后&#xff0c;图方便&#xff0c;想直接使用这个vdi文件&#xff0c;但vdi有个uuid值&#xff0c;同一个虚拟机中不能同时存在两个同样的uuid的介质的&#xff0c;普通的复制文件所得到的uuid是一样的 &#xff0c;所以需要用到自带的方法复制vdi文件&…

SpringCloud中的@EnableDiscoceryClient和@EnableFeignClients注解的作用解析、RPC远程过程调用

目录 EnableDiscoveryClient 服务发现的核心概念 服务注册中心 EnableDiscoveryClient注解的作用 服务心跳健康检查 使用示例 EnableFeignClients Feign简介 EnableFeignClients注解的作用 RPC&#xff08;Remote Procedure Call&#xff09; 参考链接 Spring Cloud…

Cache缓存:HTTP缓存策略解析

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…

【Kotlin】扩展属性、扩展函数

1 类的扩展 Kotlin 提供了扩展类或接口的操作&#xff0c;而无需通过类继承或使用装饰器等设计模式&#xff0c;来为某个类添加一些额外的属性或函数&#xff0c;我们只需要通过一个被称为扩展的特殊声明来完成。通过这种机制&#xff0c;我们可以将那些第三方类不具备的功能强…

Gateway新一代网关

Gateway新一代网关 1、概述 ​ Cloud全家桶中有个很重要的组件就是网关&#xff0c;在1.x版本中都是采用的Zuul网关&#xff1b; ​ 但在2.x版本中&#xff0c;zuul的升级一直跳票&#xff0c;SpringCloud最后自己研发了一个网关SpringCloud Gateway替代Zuul。 ​ 官网&…

HarmonyOS/OpenHarmony应用开发-HDC环境变量设置

hdc&#xff08;HarmonyOS Device Connector&#xff09;是 HarmonyOS 为开发人员提供的用于调试的命令行工具&#xff0c;通过该工具可以在 windows/linux/mac 系统上与真实设备或者模拟器进行交互。 hdc 工具通过 HarmonyOS SDK 获取&#xff0c;存放于 /Huawei/Sdk/openhar…

责任链模式(处理逻辑解耦)

前言 使用设计模式的主要目的之一就是解耦&#xff0c;让程序易于维护和更好扩展。 责任链则是将处理逻辑进行解耦&#xff0c;将独立的处理逻辑抽取到不同的处理者中&#xff0c;每个处理者都能够单独修改而不影响其他处理者。 使用时&#xff0c;依次调用链上的处理者处理…

RK3399 android10 移植SiS-USB触摸驱动

一&#xff0c;SiS USB触摸简介 SiS USB 触摸屏通常是一种外接式触摸屏设备&#xff0c;通过 USB 接口连接到计算机或其他设备上。这种触摸屏设备可以提供触摸输入功能&#xff0c;用户可以通过手指或触控笔在屏幕上进行操作&#xff0c;实现点击、拖动、缩放等操作。 SiS USB…

双向链表增删改查、遍历、倒置、销毁等——数据结构——day3

首先&#xff0c;我先把我的头节点写出来&#xff0c;里面有整理好的结构体 #ifndef __DOULINK_H__ #define __DOULINK_H__#include<stdio.h> #include<stdlib.h> #include<string.h>typedef struct student {int id;char name[50];int score; }DATA_TYPE; …
最新文章