Android设计模式-状态模式

Posted vanpersie_9987

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android设计模式-状态模式相关的知识,希望对你有一定的参考价值。

状态模式介绍

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

状态模式定义

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

使用场景

(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。此时用户再点击转发按钮就能实现真正的转发功能。

//MainActivity
public 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页面:

//LoginActivity
public 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的行为就发生了改变:

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

缺点:

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

以上是关于Android设计模式-状态模式的主要内容,如果未能解决你的问题,请参考以下文章

javascript设计模式——状态模式

android深入之设计模式托付模式

享元模式(Flyweight Pattern)

设计模式之迭代器模式详解(foreach的精髓)

设计模式-深入单例模式精髓-剖析单例模式适用场景以及多线程问题

(十七)迭代器模式详解(foreach的精髓)