设计模式之模板方法模式、策略模式、命令模式、责任链模式、状态模式

前言

本章节给您介绍23种设计模式的行为型模式中的模板方法模式策略模式命令模式责任链模式状态模式

如有帮助记得3连 加 关注哦!欢迎品论去留言交流,谢谢阅读


一、模板方法模式

在面向对象程序设计过程中,程序员常常会遇到这种情况:设计一个系统时知道了算法所需的关键步骤,而且确定了这些步骤的执行顺序,但某些步骤的具体实现还未知,或者说某些步骤的实现与具体的环境相关

例如,去银行办理业务一般要经过以下4个流程:取号、排队、办理具体业务、对银行工作人员进行评分等,其中取号、排队和对银行工作人员进行评分的业务对每个客户是一样的,可以在父类中实现,但是办理具体业务却因人而异,它可能是存款、取款或者转账等,可以延迟到子类中实现。

这样的例子在生活中还有很多,例如,一个人每天会起床、吃饭、做事、睡觉等,其中“做事”的内容每天可能不同。我们把这些规定了流程或格式的实例定义成模板,允许使用者根据自己的需求去更新它,例如,简历模板、论文模板、Word 中模板文件等。

模板方法(Template Method)模式定义如下:定义一个操作中的算法骨架,而将算法的一些步骤延迟到子类中,使得子类可以不改变该算法结构的情况下重定义该算法的某些特定步骤。

1.1、实现方式

1.1.1、模板方法模式角色

  • 模板方法:定义了算法的骨架,按某种顺序调用其包含的基本方法。

  • 基本方法:是整个算法中的一个步骤,包含以下几种类型。

    • 抽象方法:在抽象类中声明,由具体子类实现。
    • 具体方法:在抽象类中已经实现,在具体子类中可以继承或重写它。
    • 钩子方法:在抽象类中已经实现,包括用于判断的逻辑方法和需要子类重写的空方法两种。

1.1.2、代码实现

案例:比如炒菜,都需要倒油、热油、加入材料、加入调味料、翻炒等步骤,但是加入什么材料和调味料不同。

public abstract class AbstarctClass {

    // 定义模板方法
    public final void cookProcess() {
        pourOil();
        heatOil();
        pourVegetable();
        pourSauce();
        fry();
    }

    // 1、倒油
    public void pourOil() {
        System.out.println("倒油");
    }

    // 2、热油
    public void heatOil() {
        System.out.println("热油");
    }

    // 3、倒入蔬菜
    public abstract void pourVegetable();

    // 4、倒入调料
    public abstract void pourSauce();

    // 5、翻炒
    public void fry() {
        System.out.println("炒啊炒啊炒熟了!");
    }
}

// 炒包菜
public class ConcreteClass_Baocai extends AbstarctClass{

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

    @Override
    public void pourSauce() {
        System.out.println("下锅的酱料是辣椒");
    }
}

// 炒菜心
public class ConcreteClass_Caixin extends AbstarctClass{

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

    @Override
    public void pourSauce() {
        System.out.println("下锅的酱料是蒜蓉");
    }
}

// 客户端
public class Client {
    public static void main(String[] args) {
        ConcreteClass_Baocai baocai = new ConcreteClass_Baocai();
        baocai.cookProcess();
    }
}

1.4、模板方法模式优缺点

优点

  • 它封装了不变部分,扩展可变部分。它把认为是不变部分的算法封装到父类中实现,而把可变部分算法由子类继承实现,便于子类继续扩展。
  • 它在父类中提取了公共的部分代码,便于代码复用。
  • 部分方法是由子类实现的,因此子类可以通过扩展方式增加相应的功能,符合开闭原则。

缺点

  • 对每个不同的实现都需要定义一个子类,这会导致类的个数增加,系统更加庞大,设计也更加抽象,间接地增加了系统实现的复杂度。
  • 父类中的抽象方法由子类实现,子类执行的结果会影响父类的结果,这导致一种反向的控制结构,它提高了代码阅读的难度。
  • 由于继承关系自身的缺点,如果父类添加新的抽象方法,则所有子类都要改一遍。

