设计模式09-行为型模式2(状态模式/策略模式/Java)

5.4 状态模式

5.4.1 状态模式的定义

1.模式动机:有些对象具有多种状态,这些状态在某些情况下能够相互转换,对象在不同的状态下将具有不同的行为,将拥有状态的对象中和状态的行为分离。

2.模式定义:允许一个对象在其内部状态改变时改变它的行为,对象看起来似乎修改了它的类

5.4.2 状态模式的结构与分析
  • 环境类是指拥有状态的对象,可以对状态进行切换。
  • 抽象状态类是具体状态类的父类,各状态的行为封装在具体的状态类中,对于客户端而言,无须关心对象状态的转换以及对象所处的当前状态,无论对于何种状态的对象,客户端都可以一致处理。
java 复制代码
public abstract class State {
    public abstract void handle();
}

public class ConcreteState extends State{
    @Override
    public void handle() {

    }
}

public class Context {
    private State state;
    public void setState(State state) {
        this.state = state;
    }
    public void request() {
        state.handle();
    }
}
5.4.3 状态模式的案例

在某论坛系统中,用户可以发表留言,发表留言将增加积分;用户也可以回复留言,回复留言也将增加积分;用户还可以下载文件,下载文件将扣除积分。该系统用户分为三个等级,分别是新手、高手和专家,这三个等级对应三种不同的状态,这三种状态分别定义如下:

(1) 如果积分小于100分,则为新手状态,用户可以发表留言、回复留言,但是不能下载文件。如果积分大于等于1000分,则转换为专家状态;如果积分大于等于100分,则转换为高手状态。

(2) 如果积分大于等于100分但小于1000分,则为高手状态,用户可以发表留言、回复留言,还可以下载文件,而且用户在发表留言时可以获取双倍积分。积分小于100分,转换为新手状态;如果积分大于等于1000分,则转换为专家状态;如果下载文件后积分小于0,则不能下载该文件。

(3) 如果积分大于等于1000分,则为专家状态,用户可以发表留言、回复留言和下载文件,用户除了在发表留言时可以获取双倍积分外,下载文件只扣除所需积分的一半。积分小于100分,则转换为新手状态;积分小于1000分,但大于等于100,则转换为高手状态;如果下载文件后积分小于0,则不能下载该文件。

由于上述类图将point放到了状态方面,不好理解,下面代码将point放到ForumAccount中

java 复制代码
public class ForumAccount {
    private String username;
    private State state;
    private int point;

    public ForumAccount(String username) {
        this.username = username;
        this.point = 0;
        this.state = new PrimaryState(this);
    }

    public void writeNote(int point) {
        state.writeNote(point);
    }
    public void replyNote(int point) {
        state.replyNote(point);
    }
    public void downloadNote(int point) {
        state.downloadNote(point);
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public State getState() {
        System.out.println(state.getStateName());
        return state;
    }

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

    public int getPoint() {
        return point;
    }

    public void setPoint(int point) {
        this.point = point;
    }

}
java 复制代码
public abstract class State {
    protected ForumAccount forumAccount;
    protected String stateName;
    public State(ForumAccount forumAccount) {
        this.forumAccount = forumAccount;
    }
    public abstract void writeNote(int point);
    public abstract void replyNote(int point);
    public abstract void downloadNote(int point);
    public void updateState(int point) {
        if (point >= 1000) {
            forumAccount.setState(new HighState(forumAccount));
        } else if (point >= 100) {
            forumAccount.setState(new MiddleState(forumAccount));
        } else {
            forumAccount.setState(new PrimaryState(forumAccount));
        }
    }
    public String getStateName() {
        return stateName;
    }
}
java 复制代码
public class PrimaryState extends State{
    public PrimaryState(ForumAccount forumAccount) {
        super(forumAccount);
        this.stateName = "新手级别";
    }

    @Override
    public void writeNote(int point) {
        System.out.println("新手版-写评论");
        forumAccount.setPoint(forumAccount.getPoint() + point);
        updateState(forumAccount.getPoint());
    }

    @Override
    public void replyNote(int point) {
        System.out.println("新手版-回复评论");
        forumAccount.setPoint(forumAccount.getPoint() + point);
        updateState(forumAccount.getPoint());
    }

    @Override
    public void downloadNote(int point) {
        System.out.println("新手状态-无法下载文件");
    }
}
java 复制代码
public class MiddleState extends State{
    public MiddleState(ForumAccount forumAccount) {
        super(forumAccount);
        this.stateName = "高手级别";
    }

    @Override
    public void writeNote(int point) {
        System.out.println("高手版-写评论");
        forumAccount.setPoint(forumAccount.getPoint() + point);
        updateState(forumAccount.getPoint());
    }

    @Override
    public void replyNote(int point) {
        System.out.println("高手版-回复评论");
        forumAccount.setPoint(forumAccount.getPoint() + point * 2);
        updateState(forumAccount.getPoint());
    }

    @Override
    public void downloadNote(int point) {
        if (point > forumAccount.getPoint()) {
            System.out.println("积分不足,不能下载");
        } else {
            System.out.println("高手版-下载文件");
            forumAccount.setPoint(forumAccount.getPoint() - point);
            updateState(forumAccount.getPoint());
        }
    }
}
java 复制代码
public class HighState extends State {
    public HighState(ForumAccount forumAccount) {
        super(forumAccount);
        this.stateName = "专家级别";
    }

    @Override
    public void writeNote(int point) {
        System.out.println("专家版-写评论");
        forumAccount.setPoint(forumAccount.getPoint() + point);
        updateState(forumAccount.getPoint());
    }

