24种设计模式之行为型模式(上)-Java版

软件设计模式是前辈们代码设计经验的总结,可以反复使用。设计模式共分为3大类,创建者模式(6种)、结构型模式(7种)、行为型模式(11种),一共24种设计模式,软件设计一般需要满足7大基本原则。下面通过5章的学习一起来看看设计模式的魅力吧。

行为模式(11种):本质是描述类与对象协助完成单个对象无法完成的任务,以及怎么分配职责。

包括:模板方法、策略、命令、责任链、状态、观察者、中介者模式、迭代器、访问者、备忘录、解释器。

目录

1.模板方法模式

2.策略模式

3.命令模式

4.责任链模式

2.5.状态模式

2.6.观察者模式


1.模板方法模式

定义:定义一个算法骨架,将算法的一些步骤延迟到其子类中,使得子类不改变算法结构的情况下,重新定义该算法的特定步骤。

模板方法主要包含一下角色:

01.抽象类:由一个模板方法和多个基本方法组成,负责给出一个算法的轮廓与骨架。

02.具体子类:用于实现抽象类中的方法。

模板方法模式优点及使用场景:

优点:提高了代码的复用性,子类继承父类并重写方法,实现了反转控制,父类掉子类。

使用场景:整体算法步骤固定,部分的步骤不同,JDK中InputStreaam类就使用了模板方法模式。

下面通过模板方法模拟炒菜的过程:倒油、热油、倒数蔬菜、翻炒等步骤。

1.首先定义一个抽象类,抽象类中定义一个模板方法与基本方法(抽象的与具体的)。

/**
 * @author nuist__NJUPT
 * @ClassName AbstractClass
 * @description: 抽象类-定义模板方法与基本方法
 * @date 2024年02月01日
 */
public abstract class AbstractClass {

    // 模板方法的定义
    public final void cookProcess(){
        pourSoil();
        heatSoil();
        pourVegetable();
        pourSauce();
        fry();
    }
    public void pourSoil(){
        System.out.println("倒油");
    }

    public void heatSoil(){
        System.out.println("热油");
    }

    public abstract void pourVegetable() ;

    public abstract void pourSauce() ;

    public void fry(){
        System.out.println("炒菜直至菜熟了...");
    }
}

2.定义具体的子类,子类继承抽象类,并重写抽象方法,具体如下:

/**
 * @author nuist__NJUPT
 * @ClassName ConcreteClass_BaoCai
 * @description: 炒包菜子类
 * @date 2024年02月01日
 */
public class ConcreteClass_BaoCai extends AbstractClass {

    public void pourVegetable() {
        System.out.println("下锅的蔬菜是包菜");
    }

    public void pourSauce() {
        System.out.println("加入的调料是辣椒");
    }
}
/**
 * @author nuist__NJUPT
 * @ClassName ConcreteClass_CaiXin
 * @description: 炒菜心子类
 * @date 2024年02月01日
 */
public class ConcreteClass_CaiXin extends AbstractClass {


    public void pourVegetable() {
        System.out.println("下锅的蔬菜是菜心");
    }

    public void pourSauce() {
        System.out.println("加入的调料是蒜苗");
    }
}

3.定义测试类,测试模板方法模式。

/**
 * @author nuist__NJUPT
 * @ClassName Client
 * @description: 测试类
 * @date 2024年02月01日
 */
public class Client {
    public static void main(String[] args) {

        ConcreteClass_BaoCai concreteClass_baoCai = new ConcreteClass_BaoCai() ;
        concreteClass_baoCai.cookProcess();

        System.out.println("--------------------------------");

        ConcreteClass_CaiXin concreteClass_caiXin = new ConcreteClass_CaiXin() ;
        concreteClass_caiXin.cookProcess();
    }
}

2.策略模式

定义:策略模式定义了一系列算法,并把个每个算法封装起来,并把使用算法的责任与算法的实现进行分割,并委派不同的对象对算法进行管理。

策略模式的主要角色如下:

01.抽象策略:通常是一个接口或者一个抽象类。

02.具体策略:实现了抽象策略中定义的接口,提供了具体的算法实现。

03.环境类:持有策略类的引用,由测试类调用。