1.3、应用场景

  • 算法的整体步骤很固定,但其中个别部分易变时,这时候可以使用模板方法模式,将容易变的部分抽象出来,供子类实现。
  • 当多个子类存在公共的行为时,可以将其提取出来并集中到一个公共父类中以避免代码重复。首先,要识别现有代码中的不同之处,并且将不同之处分离为新的操作。最后,用一个调用这些新的操作的模板方法来替换这些不同的代码。
  • 当需要控制子类的扩展时,模板方法只在特定点调用钩子操作,这样就只允许在这些点进行扩展。

二、策略模式

在现实生活中常常遇到实现某种目标存在多种策略可供选择的情况,例如,出行旅游可以乘坐飞机、乘坐火车、骑自行车或自己开私家车等,超市促销可以釆用打折、送商品、送积分等方法。

在软件开发中也常常遇到类似的情况,当实现某一个功能存在多种算法或者策略,我们可以根据环境或者条件的不同,选择不同的算法或者策略来完成该功能,如数据排序策略有冒泡排序、选择排序、插入排序、二叉树排序等。

如果使用多重条件转移语句实现(即硬编码),不但使条件语句变得很复杂,而且增加、删除或更换算法要修改原代码,不易维护,违背开闭原则。如果采用策略模式就能很好解决该问题。

策略(Strategy)模式的定义:该模式定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的变化不会影响使用算法的客户。策略模式属于对象行为模式,它通过对算法进行封装,把使用算法的责任和算法的实现分割开来,并委派给不同的对象对这些算法进行管理。

2.1、实现方式

2.1.1、策略模式角色

  • 抽象策略(Strategy)类:定义了一个公共接口,各种不同的算法以不同的方式实现这个接口,环境角色使用这个接口调用不同的算法,一般使用接口或抽象类实现。
  • 具体策略(Concrete Strategy)类:实现了抽象策略定义的接口,提供具体的算法实现。
  • 环境(Context)类:持有一个策略类的引用,最终给客户端调用。

2.1.2、代码实现

案例:商场不同的节日有不同的活动

// 百货超市促销活动接口(抽象策略)
public interface Strategy {

    void show();

}
// 具体策略类
public class StrategyA implements Strategy{
    @Override
    public void show() {
        System.out.println("买一送一");
    }
}

public class StrategyB implements Strategy{
    @Override
    public void show() {
        System.out.println("满500减200");
    }
}

public class StrategyC implements Strategy{
    @Override
    public void show() {
        System.out.println("满1000加一元任选200以下产品");
    }
}

// 促销员(环境)
public class SaleMan {

    private Strategy strategy;

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

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

    // 促销
    public void salesManShow() {
        strategy.show();
    }
}

public class Client {
    public static void main(String[] args) {
    	// 告知售货员什么活动
        SaleMan saleMan = new SaleMan(new StrategyA());
        saleMan.salesManShow();
    }
}

2.2、策略模式优缺点

优点

  • 多重条件语句不易维护,而使用策略模式可以避免使用多重条件语句,如 if…else 语句、switch…case 语句。
  • 策略模式提供了一系列的可供重用的算法族,恰当使用继承可以把算法族的公共代码转移到父类里面,从而避免重复的代码。
  • 策略模式可以提供相同行为的不同实现,客户可以根据不同时间或空间要求选择不同的。
  • 策略模式提供了对开闭原则的完美支持,可以在不修改原代码的情况下,灵活增加新算法。
  • 策略模式把算法的使用放到环境类中,而算法的实现移到具体策略类中,实现了二者的分离。

缺点

  • 客户端必须理解所有策略算法的区别,以便适时选择恰当的算法类。
  • 策略模式造成很多的策略类,增加维护难度。

2.3、应用场景

  • 一个系统需要动态地在几种算法中选择一种时,可将每个算法封装到策略类中。
  • 一个类定义了多种行为,并且这些行为在这个类的操作中以多个条件语句的形式出现,可将每个条件分支移入它们各自的策略类中以代替这些条件语句。
  • 系统中各算法彼此完全独立,且要求对客户隐藏具体算法的实现细节时。
  • 系统要求使用算法的客户不应该知道其操作的数据时,可使用策略模式来隐藏与算法相关的数据结构。
  • 多个类只区别在表现行为不同,可以使用策略模式,在运行时动态选择具体要执行的行为。

