设计模式-状态模式

原文地址:LoveDev

一个对象的行为其属性的动态变化,这样的属性叫状态,这类对象也叫做有状态的对象。当此类对象被某一事件修改其内部状态时,程序的行为也要随之改变

状态模式又称状态对象模式(Pattern of Objects for States)是用于解决对象的复杂状态及不同状态下的行为的一种模式。

模式定义

状态模式允许一个对象在其内部属性改变的时候改变其行为,这个对象看上去就像是改变了它的类一样

模式结构

状态模式涉及角色:

  • 环境角色(Context):定义客户感兴趣的接口,并保留一个具体状态类的实例
  • 抽象状态角色(State):定义一个借口,封装特定状态下的对应行为
  • 具体状态角色(ConcreteState):抽象状态角色的子类,每个子类实现了相关的行为

Tip:

  • 该图为UML图
  • 类包含3个组成部分,第一栏为类名,第二栏为属性,第三栏为方法
  • 属性和方法前可加一个可见性修饰符, + 号表示 public 修饰符, - 号表示 private 修饰符, # 号表示 protected 修饰符,省略表示包级可见。
  • 接口包含2个组成部分,第一栏为接口名,第二栏为方法,在接口名之上加上 <<interface>>

使用场景

比如游戏中一个用户的用过外挂违规次数属性,如果用户用过1~3次,每次警告制裁;3次以上,每次封号3天;5次以上,每次封号1周;到达10次,永久封号。

根据以上描述可以分为四种状态:

  • 警告
  • 封号3天
  • 封号1周
  • 永久封号

源码

环境角色

public class PunishManager {

    //保存违规用户及次数
    private Map<String, Integer> mPunishMap = new HashMap<>();


    /**
     * 获取违规用户及次数
     */
    Map<String, Integer> getPunishMap() {
        return mPunishMap;
    }

    /**
     * 获取具体状态角色,封装转换规则
     *
     * @param oldPunishCount 违规次数
     * @return 具体状态角色
     */
    private PunishState getPunishState(Integer oldPunishCount) {

        //推荐尽量少用else,如果超过3层if-else代码推荐使用卫语句
        if (oldPunishCount <= 3) {
            return new LowPunishState();
        }

        if (oldPunishCount <= 5) {
            return new MidPunishState();
        }

        if (oldPunishCount < 10) {
            return new HeightPunishState();
        }

        return new BlackPunishState();
    }

    /**
     * 违规处理
     *
     * @param uid 用户ID
     */
    public void punish(String uid) {
        //获取之前违规次数
        Integer oldPunishCount = mPunishMap.get(uid);

        if (oldPunishCount == null) {
            oldPunishCount = 0;
        }

        oldPunishCount += 1;
        mPunishMap.put(uid, oldPunishCount);

        //获取对应状态对象进行响应操作
        getPunishState(oldPunishCount).punish(uid, oldPunishCount, this);
    }
}

抽象状态角色

public interface PunishState {
    /**
     * 违规处理
     *
     * @param uid            用户ID
     * @param violationCount 违规次数
     * @param punishManager  环境角色
     */
    public void punish(String uid, int violationCount, PunishManager punishManager);
}

具体状态角色

根据不同的具体状态角色做相应的业务

public class LowPunishState implements PunishState {

    @Override
    public void punish(String uid, int violationCount, PunishManager punishManager) {
        //违规1~3次,警告制裁
        System.out.println("警告制裁");
    }
}
public class MidPunishState implements PunishState {
    @Override
    public void punish(String uid, int violationCount, PunishManager punishManager) {
        //违规3次以上,封号三天
        System.out.println("封号三天");
    }
}
public class HeightPunishState implements PunishState {
    @Override
    public void punish(String uid, int violationCount, PunishManager punishManager) {
        //违规5次以上,封号一周
        System.out.println("封号一周");
    }
}
public class BlackPunishState implements PunishState {
    @Override
    public void punish(String uid, int violationCount, PunishManager punishManager) {
        //违规10次,永久封号
        System.out.println("永久封号");
    }
}

入口类

public class Main {
    public static void main(String[] args) {
        PunishManager punishManager = new PunishManager();

        for (int i = 1; i <= 10; i++) {
            punishManager.punish("Kevin");
        }
    }
}

运行结果:


优点

  • 封装了转换规则
  • 结构清晰,提高可维护性
  • 不同状态对应的不同行为放到单独类中,方便增加新的状态,只需改变对象状态即可改变对象行为

缺点

  • 增加了类和对象的个数
  • 状态模式对“开闭原则”的支持并不太好,对于可以切换状态的状态模式,增加新的状态类需要修改那些负责状态转换的源代码,否则无法切换到新增状态;而且修改某个状态类的行为也需修改对应类的源代码。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,362评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,330评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,247评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,560评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,580评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,569评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,929评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,587评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,840评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,596评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,678评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,366评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,945评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,929评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,165评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,271评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,403评论 2 342

推荐阅读更多精彩内容