策略模式的优缺点及使用场景:

优点:具体的策略类之间可以自由切换,易于扩展。

缺点:客户端必须知道所有的策略类,策略模式会产生很多策略类,可以通过享元模式减少策略类。

使用场景:一个系统动态的在多种算法中选择其中的一种。Arrays.sort()方法就是应用了策略模式,根据传入的策略进行规则排序。

下面通过一个促销活动案例,来学习策略模式,其中,促销活动策略为抽象策略接口,其实现子类为具体的策略,促销员类为环境类,用于和客户端交互。

1.首先定义抽象策略接口。

/**
 * @author nuist__NJUPT
 * @InterfaceName Strategy
 * @description: 抽象策略
 * @date 2024年02月01日
 */
public interface Strategy {

    void show() ;
}

2.定义两个具体的策略类,实现抽象策略接口。

/**
 * @author nuist__NJUPT
 * @ClassName StrategyA
 * @description: 具体策略类
 * @date 2024年02月01日
 */
public class StrategyA implements Strategy {

    public void show() {
        System.out.println("买一送一");
    }
}
/**
 * @author nuist__NJUPT
 * @ClassName StrategyB
 * @description: 具体策略类
 * @date 2024年02月01日
 */
public class StrategyB implements Strategy{

    public void show() {
        System.out.println("满200减30");
    }

}

3.定义环境类,用于注入接口,并调用接口方法,用于和客户端进行交互。


/**
 * @author nuist__NJUPT
 * @ClassName SalesMan
 * @description 环境类
 * @date 2024年02月01日
 */
public class SalesMan {

    // 聚合策略对象
    private Strategy strategy ;

    public SalesMan(Strategy strategy) {
        this.strategy = strategy;
    }

    public Strategy getStrategy() {
        return strategy;
    }

    public void setStrategy(Strategy strategy) {
        this.strategy = strategy;
    }

    public void show(){
        strategy.show();
    }
    
}

4.最后定义测试类,测试策略模式。


/**
 * @author nuist__NJUPT
 * @ClassName Main
 * @description: 测试类
 * @date 2024年02月01日
 */
public class Main {
    public static void main(String[] args) {
        SalesMan salesMan = new SalesMan(new StrategyA()) ;
        salesMan.show();

        System.out.println("==============================");

        salesMan.setStrategy(new StrategyB());
        salesMan.show();
    }
}
3.命令模式

定义:将一个请求封装成一个对象,将发出请求与执行请求分割开,这样可以使得两者通过命令对象进行沟通。

命令模式包含以下主要角色:

01.抽象命令类角色:定义命令的接口,声明执行的方法。

02.具体命令角色:实现命令接口的类。

03.实现者/接收者:真正执行命令的对象。

04.调用者/请求者:要求命令对象执行请求。

命令模式的优缺点及使用场景:

优点:降低系统的耦合度,增加与删除命令也很方便

缺点:系统架构比较复杂,命令类会比较多。

使用场景:请求与执行解耦的场景,JDK中Runnable接口就是命令对象

我们通过一个案例去学习一下策略模式,其中:服务器是请求者用于发送命令,厨师是接收者用于执行命令。

1.首先定义一个订单类,订单类包含餐桌编号,餐品名称和餐品数量集合map。

import java.util.HashMap;
import java.util.Map;

/**
 * @author nuist__NJUPT
 * @ClassName Order
 * @description: 订单类
 * @date 2024年02月01日
 */
public class Order {
    // 餐桌号码
    private int diningTable ;
    // 所下的餐品及份数
    private Map fooDir = new HashMap() ;

    public int getDiningTable() {
        return diningTable;
    }

    public void setDiningTable(int diningTable) {
        this.diningTable = diningTable;
    }

    public Map getFooDir() {
        return fooDir;
    }

    public void setFood(String name, int num) {
        fooDir.put(name, num) ;
    }
}

2.定义抽象命令类与具体命令类,在具体命令类中持有订单对象与接收者。

/**
 * @author nuist__NJUPT
 * @InterfaceName Command
 * @description: 抽象命令类
 * @date 2024年02月01日
 */
public interface Command {
    void execute() ;
}