三、命令模式

在软件开发系统中,“方法的请求者”与“方法的实现者”之间经常存在紧密的耦合关系,这不利于软件功能的扩展与维护。例如,想对方法进行“撤销、重做、记录”等处理都很不方便,因此“如何将方法的请求者与实现者解耦?”变得很重要,命令模式就能很好地解决这个问题。

在现实生活中,命令模式的例子也很多。比如看电视时,我们只需要轻轻一按遥控器就能完成频道的切换,这就是命令模式,将换台请求和换台处理完全解耦了。电视机遥控器(命令发送者)通过按钮(具体命令)来遥控电视机(命令接收者)。

再比如,我们去餐厅吃饭,菜单不是等到客人来了之后才定制的,而是已经预先配置好的。这样,客人来了就只需要点菜,而不是任由客人临时定制。餐厅提供的菜单就相当于把请求和处理进行了解耦,这就是命令模式的体现。

命令(Command)模式的定义如下:将一个请求封装为一个对象,使发出请求的责任和执行请求的责任分割开。这样两者之间通过命令对象进行沟通,这样方便将命令对象进行储存、传递、调用、增加与管理。

3.1、命令模式实现方式

案例:餐厅点餐案例

3.1.1、代码实现

// 订单类
public class Order {

    // 餐桌号码
    private String diningTable;
    // 产品和份数
    private Map<String,Integer> foodDir = new HashMap<>();

    public String getDiningTable() {
        return diningTable;
    }

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

    public Map<String, Integer> getFood() {
        return foodDir;
    }

    public void setFood(String name,Integer num) {
        this.foodDir.put(name,num);
    }
}

// 抽象命令类
public interface Command {

    void execute();
}

// 具体命令类
public class OrderCommand implements Command{

    // 持有接收者对象
    private SeniorChef receiver;
    private Order order;

    public OrderCommand(SeniorChef receiver, Order order) {
        this.receiver = receiver;
        this.order = order;
    }

    @Override
    public void execute() {
        System.out.println(order.getDiningTable() + "桌的订单:");
        // 获取订单
        Map<String, Integer> orderFood = order.getFood();

        Set<String> keySet = orderFood.keySet();

        for (String foodName : keySet) {
            if(foodName != null) {
                // 制作餐品
                receiver.makeFood(foodName,orderFood.get(foodName));
            }
        }
        System.out.println(order.getDiningTable() + "桌订单准备完毕。");

    }
}

// 厨师类
public class SeniorChef {

    public void makeFood(String name,int num) {
        System.out.println(num + "份" + name);
    }
}


public class Waitor {
    private List<Command> commands = new ArrayList<>();

    public void setCommands(Command command) {
        this.commands.add(command);
    }

    // 命令厨师制作餐品
    public void orderUp() {
        System.out.println("美女服务员:大厨,新订单来了......");
        for (Command command : commands) {
            if(command != null) {
                command.execute();
            }
        }
    }
}

public class Client {
    public static void main(String[] args) {
        // 创建第一个订单
        Order order = new Order();
        order.setDiningTable("1");
        order.setFood("西红柿鸡蛋盖浇饭",1);
        order.setFood("小杯可乐",2);

        // 第二个订单
        Order order1 = new Order();
        order1.setDiningTable("2");
        order1.setFood("鱼香肉丝盖浇饭",1);
        order1.setFood("小杯雪碧",1);

        // 创建厨师对象
        SeniorChef receiver = new SeniorChef();

        // 创建命令对象
        OrderCommand cmd1 = new OrderCommand(receiver, order);
        OrderCommand cmd2 = new OrderCommand(receiver, order1);

        // 创建调用者
        Waitor waitor = new Waitor();
        waitor.setCommands(cmd1);
        waitor.setCommands(cmd2);

        // 提交订单
        waitor.orderUp();
    }
}

