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

news2024/12/24 9:06:18

观察者模式

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

介绍

观察者模式:对象之间多对一依赖的一种设计方案,被依赖的对象是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.coloradmin.cn/o/1532369.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

sdsl库编译安装和使用

1. 下载和编译 git clone https://github.com/simongog/sdsl-lite.git cd sdsl-lite# 建一个conda环境 激活环境&#xff0c;安装cmake。 ./install.sh /usr/local/ 2. 示例代码 #include <sdsl/suffix_arrays.hpp> #include <fstream>using namespace sdsl;int…

SpringCloud Gateway工作流程

Spring Cloud Gateway的工作流程 具体的流程&#xff1a; 用户发送请求到网关 请求断言&#xff0c;用户请求到达网关后&#xff0c;由Gateway Handler Mapping&#xff08;网关处理器映射&#xff09;进行Predicates&#xff08;断言&#xff09;&#xff0c;看一下哪一个符合…

抖音视频批量下载软件可导出视频分享链接|手机网页视频提取|视频爬虫采集工具

解锁抖音视频无水印批量下载新姿势&#xff01; 在快节奏的生活中&#xff0c;抖音作为时下最热门的短视频平台之一&#xff0c;吸引着广大用户的目光。而如何高效地获取喜欢的视频内容成为了许多人关注的焦点。Q:290615413现在&#xff0c;我们推出的抖音视频批量下载软件&…

前端知识点03(JS)

文章目录 前端知识点03&#xff08;JS&#xff09;1、JS中this指向问题2、script中的async和defer的区别3、setTimeOut和setInterval4、Es6和ES5的区别5、ES6的新特性 &#x1f389;写在最后 前端知识点03&#xff08;JS&#xff09; hello hello~ &#xff0c;这里是 code袁~&…

window下安装并使用nvm(含卸载node、卸载nvm、全局安装npm)

window下安装并使用nvm&#xff08;含卸载node、卸载nvm、全局安装npm&#xff09; 一、卸载node二、安装nvm三、配置路径和下载源四、使用nvm安装node五、nvm常用命令六、卸载nvm七、全局安装npm、cnpm八、遇到的问题 nvm 全名 node.js version management&#xff0c;顾名思义…

鸿蒙Harmony应用开发—ArkTS-高级组件:@ohos.arkui.advanced.ComposeTitleBar(头像和单双行文本标题栏)

一种普通标题栏&#xff0c;支持设置标题、头像&#xff08;可选&#xff09;和副标题&#xff08;可选&#xff09;&#xff0c;可用于一级页面、二级及其以上界面配置返回键。 说明&#xff1a; 该组件从API Version 10开始支持。后续版本如有新增内容&#xff0c;则采用上角…

【论文阅读】Masked Autoencoders Are Scalable Vision Learners

Masked Autoencoders Are Scalable Vision Learners 引用&#xff1a; He K, Chen X, Xie S, et al. Masked autoencoders are scalable vision learners[C]//Proceedings of the IEEE/CVF conference on computer vision and pattern recognition. 2022: 16000-16009. 论文链…

2024 Python3.10 系统入门+进阶(二):Python编程环境搭建

目录 一、Windows安装Python1.1 下载并安装 Python1.2 测试安装是否成功 二、Linux系统安装Python(新手可以跳过)2.1 基于RockyLinux系统安装Python(编译安装)2.2 基于Ubuntu系统安装Python(编译安装) 三、如何运行Python程序&#xff1f;3.1 Python 交互式编程3.2 编写Python源…

基于Spring Boot+Vue的社区医院管理系统

末尾获取源码作者介绍&#xff1a;大家好&#xff0c;我是墨韵&#xff0c;本人4年开发经验&#xff0c;专注定制项目开发 更多项目&#xff1a;CSDN主页YAML墨韵 学如逆水行舟&#xff0c;不进则退。学习如赶路&#xff0c;不能慢一步。 目录 一、项目简介 一、研究背景 二…

【FAQ】BSV区块链代码库常见问题解答

