Android源码设计模式学习笔记-状态模式

状态设计模式抽象一系列操作到一个特定的状态类中,这样避免使用if else或者switch case去区分不同状态逻辑所导致的代码逻辑臃肿,耦合性高.
例如目前有个电视遥控器,它有两种状态,一种开机状态,一种关机状态,它还具有一些操作,例如切换频道和调整音量大小,在开机状态下这下操作才能顺利执行,在关机状态下是不能执行的。于是代码如下:

public class TvController {
    //开机状态
    private final static int POWER_ON = 1;
    //关机状态
    private final static int POWER_OFF = 2;
    private int mState = POWER_OFF;

    public void powerOn(){
        mState = POWER_ON;
        if (mState == POWER_OFF){
            System.out.println("开机啦");
        }
    }

    public void powerOff(){
        mState = POWER_OFF;
        if (mState == POWER_ON){
            System.out.println("关机啦");
        }
    }

    public void nextChannel(){
        if (mState == POWER_ON){
            System.out.println("下一频道");
        }else {
            System.out.println("两个红灯提示没有开机");
        }
    }

    public void prevChannel(){
        if (mState == POWER_ON){
            System.out.println("上一频道");
        }else {
            System.out.println("两个红灯提示没有开机");
        }
    }

    public void turnUp(){
        if (mState == POWER_ON){
            System.out.println("调高音量");
        }else{
            System.out.println("两个红灯提示没有开机");
        }
    }

    public void turnDown(){
        if (mState == POWER_ON){
            System.out.println("调低音量");
        }else{
            System.out.println("两个红灯提示没有开机");
        }
    }
}

可以看到在每次操作nextChannel,prevChannel,turnUp,turnDown时候都要进行if else的判断,如果我们再增加一种待机状态的化,还会增加else if语句,这样每次在增加操作的或者状态的时候都要去修改TvController这个类,这不符合开闭原则,而且容易写代码遗漏某些状态case. 状态模式就是用来解决这个问题的。
先看下状态模式的uml


image.png

通过抽象所有操作到IState接口中,实现类为不同状态,在不同状态下作出不同的实际操作,Context是一个外部调用用来设置状态,和调用不同操作的类,实际上它也可以实现IState接口。说了这么多,可能还比较抽象,下面看看优化过后的代码。
对电视遥控器的状态操作进行抽象

public interface TvState {
    void nextChannel();
    void prevChannel();
    void turnUp();
    void turnDown();
}

状态抽象类的实现类,在不同状态下实际的操作不一样

public class PowerOnState implements TvState {
    @Override
    public void nextChannel() {
        System.out.println("下一频道");
    }

    @Override
    public void prevChannel() {
        System.out.println("上一频道");
    }

    @Override
    public void turnUp() {
        System.out.println("调高音量");
    }

    @Override
    public void turnDown() {
        System.out.println("调低音量");
    }
}
public class PowerOffState implements TvState {
    @Override
    public void nextChannel() {

    }

    @Override
    public void prevChannel() {

    }

    @Override
    public void turnUp() {

    }

    @Override
    public void turnDown() {

    }
}

对电视遥控器的状态进行抽象(注意之前TvState抽象的是状态操作,这里抽象的是状态)

public interface PowerState {
    void powerOn();
    void powerOff();
}

最后的遥控器类

public class TvController implements PowerState,TvState {

    private TvState tvState;

    @Override
    public void powerOn() {
        tvState = new PowerOnState();
        System.out.println("开机啦");
    }

    @Override
    public void powerOff() {
        tvState = new PowerOffState();
        System.out.println("关机啦");
    }

    @Override
    public void nextChannel() {
        tvState.nextChannel();
    }

    @Override
    public void prevChannel() {
        tvState.prevChannel();
    }

    @Override
    public void turnUp() {
        tvState.turnUp();
    }

    @Override
    public void turnDown() {
        tvState.turnDown();
    }
}

调用实现:

        TvController tvController = new TvController();
        tvController.powerOn();
        tvController.nextChannel();
        tvController.turnDown();
        tvController.powerOff();
        tvController.nextChannel();