3.2、命令模式优缺点

优点

  • 通过引入中间件(抽象接口)降低系统的耦合度。
  • 扩展性良好,增加或删除命令非常方便。采用命令模式增加与删除命令不会影响其他类,且满足“开闭原则”。
  • 可以实现宏命令。命令模式可以与组合模式结合,将多个命令装配成一个组合命令,即宏命令。
  • 方便实现 Undo 和 Redo 操作。命令模式可以与后面介绍的备忘录模式结合,实现命令的撤销与恢复。
  • 可以在现有命令的基础上,增加额外功能。比如日志记录,结合装饰器模式会更加灵活。

缺点

  • 可能产生大量具体的命令类。因为每一个具体操作都需要设计一个具体命令类,这会增加系统的复杂性。
  • 命令模式的结果其实就是接收方的执行结果,但是为了以命令的形式进行架构、解耦请求与实现,引入了额外类型结构(引入了请求方与抽象命令接口),增加了理解上的困难。不过这也是设计模式的通病,抽象必然会额外增加类的数量,代码抽离肯定比代码聚合更加难理解。

3.3、应用场景

当系统的某项操作具备命令语义,且命令实现不稳定(变化)时,可以通过命令模式解耦请求与实现。使用抽象命令接口使请求方的代码架构稳定,封装接收方具体命令的实现细节。接收方与抽象命令呈现弱耦合(内部方法无需一致),具备良好的扩展性。

  1. 请求调用者需要与请求接收者解耦时,命令模式可以使调用者和接收者不直接交互。
  2. 系统随机请求命令或经常增加、删除命令时,命令模式可以方便地实现这些功能。
  3. 当系统需要执行一组操作时,命令模式可以定义宏命令来实现该功能。
  4. 当系统需要支持命令的撤销(Undo)操作和恢复(Redo)操作时,可以将命令对象存储起来,采用备忘录模式来实现。

四、责任链模式

在现实生活中,一个事件需要经过多个对象处理是很常见的场景。例如,采购审批流程、请假流程等。公司员工请假,可批假的领导有部门负责人、副总经理、总经理等,但每个领导能批准的天数不同,员工必须根据需要请假的天数去找不同的领导签名,也就是说员工必须记住每个领导的姓名、电话和地址等信息,这无疑增加了难度。

在计算机软硬件中也有相关例子,如总线网中数据报传送,每台计算机根据目标地址是否同自己的地址相同来决定是否接收;还有异常处理中,处理程序根据异常的类型决定自己是否处理该异常;还有 Struts2的拦截器、JSP 和 Servlet的 Filter 等,所有这些,都可以考虑使用责任链模式来实现。

责任链(Chain of Responsibility)模式的定义:为了避免请求发送者与多个请求处理者耦合在一起,于是将所有请求的处理者通过前一对象记住其下一个对象的引用而连成一条链;当有请求发生时,可将请求沿着这条链传递,直到有对象处理它为止。

注意:责任链模式也叫职责链模式。

在责任链模式中,客户只需要将请求发送到责任链上即可,无须关心请求的处理细节和请求的传递过程,请求会自动进行传递。所以责任链将请求的发送者和请求的处理者解耦了。

4.1、实现方式

4.1.1、责任链模式角色

  • 抽象处理者(Handler)角色:定义一个处理请求的接口,包含抽象处理方法和一个后继连接。
  • 具体处理者(Concrete Handler)角色:实现抽象处理者的处理方法,判断能否处理本次请求,如果可以处理请求则处理,否则将该请求转给它的后继者。
  • 客户类(Client)角色:创建处理链,并向链头的具体处理者对象提交请求,它不关心处理细节和请求的传递过程。

4.1.2、代码实现

请假流程控制:请假一天以下只需要小组长同意,请假1天到3天需要部门经理同意,请假3天到7天需要总经理同意

// 请假条类
public class LeaveRequest {

    // 姓名
    private String name;

    // 请假天数
    private int num;

    // 请假原因
    private String cause;

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

    public String getName() {
        return name;
    }

