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;
​@Overridepublic void registerObserver(WeatherObserver observer) {observers.add(observer);}
​@Overridepublic void removeObserver(WeatherObserver observer) {observers.remove(observer);}
​@Overridepublic 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;}
​@Overridepublic 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("晴天");
​// 移除观察者2weatherStation.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);}
​@Overridepublic 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;}
​@Overridepublic void sendMessage(String message) {mediator.sendMessage(message, this);}
​@Overridepublic 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;}
​@Overridepublic 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;}
​@Overridepublic int interpret() {if (operator == '+') {return leftOperand.interpret() + rightOperand.interpret();} else if (operator == '-') {return leftOperand.interpret() - rightOperand.interpret();}// Handle other operators if neededreturn 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 {@Overridepublic 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 {@Overridepublic void draw(LotteryContext context) {// 已经中奖了,不能再抽奖System.out.println("你已经中过奖了,请等待下一次抽奖。");}
}
​
// 未中奖状态
class NotWinnerState implements LotteryState {@Overridepublic 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 {@Overridepublic double applyDiscount(double totalPrice) {return totalPrice;}
}
​
// 第二个具体策略类:固定折扣
class FixedDiscountStrategy implements DiscountStrategy {private double discountRate;
​public FixedDiscountStrategy(double discountRate) {this.discountRate = discountRate;}
​@Overridepublic 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;}
​@Overridepublic 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<>() {@Overridepublic 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;}
​@Overridepublic 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;}
​@Overridepublic 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 {@Overridepublic 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.mzph.cn/news/759495.shtml

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

相关文章

【百度灵境矩阵实训营】操作指南

【百度灵境矩阵实训营】操作指南 写在最前面提交注意事项比赛参与指南1、创建智能体作品要求 2、提交作品 学习资料包 &#x1f308;你好呀&#xff01;我是 是Yu欸 &#x1f30c; 2024每日百字篆刻时光&#xff0c;感谢你的陪伴与支持 ~ &#x1f680; 欢迎一起踏上探险之旅&…

Docker容器化技术(docker-compose示例:部署discuz论坛和wordpress博客,使用adminer管理数据库)

安装docker-compose [rootservice ~]# systemctl stop firewalld [rootservice ~]# setenforce 0 [rootservice ~]# systemctl start docker[rootservice ~]# wget https://github.com/docker/compose/releases/download/v2.5.0/docker-compose-linux-x86_64创建目录 [rootse…

用纯C语言实现空气压强和电压关系

已知P是真空度&#xff0c;U是电压&#xff0c;C是3.572&#xff08;以PA为单位&#xff09;&#xff0c;P10**(U-C)/1.286 1、使用 math.h 头文件中提供的函数&#xff0c;比如 pow() 来计算幂 #include <stdio.h> #include <math.h>int main() {double U; // 电…

接口测试、postman、测试点提取【主】

接口测试是测试系统组件间接口的一种测试 接口测试主要用于检测外部系统与系统之间以及内部各个子系统之间的交互点 测试的重点是要检查数据的交换&#xff0c;传递和控制管理过程&#xff0c;以及系统间的相互逻辑依赖关系 文章目录 HTTP接口 & Web Service接口RESTful接口…

SQLiteC/C++接口详细介绍sqlite3_stmt类(五)

返回&#xff1a;SQLite—系列文章目录 上一篇&#xff1a;SQLiteC/C接口详细介绍sqlite3_stmt类&#xff08;四&#xff09;- 下一篇&#xff1a; 无 12. sqlite3_bind_text16函数 sqlite3_bind_text16函数用于将UTF-16编码的文本数据&#xff08;字符串&#xff09;绑定…

语音识别教程:Whisper

语音识别教程&#xff1a;Whisper 一、前言 最近看国外教学视频的需求&#xff0c;有些不是很适应&#xff0c;找了找AI字幕效果也不是很好&#xff0c;遂打算基于Whisper和GPT做一个AI字幕给自己。 二、具体步骤 1、安装FFmpeg Windows: 进入 https://github.com/BtbN/FF…

[嵌入式系统-43]:SOC芯片内部的总线规范

目录 一、SOC&#xff08;System on Chip&#xff09; 二、AMBA 2.1 AMBA概述 2.2 AMBA架构 三、AXI 2.1 AXI概述 一、SOC&#xff08;System on Chip&#xff09; SOC&#xff08;System on Chip&#xff09;通常包含多个功能模块&#xff0c;如处理器核心、内存控制器…

综合知识篇16-信息系统基础知识考点(2024年软考高级系统架构设计师冲刺知识点总结系列文章)

专栏系列文章: 2024高级系统架构设计师备考资料(高频考点&真题&经验)https://blog.csdn.net/seeker1994/category_12593400.html案例分析篇00-【历年案例分析真题考点汇总】与【专栏文章案例分析高频考点目录】(2024年软考高级系统架构设计师冲刺知识点总结-案例…

基于SpringBoot和Vue的网上订餐系统的设计与实现

今天要和大家聊的是一款基于SpringBoot和Vue的网上订餐系统的设计与实现。 &#xff01;&#xff01;&#xff01; 有需要的小伙伴可以通过文章末尾名片咨询我哦&#xff01;&#xff01;&#xff01; &#x1f495;&#x1f495;作者&#xff1a;李同学 &#x1f495;&#x…

nginx介绍及搭建

架构模型 Nginx是由一个master管理进程、多个worker进程组成的多进程模型。master负责管理worker进程&#xff0c;worker进程负责处理网络事件&#xff0c;整个框架被设计为一种依赖事件驱动、异步、非阻塞的模式。 优势&#xff1a; 1、充分利用多核&#xff0c;增强并发处理…

[隐私计算实训营学习笔记] 第1讲 数据要素流通

信任四基石 数据的分级分类 技术信任&#xff1a;全链路审计、闭环完成的数据可信流通体系 技术信任&#xff1a;开启数据密态时代 数据可流通的基础设施&#xff1a;密态天空计算

LeetCode 面试经典150题 罗马数字转整数

题目&#xff1a; 罗马数字包含以下七种字符: I&#xff0c; V&#xff0c; X&#xff0c; L&#xff0c;C&#xff0c;D 和 M。 字符 数值 I 1 V 5 X 10 L 50 C 100 D 500 M …

ZnO非线性电阻片功率损耗的频率特性

对不同频率正弦交流电压作用下直流氧化锌阀片的功率损耗进行了测量。图3.16(a)和(b)分别为试品类型A和试品类型B两种直流ZnO阀片在不同频率电压作用下的功率损耗随荷电率变化的特性。两种阀片的功率损耗都随频率和荷电率的增加而增加。当频率达到1kHz或以上时,阀片的功率损耗增…

Django项目不显示图片,路径找不到

1.问题 创建Django项目简单写一个网页&#xff0c;文字能显示&#xff0c;图片却无法加载&#xff0c;路径错误&#xff0c;找不到图片。 2.背景 我的项目结构 C:. ├─.idea │ └─inspectionProfiles ├─app01 │ ├─migrations │ ├─templates │ │ ├─app0…

JVM垃圾收集器你会选择吗?

目录 一、Serial收集器 二、ParNew收集器 三、Paralle Scavenge 四、Serial Old 五、Parallel Old 六、CMS收集器 6.1 CMS对处理器资源非常敏感 6.2 CMS容易出现浮动垃圾 6.3 产生内存碎片 七、G1 收集器 八、如何选择合适的垃圾收集器 JVM 垃圾收集器是Java虚…

玩转C语言——C语言中内存存储

一、 整数在内存中的存储 我们知道&#xff1a;整数的2进制表⽰⽅法有三种&#xff0c;即 原码、反码和补码 三种表⽰⽅法均有符号位和数值位两部分&#xff0c;符号位都是⽤0表⽰“正”&#xff0c;⽤1表⽰“负”&#xff0c;⽽数值位最 ⾼位的⼀位是被当做符号位&#xff0c;…

【Java】使用 Java 语言实现一个冒泡排序

大家好&#xff0c;我是全栈小5&#xff0c;欢迎阅读小5的系列文章。 这是《Java》系列文章&#xff0c;每篇文章将以博主理解的角度展开讲解&#xff0c; 特别是针对知识点的概念进行叙说&#xff0c;大部分文章将会对这些概念进行实际例子验证&#xff0c;以此达到加深对知识…

【动态三维重建】Deformable 3D Gaussians 可变形3D GS用于单目动态场景重建(CVPR 2024)

主页&#xff1a;https://ingra14m.github.io/Deformable-Gaussians/ 代码&#xff1a;https://github.com/ingra14m/Deformable-3D-Gaussians 论文&#xff1a;https://arxiv.org/abs/2309.13101 文章目录 摘要一、前言二、相关工作2.1 动态场景的神经渲染2.2 神经渲染加速 三…

【Web应用技术基础】HTML(3)——表格

目录 题目1&#xff1a;原始表格 题目2&#xff1a;width、height 题目3&#xff1a; cellpadding 题目4&#xff1a;cellspacing、cellpadding 题目5&#xff1a;caption 题目6&#xff1a;rowspan 题目7&#xff1a;colspan 题目8&#xff1a;汇总题 题目1&#xff1…

从零到一构建短链接系统(八)

1.git上传远程仓库&#xff08;现在才想起来&#xff09; git init git add . git commit -m "first commit" git remote add origin OLiyscxm/shortlink git push -u origin "master" 2.开发全局异常拦截器之后就可以简化UserController 拦截器可以…