​​发表时间&#xff1a;2024年2月27日 BSV区块链协会上线了JavaScript和TypeScript SDK&#xff08;即“标准开发工具包”&#xff09;。TypeScript SDK旨在为开发者提供新版统一核心代码库&#xff0c;让开发者可以在BSV区块链上便捷地进行开发&#xff0c;尤其是开发那些可…

02分布式搜索引擎ES

elasticsearch查询 1.DSL查询文档1.1.DSL查询分类1.2.全文检索查询1.3.精准查询1.4.地理坐标查询1.5.复合查询 2.搜索结果处理2.1.排序2.2.分页2.3.高亮2.4.总结 3.RestClient查询文档3.1.快速入门3.2.match查询3.3.精确查询3.4.布尔查询3.5.排序、分页3.6.高亮 1.DSL查询文档 …

【开源】SpringBoot框架开发个人保险管理系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 登录注册模块2.2 保险档案模块2.3 保险订单模块2.4 保险理赔模块 三、系统展示四、核心代码4.1 查询保险产品4.2 新增保险预定4.3 订单支付4.4 新增理赔单4.5 查询保险理赔 五、免责说明 一、摘要 1.1 项目介绍 基于J…

ArcGIS Pro、R和INVEST:三位一体的生态系统服务评估框架

生态系统服务是指生态系统所形成的用于维持人类赖以生存和发展的自然环境条件与效用&#xff0c;是人类直接或间接从生态系统中得到的各种惠益。联合国千年生态系统评估&#xff08;Millennium ecosystem assessment&#xff0c;MA&#xff09;提出生态系统服务包括供给、调节、…

Go微服务实战——服务的监控与链路追踪(监控数据可视化)

链路追踪背景 对于早期系统或者服务来说&#xff0c;开发人员一般通过打日志的方式来进行埋点&#xff08;常用的数据采集方式&#xff09;&#xff0c;然后再根据日志系统和性能监控定位及分析问题。对于单体的应用通过日志系统完全可以定位到问题&#xff0c;从而排查异常。…

Docker系列

目录 练习&#xff1a;去DockerHub搜索并拉取一个Redis镜像 练习&#xff1a;去DockerHub搜索并拉取一个Redis镜像 目标&#xff1a; 1&#xff09;去DockerHub搜索Redis镜像 2&#xff09;查看Redis镜像的名称和版本 3&#xff09;利用docker pull命令拉取镜像 查看是否…

数据仓库的魅力及其在企业中的应用实践

数据仓库&#xff0c;这一创新性的概念来自于比尔恩门&#xff0c;从1980年代末提出以来&#xff0c;便凭借其独特的架构设计和强大的数据处理能力&#xff0c;在全球商业领域中掀起了一场革命。它不仅是解决企业海量数据存储和查询需求的关键技术&#xff0c;更是推动企业实现…

贵州省二级分类土地利用数据(矢量)

贵州省&#xff0c;地处中国西南腹地&#xff0c;地貌属于中国西南部高原山地&#xff0c;境内地势西高东低&#xff0c;自中部向北、东、南三面倾斜&#xff0c;平均海拔在1100米左右。贵州高原山地居多&#xff0c;素有“八山一水一分田”之说。全省地貌可概括分为&#xff1…

双向链表、单双向链表比较、双向链表的基本操作

我要成为嵌入式高手之3月20日数据结构第三天&#xff01;&#xff01; ———————————————————————————— 双向链表 双向链表与单向链表的区别&#xff1a;双向链表中的结点的指针域包含前驱结点的地址&#xff0c;而单向链表的结点中指针域只有后驱结…

PyTorch 深度学习(GPT 重译)(六)

十四、端到端结节分析&#xff0c;以及接下来的步骤 本章内容包括 连接分割和分类模型 为新任务微调网络 将直方图和其他指标类型添加到 TensorBoard 从过拟合到泛化 在过去的几章中&#xff0c;我们已经构建了许多对我们的项目至关重要的系统。我们开始加载数据&#xf…

RK3399 android10 移植SiS-USB触摸驱动

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