import java.util.Map;

/**
 * @author nuist__NJUPT
 * @ClassName OrderCommand
 * @description: 具体的命令类
 * @date 2024年02月01日
 */
public class OrderCommand implements Command {

    // 持有接收者对象
    private Chief chief ;
    // 持有订单对象
    private Order order ;

    public OrderCommand(Chief chief, Order order) {
        this.chief = chief;
        this.order = order;
    }

    public void execute() {
        System.out.println(order.getDiningTable() + "桌的订单:");
        Map fooDir = order.getFooDir();
        for(String foodName : fooDir.keySet()){
            chief.makeFood(foodName,fooDir.get(foodName));
        }
        System.out.println(order.getDiningTable() + "桌的饭准备好了...");
    }
}

3.定义请求者发起命令,定义接收者执行具体的命令。

import java.util.ArrayList;
import java.util.List;

/**
 * @author nuist__NJUPT
 * @ClassName Waiter
 * @description: 请求者-服务员类
 * @date 2024年02月01日
 */
public class Waiter {
    // 持有多个命令对象
    private List list = new ArrayList() ;

    public void setCommand(Command command){
        list.add(command) ;
    }

    // 发起命令功能
    public void orderUp(){
        System.out.println("服务员:厨师,订单来了...");
        // 遍历集合
        for(Command command : list){
            if(command != null){
                command.execute();
            }
        }
    }
}
/**
 * @author nuist__NJUPT
 * @ClassName Chief
 * @description: 接收者-厨师类
 * @date 2024年02月01日
 */
public class Chief {
    public void makeFood(String name, int num){
        System.out.println(num + "份" + name);
    }
}

4.定义测试类,在测试类中创建订单与接收者对象,根据订单与接收者对象创建命令对象,根据命令对象创建发送者,由发送者发送命令。


/**
 * @author nuist__NJUPT
 * @ClassName Main
 * @description: 测试类
 * @date 2024年02月01日
 */
public class Main {
    public static void main(String[] args) {
        // 创建订单,设置餐桌号、设置食物
        Order order1 = new Order() ;
        order1.setDiningTable(1);
        order1.setFood("西红柿鸡蛋面", 1);
        order1.setFood("紫菜汤", 2);

        // 创建订单,设置餐桌号、设置食物
        Order order2 = new Order() ;
        order2.setDiningTable(2);
        order2.setFood("牛肉盖饭", 2);
        order2.setFood("冰可乐", 3);

        // 创建接收者对象
        Chief chief = new Chief();
        // 创建命令对象
        OrderCommand orderCommand1 = new OrderCommand(chief, order1);
        OrderCommand orderCommand2 = new OrderCommand(chief, order2);
        // 创建请求者对象
        Waiter waiter = new Waiter();
        waiter.setCommand(orderCommand1);
        waiter.setCommand(orderCommand2);
        // 请求者发起命令
        waiter.orderUp();
    }
}
4.责任链模式

定义:责任链模式又名职责链模式,就是将请求的处理者拼接成一条链,当请求发生时候,可以沿着链进行传递,直至找到处理的对象。

责任链的主要角色:

01.抽象处理者角色:定义一个处理请求的接口或者抽象类。
02.具体处理者角色:实现抽线处理者的抽象方法。
03.客户类角色:创建处理链,并向链头的具体处理者对象提交请求。

责任链模式的优缺点及使用场景:

优点:简化了对象之间的连接,使得责任也能正常分担。

缺点:责任链太长可能会影响性能,另外要是出现环形责任链也会导致系统错误。

使用场景:在Javaweb中的过滤器链Filterchain就是责任链模式

我们通过一个请假系统案例来学习责任链模式,请假1天以内需要小组长同意,请假1-3天需要部门经理同意,请假3-7天需要总经理同意。

1.首先定义一个请假条类,包含请假人姓名,请假时间,请假原因。

/**
 * @author nuist__NJUPT
 * @ClassName LeaveRequest
 * @description: 请假条类
 * @date 2024年02月02日
 */
public class LeaveRequest {

    // 姓名
    private String name ;
    // 请假天数
    private int num ;
    // 请假内容
    private String content ;

