Leetcode-设计模式-行为型模式

文章目录

行为型模式

一、责任链模式(责任链主要用于处理 职责相同,程度不同的类)

责任链模式的优点:

  • 降低对象之间的耦合
  • 扩展性强,符合开闭原则
  • 灵活性强
  • 简化对象之间的连接。每个对象只需保持一个指向其后继者的引用,不需保持其他所有处理者的引用

缺点:

  • 如果责任链过长,请求的处理可能涉及多个处理对象,系统性能将受到一定影响。
  • 不能保证每个请求一定被处理,该请求可能一直传到链的末端都得不到处理。

在这里插入图片描述

例子

新建一个程序员抽象类:

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() 方法,让外部类使用这两个方法来遍历列表,以达到隐藏列表内部细节的目的
  • 中介者模式:通过引入中介者,将网状耦合结构变成星型结构
  • 备忘录模式:存储对象的状态,以便恢复
  • 观察者模式:处理一对多的依赖关系,被观察的对象改变时,多个观察者都能收到通知
  • 状态模式:关于多态的设计模式,每个状态类处理对象的一种状态
  • 策略模式:殊途同归,用多种方法做同一件事
  • 模板方法模式:关于继承的设计模式,父类是子类的模板
  • 访问者模式:将数据的结构和对数据的操作分离
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值