最后输出:

01-12 22:16:07.135 19159-19159/com.example.huangli.statepattern I/System.out: 开机啦
01-12 22:16:07.135 19159-19159/com.example.huangli.statepattern I/System.out: 下一频道
01-12 22:16:07.135 19159-19159/com.example.huangli.statepattern I/System.out: 调低音量
01-12 22:16:07.135 19159-19159/com.example.huangli.statepattern I/System.out: 关机啦

上面讲述一个电视遥控器使用状态模式的一个简单栗子,它把在抽象电源打开状态和关闭状态不同的操作到不同的类中,从而规避了使用if else去判断状态。
什么时候可以使用到这种模式?
只要需要去判断不同状态下面作出不同的操作,并且这几种状态都抽象出一系列操作我们就可以使用这一种模式,举一个实际在安卓开发过程运用状态模式的栗子。
在浏览微博的时候,对感兴趣的微博可以转发,但是在登录和未登陆的情况下操作不一样,登录的情况下我可以直接转发微博,未登录的情况下需要跳转登录页面,评论功能同理。这里就符合使用状态模式的条件,在做出转发和评论的时候需要区分登录和未登录状态,并且登录和未登录状态都能抽象出一系列操作(转发和评论)。
先看下ui


image.png

当点击转发的时候如果在登录状态下就提示转发成功,未登录的状态就跳转登录画面.
具体代码实现:

public class MainActivity extends AppCompatActivity implements View.OnClickListener{

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        findViewById(R.id.forward_btn).setOnClickListener(this);
        findViewById(R.id.login_btn).setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()){
            case R.id.forward_btn:
                LoginContext.getLoginContext().forward(this);
                break;
            case R.id.login_btn:
                LoginContext.getLoginContext().setState(new LogoutState());
                break;
        }
    }
}
public class LoginActivity extends AppCompatActivity {

    private EditText username;
    private EditText password;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_login);

        username = findViewById(R.id.username_et);
        password = findViewById(R.id.password_et);

        findViewById(R.id.login_btn).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                login();
                finish();
            }
        });
    }

    private void login(){
        String userName = username.getText().toString().trim();
        String passWord = password.getText().toString().trim();
        //发送网络请求登陆
        LoginContext.getLoginContext().setState(new LoginedState());
        Toast.makeText(getApplicationContext(),"登录成功",Toast.LENGTH_LONG).show();
    }
}

对状态操作进行抽象

public interface UserState {
    /**
     * 转发
     */
    void forward(Context context);

    /**
     * 评论
     */
    void comment(Context context);
}
public class LoginedState implements UserState{
    @Override
    public void forward(Context context) {
        Toast.makeText(context,"转发微博",Toast.LENGTH_LONG).show();
    }

    @Override
    public void comment(Context context) {
        Toast.makeText(context,"评论微博",Toast.LENGTH_LONG).show();
    }
}
public class LogoutState implements UserState{
    @Override
    public void forward(Context context) {
        gotoLoginActivity(context);
    }

    @Override
    public void comment(Context context) {
        gotoLoginActivity(context);
    }

    private void gotoLoginActivity(Context context){
        Intent intent = new Intent(context,LoginActivity.class);
        context.startActivity(intent);
    }
}

Context类供客户端调用

public class LoginContext implements UserState{
    UserState mState = new LogoutState();

    static LoginContext sLoginContext = new LoginContext();

    private LoginContext() {
    }

    public static LoginContext getLoginContext(){
        return sLoginContext;
    }

    public void setState(UserState userState){
        mState = userState;
    }

    @Override
    public void forward(Context context) {
        mState.forward(context);
    }

    @Override
    public void comment(Context context) {
        mState.comment(context);
    }
}

另外再举一个栗子,目前有款下载上传文件的app, 在3g,wifi,和无网络状态下它们下载和上传的操作也不一样,3g,wifi,和无网络状态下我们可以抽象出来上传和下载的操作,这里同样符合使用状态模式的要求。

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

推荐阅读更多精彩内容