    public LeaveRequest(String name, int num, String content) {
        this.name = name;
        this.num = num;
        this.content = content;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getNum() {
        return num;
    }

    public void setNum(int num) {
        this.num = num;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }
}

2.然后定义抽象处理者角色用于处理请假条,其中定义具体的提交请假条方法和具体处理者待处理的抽象方法。

/**
 * @author nuist__NJUPT
 * @ClassName Handler
 * @description: 抽象处理者类
 * @date 2024年02月02日
 */
public abstract class Handler {

    protected final static int NUM_ONE = 1 ;
    protected final static int NUM_THREE = 3 ;
    protected final static int NUM_SEVEN = 7 ;

    // 该领导处理的请求天数区间
    private int numStart ;
    private int numEnd ;

    // 声明后续者:上级领导
    private Handler nextHandler ;

    public Handler(int numStart, int numEnd) {
        this.numStart = numStart;
        this.numEnd = numEnd;
    }

    public Handler(int numStart) {
        this.numStart = numStart;
    }

    // 设置上级领导的方法
    public void setNextHandler(Handler nextHandler) {
        this.nextHandler = nextHandler;
    }

    // 各级领导处理请求条的方法
    protected abstract void handleLeave(LeaveRequest leaveRequest) ;
    // 提交请求条
    public void submit(LeaveRequest leaveRequest){
        this.handleLeave(leaveRequest);
        if(this.nextHandler != null && leaveRequest.getNum() > this.numEnd){
            this.nextHandler.submit(leaveRequest);
        }else {
            System.out.println("流程结束...");
        }
    }


}

3.定义三个具体的处理者类,分别处理相应天数的请假条。

/**
 * @author nuist__NJUPT
 * @ClassName GroupLeader
 * @description: 小组长类-具体的处理者
 * @date 2024年02月02日
 */
public class GroupLeader extends Handler {

    public GroupLeader() {
        super(0, Handler.NUM_ONE);
    }

    protected void handleLeave(LeaveRequest leaveRequest) {
        System.out.println(leaveRequest.getName() + "请假" + leaveRequest.getNum() + "天" + leaveRequest.getContent());
        System.out.println("小组长审批:同意");
    }

}
/**
 * @author nuist__NJUPT
 * @ClassName Manager
 * @description: 部门经理类
 * @date 2024年02月03日
 */
public class Manager extends  Handler{
    public Manager() {
        super(Handler.NUM_ONE, Handler.NUM_THREE);
    }

    protected void handleLeave(LeaveRequest leaveRequest) {
        System.out.println(leaveRequest.getName() + "请假" + leaveRequest.getNum() + "天" + leaveRequest.getContent());
        System.out.println("部门经理审批:同意");
    }
}

/**
 * @author nuist__NJUPT
 * @ClassName GeneralManager
 * @description: 总经理类
 * @date 2024年02月03日
 */
public class GeneralManager extends Handler{
    public GeneralManager() {
        super(Handler.NUM_THREE, Handler.NUM_SEVEN);
    }

    protected void handleLeave(LeaveRequest leaveRequest) {
        System.out.println(leaveRequest.getName() + "请假" + leaveRequest.getNum() + "天" + leaveRequest.getContent());
        System.out.println("总经理审批:同意");
    }
}

4.最后客户端测试类,创建请假条,创建处理者角色并设置责任链,最后提交请假条。

/**
 * @author nuist__NJUPT
 * @ClassName Client
 * @description: 客户端测试类
 * @date 2024年02月03日
 */
public class Client {
    public static void main(String[] args) {
        // 创建一个请假条对象
        LeaveRequest leaveRequest = new LeaveRequest("小王", 6, "生病了" ) ;

        // 创建各级领导对象
        GroupLeader groupLeader = new GroupLeader() ;
        Manager manager = new Manager() ;
        GeneralManager generalManager = new GeneralManager() ;

        // 设置处理者链
        groupLeader.setNextHandler(manager);
        manager.setNextHandler(generalManager);

        // 小王提交请假条
        groupLeader.submit(leaveRequest);
    }
}
2.5.状态模式

定义:对于有状态的对象,把复杂的判断逻辑提取到不同的状态对象中去,允许在对象内部状态发生变化时改变其行为。

状态模式包含如下角色:
01.环境角色:定义客户端程序需要的接口,维护一个当前状态,将相关操作委派给当前状态处理

02.抽象状态角色:定义一个接口,用于封装环境对象中的状态所对应的行为。

03.具体状态角色:实现抽象状态所对应的行为。

状态模式的优缺点:

优点:状态与行为合为一体,改变状态即可改变行为,减少了条件语句的判断。

缺点:增加了系统类与对象的个数,实现起来相对复杂。

使用场景:当对象的行为取决于它的状态,需要根据状态改变其行为。

下面通过电梯的使用案例来学习一下状态模式,在状态对象中包含具体的行为。

1.首先定义抽象状态类,同时定义相应的抽象方法。

/**
 * @author nuist__NJUPT
 * @ClassName LiftState
 * @description: 抽象状态类
 * @date 2024年02月03日
 */
public abstract class LiftState {


