文章目录
- 行为型模式
- 一、责任链模式(责任链主要用于处理 职责相同,程度不同的类)
- 二、命令模式(实现同一个接口,通过多态的方式调用方法,实现不同的命令功能)
- 三、解释器模式(若问题发生的频率足够高,那么可能就值得将该问题的各个实例表述为一个简单语言中的句子。这样就可以构建一个解释器,该解释器通过解释这些句子来解决该问题)
- 四、迭代器模式(提供一种方法访问一个容器对象中各个元素,而又不需暴露该对象的内部细节)
- 五、中介者模式(用一个中介对象来封装一系列的对象交互,中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互)
- 六、备忘录模式(在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态)
- 七、观察者模式(定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新)
- 八、状态模式(对象的行为依赖于它的状态(属性),并且可以根据它的状态改变而改变它的相关行为。)
- 九、策略模式(定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换。)
- 十、模板方法模式(模板方法模式就是一个关于继承的设计模式,主要解决一些方法通用,却在每一个子类都重新写了这一方法)
- 十一、访问者模式
- 总结
行为型模式
一、责任链模式(责任链主要用于处理 职责相同,程度不同的类)
责任链模式的优点:
- 降低对象之间的耦合
- 扩展性强,符合开闭原则
- 灵活性强
- 简化对象之间的连接。每个对象只需保持一个指向其后继者的引用,不需保持其他所有处理者的引用
缺点:
- 如果责任链过长,请求的处理可能涉及多个处理对象,系统性能将受到一定影响。
- 不能保证每个请求一定被处理,该请求可能一直传到链的末端都得不到处理。
例子
新建一个程序员抽象类:
public abstract class Programmer {
protected Programmer next;
public void setNext(Programmer next) {
this.next = next;
}
abstract void handle(Bug bug);
}
在这个抽象类中:
- next 对象表示如果自己解决不了,需要将责任传递给的下一个人;
- handle 方法表示自己处理此 bug 的逻辑,在这里判断是自己解决或者继续传递。
新建菜鸟程序员类:
public class NewbieProgrammer extends Programmer {
@Override
public void handle(Bug bug) {
if (bug.value > 0 && bug.value <= 20) {
solve(bug);
} else if (next != null) {
next.handle(bug);
}
}
private void solve(Bug bug) {
System.out.println("菜鸟程序员解决了一个难度为 " + bug.value + " 的 bug");
}
}
新建普通程序员类:
public class NormalProgrammer extends Programmer {
@Override
public void handle(Bug bug) {
if (bug.value > 20 && bug.value <= 50) {
solve(bug);
} else if (next != null) {
next.handle(bug);
}
}
private void solve(Bug bug) {
System.out.println("普通程序员解决了一个难度为 " + bug.value + " 的 bug");
}
}
新建优秀程序员类:
public class GoodProgrammer extends Programmer {
@Override
public void handle(Bug bug) {
if (bug.value > 50 && bug.value <= 100) {
solve(bug);
} else if (next != null) {
next.handle(bug);
}
}
private void solve(Bug bug) {
System.out.println("优秀程序员解决了一个难度为 " + bug.value + " 的 bug");
}
}
客户端测试:
import org.junit.Test;
public class Client4 {
@Test
public void test() {
NewbieProgrammer newbie = new NewbieProgrammer();
NormalProgrammer normal = new NormalProgrammer();
GoodProgrammer good = new GoodProgrammer();
Bug easy = new Bug(20);
Bug middle = new Bug(50);
Bug hard = new Bug(100);
// 组成责任链
newbie.setNext(normal);
normal.setNext(good);
// 从菜鸟程序员开始,沿着责任链传递
newbie.handle(easy);
newbie.handle(middle);
newbie.handle(hard);
}
}
二、命令模式(实现同一个接口,通过多态的方式调用方法,实现不同的命令功能)
请求以命令的形式包裹在对象中,并传给调用对象。调用对象寻找可以处理该命令的合适的对象,并把该命令传给相应的对象,该对象执行命令。
命令模式的主要优点如下:
- 降低系统的耦合度。将 “行为请求者” 和 ”行为实现者“ 解耦。
- 扩展性强。增加或删除命令非常方便,并且不会影响其他类。
- 封装 “方法调用”,方便实现 Undo 和 Redo 操作。
- 灵活性强,可以实现宏命令。
它的主要缺点是:
- 会产生大量命令类。增加了系统的复杂性。
例子
创建一个命令接口。
Order.java
public interface Order {
void execute();
}
步骤 2
创建一个请求类。
Stock.java
public class Stock {
private String name = "ABC";
private int quantity = 10;
public void buy(){
System.out.println("Stock [ Name: "+name+",
Quantity: " + quantity +" ] bought");
}
public void sell(){
System.out.println("Stock [ Name: "+name+",
Quantity: " + quantity +" ] sold");
}
}
步骤 3
创建实现了 Order 接口的实体类。
BuyStock.java
public class BuyStock implements Order {
private Stock abcStock;
public BuyStock(Stock abcStock){
this.abcStock = abcStock;
}
public void execute() {
abcStock.buy();
}
}
SellStock.java
public class SellStock implements Order {
private Stock abcStock;
public SellStock(Stock abcStock){
this.abcStock = abcStock;
}
public void execute() {
abcStock.sell();
}
}
步骤 4
创建命令调用类。
Broker.java
import java.util.ArrayList;
import java.util.List;
public class Broker {
private List<Order> orderList = new ArrayList<Order>();
public void takeOrder(Order order){
orderList.add(order);
}
public void placeOrders(){
for (Order order : orderList) {
order.execute();
}
orderList.clear();
}
}
步骤 5
使用 Broker 类来接受并执行命令。
CommandPatternDemo.java
public class CommandPatternDemo {
public static void main(String[] args) {
Stock abcStock = new Stock();
BuyStock buyStockOrder = new BuyStock(abcStock);
SellStock sellStockOrder = new SellStock(abcStock);
Broker broker = new Broker();
broker.takeOrder(buyStockOrder);
broker.takeOrder(sellStockOrder);
broker.placeOrders();
}
}
步骤 6
执行程序,输出结果:
Stock [ Name: ABC, Quantity: 10 ] bought
Stock [ Name: ABC, Quantity: 10 ] sold
我们期待能有一种设计,让遥控器不需要知道家居的接口。它只需要负责监听用户按下开关,再根据开关状态发出正确的命令,对应的家居在收到命令后做出响应。就可以达到将 “行为请求者” 和 ”行为实
三、解释器模式(若问题发生的频率足够高,那么可能就值得将该问题的各个实例表述为一个简单语言中的句子。这样就可以构建一个解释器,该解释器通过解释这些句子来解决该问题)
面向过程编程会有耦合度高,不易扩展等缺点
如何解决:构建语法树,定义终结符与非终结符。
在解释器模式中,我们将不可拆分的最小单元称之为终结表达式,可以被拆分的表达式称之为非终结表达式。
解释器模式中常用到递归,递归解析语句。
使用场景:
- 1、可以将一个需要解释执行的语言中的句子表示为一个抽象语法树。
- 2、一些重复出现的问题可以用一种简单的语言来进行表达。
- 3、一个简单语法需要解释的场景。
例子
步骤 1
创建一个表达式接口。
public interface Expression {
public boolean interpret(String context);
}
步骤 2
创建实现了上述接口的实体类。
TerminalExpression.java
public class TerminalExpression implements Expression {
private String data;
public TerminalExpression(String data){
this.data = data;
}
@Override
public boolean interpret(String context) {
if(context.contains(data)){
return true;
}
return false;
}
}
OrExpression.java
public class OrExpression implements Expression {
private Expression expr1 = null;
private Expression expr2 = null;
public OrExpression(Expression expr1, Expression expr2) {
this.expr1 = expr1;
this.expr2 = expr2;
}
@Override
public boolean interpret(String context) {
return expr1.interpret(context) || expr2.interpret(context);
}
}
AndExpression.java
public class AndExpression implements Expression {
private Expression expr1 = null;
private Expression expr2 = null;
public AndExpression(Expression expr1, Expression expr2) {
this.expr1 = expr1;
this.expr2 = expr2;
}
@Override
public boolean interpret(String context) {
return expr1.interpret(context) && expr2.interpret(context);
}
}
步骤 3
InterpreterPatternDemo 使用 Expression 类来创建规则,并解析它们。
InterpreterPatternDemo.java
public class InterpreterPatternDemo {
//规则:Robert 和 John 是男性
public static Expression getMaleExpression(){
Expression robert = new TerminalExpression("Robert");
Expression john = new TerminalExpression("John");
return new OrExpression(robert, john);
}
//规则:Julie 是一个已婚的女性
public static Expression getMarriedWomanExpression(){
Expression julie = new TerminalExpression("Julie");
Expression married = new TerminalExpression("Married");
return new AndExpression(julie, married);
}
public static void main(String[] args) {
Expression isMale = getMaleExpression();
Expression isMarriedWoman = getMarriedWomanExpression();
System.out.println("John is male? " + isMale.interpret("John"));
System.out.println("Julie is a married women? "
+ isMarriedWoman.interpret("Married Julie"));
}
}
步骤 4
执行程序,输出结果:
John is male? true
Julie is a married women? true
四、迭代器模式(提供一种方法访问一个容器对象中各个元素,而又不需暴露该对象的内部细节)
五、中介者模式(用一个中介对象来封装一系列的对象交互,中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互)
何时使用:多个类相互耦合,形成了网状结构。
如何解决:将上述网状结构分离为星型结构。
例子
创建中介类。
ChatRoom.java
import java.util.Date;
public class ChatRoom {
public static void showMessage(User user, String message){
System.out.println(new Date().toString()
+ " [" + user.getName() +"] : " + message);
}
}
步骤 2
创建 user 类。
User.java
public class User {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public User(String name){
this.name = name;
}
public void sendMessage(String message){
ChatRoom.showMessage(this,message);
}
}
步骤 3
使用 User 对象来显示他们之间的通信。
MediatorPatternDemo.java
public class MediatorPatternDemo {
public static void main(String[] args) {
User robert = new User("Robert");
User john = new User("John");
robert.sendMessage("Hi! John!");
john.sendMessage("Hello! Robert!");
}
}
步骤 4
执行程序,输出结果:
Thu Jan 31 16:05:46 IST 2013 [Robert] : Hi! John!
Thu Jan 31 16:05:46 IST 2013 [John] : Hello! Robert!
六、备忘录模式(在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态)
主要解决:所谓备忘录模式就是在不破坏封装的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样可以在以后将对象恢复到原先保存的状态。
如何解决:通过一个备忘录类专门存储对象状态。
例子
备忘录模式使用三个类 Memento、Originator 和 CareTaker。Memento 包含了要被恢复的对象的状态。Originator 创建并在 Memento 对象中存储状态。Caretaker 对象负责从 Memento 中恢复对象的状态。
创建 Memento 类。
Memento.java
public class Memento {
private String state;
public Memento(String state){
this.state = state;
}
public String getState(){
return state;
}
}
步骤 2
创建 Originator 类。
Originator.java
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();
}
}
步骤 3
创建 CareTaker 类。
CareTaker.java
import java.util.ArrayList;
import java.util.List;
public class CareTaker {
private List<Memento> mementoList = new ArrayList<Memento>();
public void add(Memento state){
mementoList.add(state);
}
public Memento get(int index){
return mementoList.get(index);
}
}
步骤 4
使用 CareTaker 和 Originator 对象。
MementoPatternDemo.java
public class MementoPatternDemo {
public static void main(String[] args) {
Originator originator = new Originator();
CareTaker careTaker = new CareTaker();
originator.setState("State #1");
originator.setState("State #2");
careTaker.add(originator.saveStateToMemento());
originator.setState("State #3");
careTaker.add(originator.saveStateToMemento());
originator.setState("State #4");
System.out.println("Current State: " + originator.getState());
originator.getStateFromMemento(careTaker.get(0));
System.out.println("First saved State: " + originator.getState());
originator.getStateFromMemento(careTaker.get(1));
System.out.println("Second saved State: " + originator.getState());
}
}
步骤 5
验证输出。
Current State: State #4
First saved State: State #2
Second saved State: State #3
七、观察者模式(定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新)
观察者的接口:
Java
public interface Observer {
void update(String event);
}
接口中只有一个 update 方法,用于对被观察者发出的事件做出响应。
被观察者的父类:
Java
public class Observable {
private List<Observer> observers = new ArrayList<>();
public void addObserver(Observer observer) {
observers.add(observer);
}
public void removeObserver(Observer observer) {
observers.remove(observer);
}
public void notifyObservers(String event) {
for (Observer observer : observers) {
observer.update(event);
}
}
}
被观察者中维护了一个观察者列表,提供了三个方法:
addObserver:将 observer 对象添加到观察者列表中
removeObserver:将 observer 对象从观察者列表中移除
notifyObservers:通知所有观察者有事件发生,具体实现是调用所有观察者的 update 方法
有了这两个基类,我们就可以定义出具体的罪犯与警察类。
警察属于观察者:
Java
public class PoliceObserver implements Observer {
@Override
public void update(String event) {
System.out.println("警察收到消息,罪犯在" + event);
}
}
警察实现了观察者接口,当警察收到事件后,做出响应,这里的响应就是简单的打印了一条日志。
罪犯属于被观察者:
Java
public class CriminalObservable extends Observable {
public void crime(String event) {
System.out.println("罪犯正在" + event);
notifyObservers(event);
}
}
罪犯继承自被观察者类,当罪犯有犯罪行为时,所有的观察者都会收到通知。
客户端测试:
Java
public class Client {
@Test
public void test() {
CriminalObservable zhangSan = new CriminalObservable();
PoliceObserver police1 = new PoliceObserver();
PoliceObserver police2 = new PoliceObserver();
PoliceObserver police3 = new PoliceObserver();
zhangSan.addObserver(police1);
zhangSan.addObserver(police2);
zhangSan.addObserver(police3);
zhangSan.crime("放狗咬人");
}
}
在客户端中,我们 new 了一个张三,为其添加了三个观察者:police1,police2,police3。
运行程序,输出如下:
罪犯正在放狗咬人
警察收到消息,罪犯在放狗咬人
警察收到消息,罪犯在放狗咬人
警察收到消息,罪犯在放狗咬人
八、状态模式(对象的行为依赖于它的状态(属性),并且可以根据它的状态改变而改变它的相关行为。)
主要解决:对象的行为依赖于它的状态(属性),并且可以根据它的状态改变而改变它的相关行为。
何时使用:代码中包含大量与对象状态有关的条件语句。
如何解决:将各种具体的状态类抽象出来。
例子
创建一个接口。
State.java
public interface State {
public void doAction(Context context);
}
步骤 2
创建实现接口的实体类。
StartState.java
public class StartState implements State {
public void doAction(Context context) {
System.out.println("Player is in start state");
context.setState(this);
}
public String toString(){
return "Start State";
}
}
StopState.java
public class StopState implements State {
public void doAction(Context context) {
System.out.println("Player is in stop state");
context.setState(this);
}
public String toString(){
return "Stop State";
}
}
步骤 3
创建 Context 类。
Context.java
public class Context {
private State state;
public Context(){
state = null;
}
public void setState(State state){
this.state = state;
}
public State getState(){
return state;
}
}
步骤 4
使用 Context 来查看当状态 State 改变时的行为变化。
StatePatternDemo.java
public class StatePatternDemo {
public static void main(String[] args) {
Context context = new Context();
StartState startState = new StartState();
startState.doAction(context);
System.out.println(context.getState().toString());
StopState stopState = new StopState();
stopState.doAction(context);
System.out.println(context.getState().toString());
}
}
步骤 5
执行程序,输出结果:
Player is in start state
Start State
Player is in stop state
Stop State
九、策略模式(定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换。)
何时使用:一个系统有许多许多类,而区分它们的只是他们直接的行为。
如何解决:将这些算法封装成一个一个的类,任意地替换。
步骤 1
创建一个接口。
Strategy.java
public interface Strategy {
public int doOperation(int num1, int num2);
}
步骤 2
创建实现接口的实体类。
OperationAdd.java
public class OperationAdd implements Strategy{
@Override
public int doOperation(int num1, int num2) {
return num1 + num2;
}
}
OperationSubtract.java
public class OperationSubtract implements Strategy{
@Override
public int doOperation(int num1, int num2) {
return num1 - num2;
}
}
OperationMultiply.java
public class OperationMultiply implements Strategy{
@Override
public int doOperation(int num1, int num2) {
return num1 * num2;
}
}
步骤 3
创建 Context 类。
Context.java
public class Context {
private Strategy strategy;
public Context(Strategy strategy){
this.strategy = strategy;
}
public int executeStrategy(int num1, int num2){
return strategy.doOperation(num1, num2);
}
}
步骤 4
使用 Context 来查看当它改变策略 Strategy 时的行为变化。
StrategyPatternDemo.java
public class StrategyPatternDemo {
public static void main(String[] args) {
Context context = new Context(new OperationAdd());
System.out.println("10 + 5 = " + context.executeStrategy(10, 5));
context = new Context(new OperationSubtract());
System.out.println("10 - 5 = " + context.executeStrategy(10, 5));
context = new Context(new OperationMultiply());
System.out.println("10 * 5 = " + context.executeStrategy(10, 5));
}
}
步骤 5
执行程序,输出结果:
10 + 5 = 15
10 - 5 = 5
10 * 5 = 50
十、模板方法模式(模板方法模式就是一个关于继承的设计模式,主要解决一些方法通用,却在每一个子类都重新写了这一方法)
例子
请假模板
这个模板用代码表示如下:
Java
abstract class LeaveRequest {
void request() {
System.out.print("本人");
System.out.print(name());
System.out.print("因");
System.out.print(reason());
System.out.print("需请假");
System.out.print(duration());
System.out.print("天,望批准");
}
abstract String name();
abstract String reason();
abstract String duration();
}
在这份模板中,所有的其他步骤(固定字符串)都是稳定的,只有姓名、请假原因、请假时长是抽象的,需要延迟到子类去实现。
继承此模板,实现具体步骤的子类:
Java
class MyLeaveRequest extends LeaveRequest {
@Override
String name() {
return "阿笠";
}
@Override
String reason() {
return "参加力扣周赛";
}
@Override
String duration() {
return "0.5";
}
}
测试:
Java
// 输出:本人阿笠因参加力扣周赛需请假0.5天,望批准
new MyLeaveRequest().request();
在使用模板方法模式时,我们可以为不同的模板方法设置不同的控制权限:
如果不希望子类覆写模板中的某个方法,使用 final 修饰此方法;
如果要求子类必须覆写模板中的某个方法,使用 abstract 修饰此方法;
如果没有特殊要求,可使用 protected 或 public 修饰此方法,子类可根据实际情况考虑是否覆写。
十一、访问者模式
总结
- 责任链模式:处理职责相同,程度不同的对象,使其在一条链上传递
- 命令模式:封装“方法调用”,将行为请求者和行为实现者解耦
- 解释器模式:定义自己的语法规则
- 迭代器模式:定义 next() 方法和 hasNext() 方法,让外部类使用这两个方法来遍历列表,以达到隐藏列表内部细节的目的
- 中介者模式:通过引入中介者,将网状耦合结构变成星型结构
- 备忘录模式:存储对象的状态,以便恢复
- 观察者模式:处理一对多的依赖关系,被观察的对象改变时,多个观察者都能收到通知
- 状态模式:关于多态的设计模式,每个状态类处理对象的一种状态
- 策略模式:殊途同归,用多种方法做同一件事
- 模板方法模式:关于继承的设计模式,父类是子类的模板
- 访问者模式:将数据的结构和对数据的操作分离