    public int getNum() {
        return num;
    }

    public String getCause() {
        return cause;
    }
}


// 抽象处理者
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) {
        this.numStart = numStart;
    }

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

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

    // 各级领导处理请假条的方法
    protected abstract void handleLeave(LeaveRequest leave);

    // 提交请假条处理流程
    protected final void submit(LeaveRequest leave) {
        // 处理请假条
        this.handleLeave(leave);
        // 如果当前只为不能处理,并且有下一级领导
        if(this.nextHandler != null && leave.getNum() > this.numEnd) {
            this.nextHandler.submit(leave);
        }else {
            System.out.println("处理完毕!");
        }
    }
}

// 小组长类
public class GroupLeader extends Handler{

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

    @Override
    protected void handleLeave(LeaveRequest leave) {
        System.out.println(leave.getName() + "因" + leave.getCause() + "请假" + leave.getNum() + "天");
        System.out.println("小组长审批:同意!");
    }
}

// 部门经理类
public class Manager extends Handler{

    public Manager() {
        super(Handler.NUM_ONE,Handler.NUM_THREE);
    }

    @Override
    protected void handleLeave(LeaveRequest leave) {
        System.out.println(leave.getName() + "因" + leave.getCause() + "请假" + leave.getNum() + "天");
        System.out.println("部门经理审批:同意!");
    }
}

// 总经理类
public class GeneralManager extends Handler{

    public GeneralManager() {
        super(Handler.NUM_THREE,Handler.NUM_SEVEN);
    }

    @Override
    protected void handleLeave(LeaveRequest leave) {
        System.out.println(leave.getName() + "因" + leave.getCause() + "请假" + leave.getNum() + "天");
        System.out.println("总经理审批:同意!");
    }
}

public class Client {
    public static void main(String[] args) {
        LeaveRequest leave = new LeaveRequest("小明", 4, "身体不适");
        // 创建各级领导
        GroupLeader groupLeader = new GroupLeader();
        Manager manager = new Manager();
        GeneralManager generalManager = new GeneralManager();

        // 设置责任链
        groupLeader.setNextHandler(manager);
        manager.setNextHandler(generalManager);

        groupLeader.submit(leave);
    }
}

4.2、责任链模式优缺点

优缺点

优点

  • 降低了对象之间的耦合度。该模式使得一个对象无须知道到底是哪一个对象处理其请求以及链的结构,发送者和接收者也无须拥有对方的明确信息。
  • 增强了系统的可扩展性。可以根据需要增加新的请求处理类,满足开闭原则。
  • 增强了给对象指派职责的灵活性。当工作流程发生变化,可以动态地改变链内的成员或者调动它们的次序,也可动态地新增或者删除责任。
  • 责任链简化了对象之间的连接。每个对象只需保持一个指向其后继者的引用,不需保持其他所有处理者的引用,这避免了使用众多的 if 或者 if···else 语句。
  • 责任分担。每个类只需要处理自己该处理的工作,不该处理的传递给下一个对象完成,明确各类的责任范围,符合类的单一职责原则。

缺点

  • 不能保证每个请求一定被处理。由于一个请求没有明确的接收者,所以不能保证它一定会被处理,该请求可能一直传到链的末端都得不到处理。
  • 对比较长的职责链,请求的处理可能涉及多个处理对象,系统性能将受到一定影响。
  • 职责链建立的合理性要靠客户端来保证,增加了客户端的复杂性,可能会由于职责链的错误设置而导致系统出错,如可能会造成循环调用。

4.3、应用场景

  • 多个对象可以处理一个请求,但具体由哪个对象处理该请求在运行时自动确定。
  • 可动态指定一组对象处理请求,或添加新的处理者。
  • 需要在不明确指定请求处理者的情况下,向多个处理者中的一个提交请求。

五、状态模式

在软件开发过程中,应用程序中的部分对象可能会根据不同的情况做出不同的行为,我们把这种对象称为有状态的对象,而把影响对象行为的一个或多个动态变化的属性称为状态。当有状态的对象与外部事件产生互动时,其内部状态就会发生改变,从而使其行为也发生改变。如人都有高兴和伤心的时候,不同的情绪有不同的行为,当然外界也会影响其情绪变化。