    // 声明环境角色类变量
    protected Context context ;

    public void setContext(Context context) {
        this.context = context;
    }
    
    // 电梯开启、关闭、运行、停止操作
    public abstract void open() ;
    public abstract void close() ;
    public abstract void run() ;
    public abstract void stop() ;

}

2.创建一个环境类,在环境类中设置相应的状态与环境,并通过相应的状态调用相应的方法。


/**
 * @author nuist__NJUPT
 * @ClassName Context
 * @description: 环境类
 * @date 2024年02月03日
 */
public class Context {
    // 定义对应的状态对象常量
    public final static OpeningState OPENING_STATE = new OpeningState() ;
    public final static ClosingState CLOSING_STATE = new ClosingState() ;
    public final static RunningState RUNNING_STATE = new RunningState() ;
    public final static StoppingState STOPPING_STATE = new StoppingState() ;

    // 定义一个当前电梯的状态变量
    private LiftState liftState ;


    // 设置当前状态对象
    public void setLiftState(LiftState liftState) {
        this.liftState = liftState;
        this.liftState.setContext(this);
    }

    public void open(){
        this.liftState.open();
    }

    public void stop(){
        this.liftState.stop() ;
    }

    public void close(){
        this.liftState.close();
    }

    public void run(){
        this.liftState.run() ;
    }

}

3.定义四个具体的状态类,分别实现抽象状态类,并抽象状态类中的方法。


/**
 * @author nuist__NJUPT
 * @ClassName ClosingState
 * @description: 电梯关闭状态
 * @date 2024年02月03日
 */
public class ClosingState extends LiftState{

    public void open() {
        super.context.setLiftState(Context.OPENING_STATE);
        super.context.open();
    }

    public void close() {
        System.out.println("电梯门关闭了...");
    }

    public void run() {
        super.context.setLiftState(Context.RUNNING_STATE);
        super.context.run() ;
    }

    public void stop() {
        super.context.setLiftState(Context.STOPPING_STATE);
        super.context.stop();
    }
}

/**
 * @author nuist__NJUPT
 * @ClassName RunningState
 * @description: 运行状态
 * @date 2024年02月03日
 */
public class RunningState extends LiftState{

    public void open() {

    }

    public void close() {

    }

    public void run() {
        System.out.println("电梯正在运行...");
    }

    public void stop() {
        super.context.setLiftState(Context.CLOSING_STATE);
        super.context.stop();
    }
}

/**
 * @author nuist__NJUPT
 * @ClassName StoppingState
 * @description: 停止状态
 * @date 2024年02月03日
 */
public class StoppingState extends LiftState{

    public void open() {
        super.context.setLiftState(Context.OPENING_STATE);
        super.context.open() ;
    }

    public void close() {
        super.context.setLiftState(Context.CLOSING_STATE);
        super.context.close();
    }

    public void run() {
        super.context.setLiftState(Context.RUNNING_STATE);
        super.context.run();
    }

    public void stop() {
        System.out.println("电梯停止了...");
    }
}

/**
 * @author nuist__NJUPT
 * @ClassName OpeningState
 * @description: 电梯开启状态
 * @date 2024年02月03日
 */
public class OpeningState extends LiftState {

