Android设计模式-状态模式

来源:互联网 发布:罗志祥 selina 知乎 编辑:程序博客网 时间:2024/06/06 05:41

状态模式介绍

状态模式中的行为由状态决定,不同的状态下有不同的行为。状态模式和策略模式的结构几乎完全一样,但它们的目的和本质完全不同。状态模式是平行的、不可替换的。用一句话表述,策略模式是彼此独立、可替换的。一句话描述就是,状态模式把对象的行为包装在不同状态的对象中,每一个状态对象都有一个共同的抽象状态基类。状态模式的意图是让一个对象再其内部状态改变的时候,其行为也随之改变。

状态模式定义

当一个对象的内在状态改变时允许改变其行为,这个对象看起来是改变了其类。

使用场景

(1)一个对象的行为取决于它的状态,并且必须在运行时根据状态改变它的行为。

(2)代码中包含大量的与对象状态有关的条件语句,如操作中更包含大量的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("没有开机!");        }    }}

在TVController类中,通过mState字段来判断电视的开机与关机状态。并通过判断这个字段来决定不同的操作是否该执行。如果状态不是两个而是变得更多、遥控器的功能变得更多,这就需要更多的if-else条件判断,而这些代码都充斥在一个类中。这就使得这个类变得更加难以维护。

状态模式就是为了解决该问题而出现的。我们将这些状态用对象代替,将这些状态封到对象中,使得在不同的对象中有不同的实现,这样就可以将这些if-else从TVController类中去掉:

//电视状态接口,定义了电视操作的函数public interface TvState {    public void nextChannel();    public void prevChannel();    public void turnUp();    public void turnDown();}//关机状态,只有开机功能是有效的public class PowerOffState implements TvState {    @Override    public void nextChannel() {    }    @Override    public void prevChannel() {    }    @Override    public void turnUp() {    }    @Override    public 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 interface PowerController {    void powerOn();    void powerOff();}//电视遥控器public class TvController implements PowerController {    TvState mState;    public void setTvState(TvState mTvState) {        this.mState = mTvState;    }    @Override    public void powerOn() {        setTvState(new PowerOnState());        System.out.println("开机啦");    }    @Override    public void powerOff() {        setTvState(new PowerOffState());        System.out.println("关机啦");    }    public void nextChannel() {        mTvState.nextChannel();    }    public void prevChannel() {        mTvState.prevChannel();    }    public void turnUp() {        mTvState.turnUp();    }    public void turnDown() {        mTvState.turnDown();    }}

下面是客户端代码:

public class Client {    public static void main(String[] args) {        TvController tvController = new TvController();        //设置开机状态        tvController.powerOn();        //下一频道        tvController.nextChannel();        //调高音量        tvController.turnUp();        //设置关机状态        tvController.powerOff();        //调高音量,此时不会生效        tvController.turnUp();    }}
//输出开机啦下一频道调高音量关机啦

在现实中,抽象了一个TvState接口,该接口中有操作电视的所有函数,并且有两个实现类:开机状态和关机状态。不同状态下的同一个操作会有不同的相应。

状态模式将这些行为封装到状态类中,在进行操作的时候将这些功能转发给状态对象,不同的状态有不同的实现,这样就通过多态的形式去除了重复、杂乱的if-else语句,这就是状态模式的精髓所在。

状态模式实战

在Android开发中,状态模式最常见的地方应该是用户登录系统。在用户已登录和未登录的情况下,对于同一点击事件的响应行为是不一样的。比如,在新浪微博中,用户再未登录的情况下点击转发按钮,此时会先让用户登录,然后在执行转发。而如果是已登录,则直接进行转发操作即可。

下面就演示了这个登陆过程:有两个Activity,一个是MainActivity,它是进如应用的第一个界面,上面有两个按钮,一个用于转发,一个用于注销;另一个Activity是LoginActivity ,它是用户登录界面。

用户默认状态时未登录,此时用户再MainActivity中点击转发按钮就会先条转到LoginActivity,待登陆完毕后再回到MainActivity。此时用户再点击转发按钮就能实现真正的转发功能。

//MainActivitypublic class MainActivity extends Activity {    @Override    protected void onCreate(Bundle saveInstanceState) {        super.onCreate(saveInstanceState);        setContentView(R.layout.activity_main);        //转发按钮        findViewById(R.id.forward_btn).setOnClickListener(new OnClickListener() {            @Override            public void onClick(View v) {                LoginContext.getLoginContext().forward(MainActivity.this);            }        });        //注销按钮        findViewById(R.id.logout_btn).setOnClickListener(new OnClickListener() {            //设置为注销状态            LoginContext.getLoginContext().setState(new LogOutState());        });    }} 

LoginActivity则是在用户输入用户名和密码后登陆,成功后将LoginContext的状态设为已登录状态,并返回MainActivity页面:

//LoginActivitypublic class LoginActivity extends activity {    EditText userNameEditText;    EditText pwdEditText;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_login);        //绑定控件        ... ...        //登录按钮        ... ...        @Override        public void onClick(View v) {            login();            finish();        }        private void login() {            String userName = userNameEditText.getText().toString().trim();            String pwd = pwdEditText.getText().toString().trim();            //执行网络请求,进行登录......            //登陆后修改为已登录状态            LoginContext.getLoginContext().setState(new LoginedState());            Toast.makeText(getApplicationContext(),"登录成功", Toast.LENGTH_LONG).show();        }    }}

这里的LoginContext就是“遥控器”这个角色,是用户的操作对象和状态管理对象。LoginContext将相关操作委托给状态对象,这样在状态改变时,LoginContext的行为就发生了改变:

//LoginContextpublic class LoginContext {    //用户状态,默认为 未登录状态    UserState mState = new LogoutState();    //单例 恶汉模式    static LoginContext sLoginContext = new LoginContext();    private LoginContext() {    }    public static LoginContext getLoginContext() {        return sLoginContext;    }    public void setState(UserState state) {        this.mState = state;    }    //转发    public void forward(Context context) {        mState.forward(context);    }    public void comment(Context context) {        mState.comment(context);    }}

LoginContext通过setState来对状态进行修改,并且把操作委托给mState成员。不同的状态对象对于同一个操作进行不同的处理:

//用户状态接口public interface UserState {    //转发    public void forward(Context context);    //评论    public void conmment(Context context);}//已登录状态public class LoginedState implements UserState {    @Override    public void forward(Context context) {        Toast.makeText(getApplicationContext(),"转发微博", Toast.LENGTH_LONG).show();    }    @Override    public void comment(Context context) {        Toast.makeText(getApplicationContext(),"评论微博", Toast.LENGTH_LONG).show();    }}//注销状态public class LoginOutState implements UserState {    @Override    public void forward(Context context) {        gotoLoginAcitvity(context);    }    @Override    public void comment(Context context) {        gotoLoginAcitvity(context);    }    protected void gotoLoginAcitvity(Context context) {        Intent intent = new Intent(context, LoginActivity.class);        context.startActivity(intent);    }}

在UserState中有两个方法,转发和评论。已登录状态和未登录状态下,它们的操作并不相同。

总结

状态模式的关键点在于不同的状态下对于同一种香味的不同响应,这其实就是一个将if-else用多态来实现的一个具体示例。但是这比if-else模式更加简洁、扩展性更好、更加美观。但这并不意味着任何使用if-else的地方都必须使用状态模式,这要根据特定的场景来决定。

优点:

State模式将所有与一个特定状态相关的行为都放入一个状态对象中,它提供了一个更好的方法来组织与特定状态相关的代码,将繁琐的状态判断呈结构清晰的状态类族,在避免代码膨胀的同时也保证了可扩展性与可维护性。

缺点:

状态模式的使用必然会增加系统类和对象的个数。

0 0
原创粉丝点击