对这种有状态的对象编程,传统的解决方案是:将这些所有可能发生的情况全都考虑到,然后使用 if-else 或 switch-case 语句来做状态判断,再进行不同情况的处理。但是显然这种做法对复杂的状态判断存在天然弊端,条件判断语句会过于臃肿,可读性差,且不具备扩展性,维护难度也大。且增加新的状态时要添加新的 if-else 语句,这违背了“开闭原则”,不利于程序的扩展。

以上问题如果采用“状态模式”就能很好地得到解决。状态模式的解决思想是:当控制一个对象状态转换的条件表达式过于复杂时,把相关“判断逻辑”提取出来,用各个不同的类进行表示,系统处于哪种情况,直接使用相应的状态类对象进行处理,这样能把原来复杂的逻辑判断简单化,消除了 if-else、switch-case 等冗余语句,代码更有层次性,并且具备良好的扩展力

状态(State)模式的定义:对有状态的对象,把复杂的“判断逻辑”提取到不同的状态对象中,允许状态对象在其内部状态发生改变时改变其行为。

5.1、实现方式

5.1.1、状态模式角色

  • 环境类(Context)角色:也称为上下文,它定义了客户端需要的接口,内部维护一个当前状态,并负责具体状态的切换。
  • 抽象状态(State)角色:定义一个接口,用以封装环境对象中的特定状态所对应的行为,可以有一个或多个行为。
  • 具体状态(Concrete State)角色:实现抽象状态所对应的行为,并且在需要的情况下进行状态切换。

5.1.2、代码实现

抽奖案例:先扣除积分再进行抽奖,10%的概率中奖,如果中奖发送奖品,如果没有中奖再扣除积分继续抽奖。如果奖品方法完毕则不能抽奖。抽奖过程中有很多状态,能不能抽奖看积分,发不发奖品看是否中奖,是否有奖品。

public abstract class State {

    // 扣除积分
    public abstract void deductMoney();
    // 是否抽中奖品
    public abstract boolean raffle();
    // 发放奖品
    public abstract void dispensePrize();
}

// 不能抽奖状态
public class NoRaffleState extends State{

    // 状态对象
    RaffleActivity raffleActivity;

    public NoRaffleState(RaffleActivity raffleActivity) {
        this.raffleActivity = raffleActivity;
    }

    // 扣除积分,将状态改为可以抽奖状态
    @Override
    public void deductMoney() {
        System.out.println("扣除50积分成功,您可以抽奖了");
        raffleActivity.setState(raffleActivity.getCanRaffleState());
    }

    @Override
    public boolean raffle() {
        System.out.println("扣除积分才可以抽奖");
        return false;
    }

    @Override
    public void dispensePrize() {
        System.out.println("不能发放奖品");
    }
}

// 可以抽奖
public class CanRaffleState extends State{

    // 聚合状态类
    RaffleActivity activity;

    public CanRaffleState(RaffleActivity activity) {
        this.activity = activity;
    }

    // 扣除积分
    @Override
    public void deductMoney() {
        System.out.println("已经扣除过积分");
    }

    // 是否中奖
    @Override
    public boolean raffle() {
        System.out.println("正在抽奖,请稍等");
        Random random = new Random();
        int num = random.nextInt(10);
        // 百分之十中奖率
        if(num == 0) {
            activity.setState(activity.getDispenseState());
            return true;
        }else {
            System.out.println("很遗憾没有中奖");
            activity.setState(activity.getNoRaffleState());
            return false;
        }
    }

    // 发放奖品
    @Override
    public void dispensePrize() {
        System.out.println("没中奖不能发放奖品!");
    }
}

// 发放奖品
public class DispenseState extends State{

    RaffleActivity activity;

    public DispenseState(RaffleActivity activity) {
        this.activity = activity;
    }

    @Override
    public void deductMoney() {
        System.out.println("不能扣除积分");
    }