    public void open() {
        System.out.println("电梯开启了...");
    }

    public void close() {
        // 修改状态,关闭电梯
        super.context.setLiftState(Context.CLOSING_STATE);
        super.context.close();
    }

    public void run() {
        // 什么都不做
    }

    public void stop() {
        // 不做
    }
}

4.定义测试类,实例化环境,并添加相应的状态,最后调用相应的方法。

/**
 * @author nuist__NJUPT
 * @ClassName Client
 * @description: 客户端测试类
 * @date 2024年02月03日
 */
public class Client {
    public static void main(String[] args) {
        Context context = new Context() ;
        context.setLiftState(new RunningState()) ;
        context.stop() ;
        context.run() ;
        context.close() ;
        context.open() ;
    }
}
2.6.观察者模式

定义:观察者模式又称为发布订阅模式,定义一对多的依赖关系,让多个观察者同时监听一个主题对象,主题对象变化,观察者可以更好地更新自己。

在观察者模式中有如下角色:

01.抽象被观察者:把所有的观察者对象保存在一个集合里,提供接口可以增删观察者。

02.具体被观察者:将状态存入具体观察者对象,状态发生变化时,给具体观察者发送通知。

03.抽象观察者:定义更新接口,主题更新时候通知自己。

04.具体观察者:实现相应的接口,在主题更新时候,实现相应的状态更新。

观察者模式优缺点与使用场景:

优点:可以实现广播机制,降低了观察者与被观察者之间的耦合。

缺点:观察者收到消息可能会有延时。
使用场景:对象间存在一对多的关系,并且一个对象的状态改变会影响多个对象。

下面通过一个微信公众号推送的案例来学习一下观察者模式,首先微信公众号是被观察者,用户是观察者。

1.定义一个抽象被观察者角色和一个抽线观察者角色,在抽象被观察者中添加观察者并通知观察者,在观察者中根据通知更新相应得状态。

/**
 * @author nuist__NJUPT
 * @InterfaceName Subject
 * @description: 抽象被观察者角色
 * @date 2024年02月03日
 */
public interface Subject {

    // 添加观察者对象
    void attach(Observe observe) ;
    // 删除观察者
    void detach(Observe observe) ;
    // 通知观察者更新消息
    void notify(String message) ;

}

/**
 * @author nuist__NJUPT
 * @InterfaceName Observe
 * @description: 抽象观察者角色
 * @date 2024年02月03日
 */
public interface Observe {

    void update(String message) ;

}

2.定义具体的被观察者对象与具体的观察者对象,在被观察者对象中添加观察者并通知观察者。


import java.util.ArrayList;
import java.util.List;

/**
 * @author nuist__NJUPT
 * @ClassName SubscriptionSubject
 * @description: 具体被观察者对象
 * @date 2024年02月03日
 */
public class SubscriptionSubject implements Subject{

    // 定义一个集合存储观察者对象
    private List userList = new ArrayList() ;

    public void attach(Observe observe) {
        userList.add(observe) ;
    }

    public void detach(Observe observe) {
        userList.remove(observe) ;
    }

    public void notify(String message) {
        for(Observe observe : userList){
            observe.update(message) ;
        }
    }
}
/**
 * @author nuist__NJUPT
 * @ClassName User
 * @description: 具体的观察者对象
 * @date 2024年02月03日
 */
public class User implements Observe {

    private String name ;

    public User(String name) {
        this.name = name;
    }

    public void update(String message) {
        System.out.println(name + "-" + message);
    }
}

3.定义客户端测试类,测试观察者模式。


/**
 * @author nuist__NJUPT
 * @ClassName Client
 * @description: 客户端测试类
 * @date 2024年02月03日
 */
public class Client {
    public static void main(String[] args) {

        // 创建被观察者对象
        SubscriptionSubject subject = new SubscriptionSubject() ;
        // 添加观察者对象
        subject.attach(new User("用户张三"));
        subject.attach(new User("用户李四"));
        subject.attach(new User("用户王五"));
        // 被观察者通知观察者
        subject.notify("被观察者状态更新了,各位观察者望知悉...");

    }
}

你可能感兴趣的:(设计模式,设计模式,java,开发语言)