    @Override
    public void replyNote(int point) {
        System.out.println("专家版-回复评论");
        forumAccount.setPoint(forumAccount.getPoint() + point * 2);
        updateState(forumAccount.getPoint());
    }

    @Override
    public void downloadNote(int point) {
        if (point / 2 > forumAccount.getPoint()) {
            System.out.println("积分不足,不能下载");
        } else {
            System.out.println("专家版-下载文件");
            forumAccount.setPoint(forumAccount.getPoint() - point / 2);
            updateState(forumAccount.getPoint());
        }
    }
}
5.4.4 状态模式的优缺点
优点 缺点
1.封装了状态的转换规则,可以对状态转换代码进行集中管理,而不是需要冗长的if-else判断 1.会增加系统中类和对象的个数,导致系统运行开销增大
2.将所有与某个状态有关的行为放到一个类中,注入一个不同的状态对象即可使环境对象拥有不同行为 2.结构与实现都较为复杂,如果使用不当将导致程序结构和代码混乱,增加系统设计的难度
3.允许状态转换逻辑与状态对象合成一体,而不是提供一个巨大的条件语句块 3.对开闭原则的支持并不太好
4.可让多个环境对象共享一个状态对象,从而减少系统中对象的个数
5.4.5 状态模式的适用场景
  • 对象的行为依赖于它的状态(例如某些属性值),状态的改变将导致行为的变化

  • 在代码中包含大量与对象状态有关的条件语句,这些条件语句的出现会导致代码的可维护性和灵活性变差,不能方便地增加和删除状态,并且导致客户类与类库之间的耦合增强

5.5 策略模式

5.5.1 策略模式的定义

1.模式动机:解决某一问题的一个算法族,允许用户从该算法族中任选一个算法解决某一问题,同时可以方便地更换算法或者增加新的算法。

2.模式定义:定义一系列算法,将每一个算法封装起来,并让它们可以相互替换。

5.5.2 策略模式的结构与分析
  • 它将每一个算法封装在一个称为具体策略的类中,同时为其提供统一的抽象策略类,而使用这些算法完成某一业务功能的类称为环境类。

  • 策略模式实现了算法定义和算法使用的分离,它通过继承和多态的机制实现对算法族的使用和管理,是一种简单易用的对象行为型模式。

  • 各算法是由客户端决定的,不是策略模式自己决定的。

java 复制代码
public abstract class Strategy {
    public abstract void algorithm();
}
public class ConcreteStrategy extends Strategy{
    @Override
    public void algorithm() {
    }
}
public class Context {
    private Strategy strategy;

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

    public void request() {
        strategy.algorithm();
    }
}
5.5.3 策略模式的案例
java 复制代码
public class Person {
    private String name;
    private TravelStrategy travelStrategy;
    public Person(String name) {
        this.name = name;
    }

    public void setTravelStrategy(TravelStrategy travelStrategy) {
        this.travelStrategy = travelStrategy;
    }

    public void travel() {
        System.out.print(this.name);
        travelStrategy.travel();
    }
}

public interface TravelStrategy {
    public void travel();
}

public class TrainStrategy implements TravelStrategy{
    @Override
    public void travel() {
        System.out.println("乘坐火车去旅行");
    }
}

public class Main {
    public static void main(String[] args) {
        TravelStrategy bean = (AirPlaneTravelStrategy) XMLUtil.getBean();
        Person person = new Person("xyx");
        person.setTravelStrategy(bean);
        person.travel();
    }
}
5.5.4 策略模式的优缺点
优点 缺点
1.符合开闭原则,用户可以在不修改原有系统的基础上选择算法或行为,也可以灵活地增加新的算法或行为 1.客户端必须知道所有的策略类,并自行决定使用哪一个策略类
2.提供了管理相关的算法族的办法,避免多重条件选择语句 2.将造成系统产生很多具体策略类
3.提供了一种可以替换继承关系的办法 3.无法同时在客户端使用多个策略类
5.5.5 策略模式的适用场景
  • 一个系统需要动态地在几种算法中选择一种

  • 避免使用难以维护的多重条件选择语句

  • 不希望客户端知道复杂的、与算法相关的数据结构,提高算法的保密性与安全性

5.5.6 状态模式和策略模式的对比
状态模式 策略模式
环境类状态个数 多个状态,可以相互转化 存在一个状态,一旦选择不能随意改变
环境类与状态类关系 若环境类会影响状态的改变,为双向关联关系 单向的关联关系
客户端 无需知道具体状态,会根据行为自动切换 需要知道所选策略
行为 各状态下有多个行为 一个行为的多种实现方式
相关推荐
hxj..15 分钟前
【设计模式】观察者模式
java·观察者模式·设计模式
kevin_tech2 小时前
Go API 多种响应的规范化处理和简化策略
开发语言·后端·golang·状态模式
anyup_前端梦工厂3 小时前
JavaScript 23种经典设计模式简介
前端·javascript·设计模式
敲敲er4 小时前
[项目] C++基于多设计模式下的同步&异步日志系统
c++·设计模式
morning_judger6 小时前
【设计模式系列】桥接模式(十三)
java·设计模式·桥接模式
monkey_meng7 小时前
【Rust实现命令模式】
开发语言·设计模式·rust·命令模式
morning_judger7 小时前
【设计模式系列】原型模式(十一)
java·设计模式·原型模式
denghai邓海18 小时前
基于势能的平面运动模拟
python·平面·状态模式
老猿讲编程1 天前
安全关键型嵌入式系统设计模式整理及应用实例
安全·设计模式·iso26262·do178