    @Override
    public boolean raffle() {
        System.out.println("不能抽奖");
        return false;
    }

    // 发放奖品
    @Override
    public void dispensePrize() {
        if(activity.getCount() > 0) {
            System.out.println("恭喜中奖!");
            // 改变状态为不能抽奖
            activity.setState(activity.getNoRaffleState());
        }else {
            System.out.println("很遗憾,奖品送完了");
            // 修改状态为奖品发放完毕
            activity.setState(activity.getDispenseOutState());
        }
    }
}

//奖品发放完毕
public class DispenseOutState extends State{

    RaffleActivity activity;

    public DispenseOutState(RaffleActivity activity) {
        this.activity = activity;
    }


    @Override
    public void deductMoney() {
        System.out.println("奖品发放完毕,请下次参加!");
    }

    @Override
    public boolean raffle() {
        System.out.println("奖品发放完毕,请下次参加!");
        return false;
    }

    @Override
    public void dispensePrize() {
        System.out.println("奖品发放完毕,请下次参加!");
    }
}

public class RaffleActivity {
    // 当前状态
    State state = null;
    // 奖品数量
    int count = 0;

    State noRaffleState = new NoRaffleState(this);
    State canRaffleState = new CanRaffleState(this);
    State dispenseState = new DispenseState(this);
    State dispenseOutState = new DispenseOutState(this);

    public RaffleActivity(int count) {
        this.count = count;
        this.state = noRaffleState;
    }

    // 扣分
    public void debuctMoney() {
        state.deductMoney();
    }

    // 抽奖
    public void raffle() {
        if(state.raffle()) {
            state.dispensePrize();
        }
    }

    public State getNoRaffleState() {
        return noRaffleState;
    }

    public State getCanRaffleState() {
        return canRaffleState;
    }

    public State getDispenseOutState() {
        return dispenseOutState;
    }

    public State getDispenseState() {
        return dispenseState;
    }

    public State getState() {
        return state;
    }

    public void setState(State state) {
        this.state = state;
    }

    public void setCount(int count) {
        this.count = count;
    }

    public int getCount() {
        int curCount = count;
        curCount--;
        return curCount;
    }
}

public class Client {
    public static void main(String[] args) {
        RaffleActivity activity = new RaffleActivity(1);

        for (int i = 0; i < 20; i++) {
            System.out.println("-----------第"+ (i + 1) +"次抽奖-----------------");
            // 扣除积分
            activity.debuctMoney();
            // 抽奖
            activity.raffle();
        }
    }
}

5.2、状态模式优缺点

优点

  • 结构清晰,状态模式将与特定状态相关的行为局部化到一个状态中,并且将不同状态的行为分割开来,满足“单一职责原则”。
  • 将状态转换显示化,减少对象间的相互依赖。将不同的状态引入独立的对象中会使得状态转换变得更加明确,且减少对象间的相互依赖。
  • 状态类职责明确,有利于程序的扩展。通过定义新的子类很容易地增加新的状态和转换。

缺点

  • 状态模式的使用必然会增加系统的类与对象的个数。
  • 状态模式的结构与实现都较为复杂,如果使用不当会导致程序结构和代码的混乱。
  • 状态模式对开闭原则的支持并不太好,对于可以切换状态的状态模式,增加新的状态类需要修改那些负责状态转换的源码,否则无法切换到新增状态,而且修改某个状态类的行为也需要修改对应类的源码。

5.3、应用场景

  • 当一个对象的行为取决于它的状态,并且它必须在运行时根据状态改变它的行为时,就可以考虑使用状态模式。
  • 一个操作中含有庞大的分支结构,并且这些分支决定于对象的状态时。

总结

本章我们一起学习了行为型设计模式中的5种,分别为:模板方法模式、策略模式、命令模式、责任链模式、状态模式,您可多多练习,思考理解。看看是否可以将之前项目中的if…else通过策略模式优化,看看是否可以将项目中的固定逻辑,通过模板方法模式定义优化,看看是否可以将系统中的状态,通过状态模式提高一下等。

如有疑问记得评论区留言或者私信哦!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

石添的编程哲学

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值