Android编程设计模式之状态模式详解

本文实例讲述了Android编程设计模式之状态模式。分享给大家供大家参考,具体如下:

一、介绍

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

二、定义

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

三、使用场景

(1)一个对象的行为取决于它的状态,并且它必须在运行时根据状态改变它的行为。
(2)代码中包含大量与对象状态有关的条件语句,例如,一个操作中含有庞大的多分支语句(if-else或switch-case),且这些分支依赖于该对象的状态。

状态模式将每一个条件分支放入一个独立的类中,这使得你可以根据对象自身的情况将对象的状态作为一个对象,这一对象可以不依赖与其他对象而独立变化,这样通过多态去除过多的、重复的if-else等分支语句。

四、状态模式的UML类图

UML类图:

角色介绍:

Context:环境类,定义客户感兴趣的接口,维护一个State子类的实例,这个实例定义了对象的当前状态。

State:抽象状态类或状态接口,定义一个或者一组接口,表示该状态下的行为。

ConcreteStateA、ConcreteStateB:具体状态类,每一个具体的状态类实现抽象State中定义的接口,从而达到不同状态下的不同行为。

五、简单示例

下面我们就以电视遥控器为例来演示一下状态模式的实现。我们首先将电视的状态简单分为开机状态和关机状态,在开机状态下可以通过遥控器进行频道切换、调整音量等操作,但是,此时重复按开机键是无效的;而在关机状态下,频道切换、调整音量、关机都是无效的操作,只有按开机按钮时才会生效。也就是说电视的内部状态决定了遥控器的行为。

首先是普通的实现方法:

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(){
    if(mState ==POWER_OFF){
      System.out.println("电视开机了");
    }
    mState = POWER_ON;
  }
  public void powerOff(){
    if(mState ==POWER_ON){
      System.out.println("电视关机了");
    }
    mState = POWER_OFF;
  }
  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("没有开机");
    }
  }
}

可以看到,每次执行通过判断当前状态来进行操作,部分的代码重复,假设状态和功能增加,就会越来越难以维护。这时可以使用状态模式,如下:

电视的状态接口:

/**
 * 电视状态接口,定义了电视的操作函数
 *
 **/
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 {
  public void powerOn();
  public void powerOff();
}

电视遥控器:

/**
 * 电视遥控器
 *
 * */
public class TVController implements PowerController{
  TVState mTVState;
  public void setTVState(TVState mTVState){
    this.mTVState = 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.turnDown();
  }
}

输出结果如下:

开机了
下一频道
调高音量
关机了

上述实现中,我们抽象了一个TVState接口,该接口中有操作电视的所有函数,该接口有两个实现类,即开机状态(PowerOnState)和关机状态(PowerOffState)。开机状态下只有开机功能是无效的,也就是说在已经开机的时候用户在按开机键不会产生任何反应;而在关机状态下,只有开机功能是可用的,其他功能都不会生效。同一个操作,如调高音量的turnUp函数,在关机状态下无效,在开机状态下就会将电视的音量调高,也就是说电视内部状态影响了电视遥控器的行为。状态模式将这些行为封装到状态类中,在进行操作时将这些功能转发给状态对象,不同的状态有不同的实现,这样就通过多态的形式去除了重复、杂乱的if-else语句,这也正是状态模式的精髓所在。

六、Android实战中的使用

1、登录系统,根据用户是否登录,判断事件的处理方式。
2、Wi-Fi管理,在不同的状态下,WiFi的扫描请求处理不一。

下面以登录系统为例讲解下状态模式在实战中的使用:

在android开发中,我们遇到登录界面是十分常见的,而状态设计模式在登录界面的应用十分广泛,用户在登录状态下和未登录状态下,对逻辑的操作是不一样的。例如最常见的情况就是在玩新浪微博的时候,用户在登录的情况下才能完成评论和转发微博的操作;而当用户处于未登录的情况下要执行转发和评论微博的操作需要进入登录界面登录以后才能执行,所以面对这两者不同的状况,利用状态设计模式来设计这个例子最好不过。

1、状态基类

前面我们讲过状态设计模式的原理实则是多态,在这里我们用UserState接口表示此基类,包换转发操作和评论这两种状态,代码如下:

public interface UserState {
  /**
   * 转发操作
   * @param context
   */
  public void forword(Context context);
  /**
   * 评论操作
   * @param context
   */
  public void commit(Context context);
}

2、用户在登录和未登录两种状况下的实现类LoginState和LogoutState;代码如下:

在LoginState.java中,用户是可以执行转发和评论操作。

public class LoginState implements UserState{
  @Override
  public void forword(Context context) {
    Toast.makeText(context, "转发成功", Toast.LENGTH_SHORT).show();
  }
  @Override
  public void commit(Context context) {
    Toast.makeText(context, "评论成功", Toast.LENGTH_SHORT).show();
  }
}

在LogoutState.java中,用户在未登录的情况下不允许执行操作,而是应该跳转到登录界面执行登录以后才可以执行。

public class LogoutState implements UserState{
  /**
   * 跳转到登录界面登录以后才能转发
   */
  @Override
  public void forword(Context context) {
    gotoLohinActivity(context);
  }
  /**
   * 跳转到登录界面登录以后才能评论
   */
  @Override
  public void commit(Context context) {
    gotoLohinActivity(context);
  }
  /**
   * 界面跳转操作
   * @param context
   */
  private void gotoLohinActivity(Context context){
    context.startActivity(new Intent(context,LoginActivity.class));
  }
}

3、操作角色LoginContext

这里的LoginContext就是在状态模式的Context角色,是用户操作对象和管理对象,LoginContext委托相关的操作给状态对象,在其中状态的发生改变,LoginContext的行为也发生改变。LoginContext的代码如*下:

温馨提示:

这里我们用到单例就是为了全局只有一个LoginContext去控制用户状态;

public class LoginContext {
  //用户状态默认为未登录状态
  UserState state = new LogoutState();
  private LoginContext(){};//私有构造函数,避免外界可以通过new 获取对象
  //单例模式
  public static LoginContext getInstance(){
   return SingletonHolder.instance;
  }
  /**
  *静态代码块
  */
  private static class SingletonHolder{
   private static final LoginContext instance = new LoginContext();
  }
  public void setState(UserState state){
    this.state = state;
  }
  //转发
  public void forward(Context context){
    state.forword(context);
  }
  //评论
  public void commit(Context context){
    state.commit(context);
  }
}

4、界面展示

LoginActivity.java,此界面执行登录操作,登录成后把 LoginContext.getInstance().setState(new LoginState());设置为登录状态,在MainActivity中就执行的是登录状态下的操作,即可以转发可评论;

public class LoginActivity extends Activity implements OnClickListener{
  private static final String LOGIN_URL = "http://10.10.200.193:8080/Day01/servlet/LoginServlet";
  private EditText et_username;
  private EditText et_password;
  private Button btn_login;
  private String username;
  private String password;
  private KJHttp http;
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_login);
    initView();
    initData();
  }
  private void initView() {
    et_username = (EditText) findViewById(R.id.et_username);
    et_password = (EditText) findViewById(R.id.et_password);
    btn_login = (Button) findViewById(R.id.btn_login);
    btn_login.setOnClickListener(LoginActivity.this);
  }
  private void initData() {
    http = new KJHttp();
  }
  /**
   * 执行登录操作
   *
   * @param username2
   * @param password2
   */
  protected void sendLogin(String username2, String password2) {
    HttpParams params = new HttpParams();
    params.put("username", "user1");
    params.put("password", "123456");
    http.post(LOGIN_URL, params, new HttpCallBack() {
      @Override
      public void onSuccess(String t) {
        if ("200".equals(t)) {
          //设置为登录状态
          LoginContext.getInstance().setState(new LoginState());
          startActivity(new Intent(LoginActivity.this,MainActivity.class));
          finish();
          Toast.makeText(LoginActivity.this, "登录成功", Toast.LENGTH_SHORT).show();
        }
      }
    });
  }
  @Override
  public void onClick(View v) {
    switch (v.getId()) {
    case R.id.btn_login:
      username = et_username.getEditableText().toString().trim();
      password = et_password.getEditableText().toString().trim();
      if (TextUtils.isEmpty(username) || TextUtils.isEmpty(password)) {
        Toast.makeText(LoginActivity.this, "用户名密码不能为空", Toast.LENGTH_SHORT).show();
        return;
      }
      sendLogin(username, password);
      break;
    }
  }
}

MainActivity.java,在用户登录成功后,点击转发和评论执行的是登录状态下的操作,而当用户注销时,我们把LoginContext的状态设置为未登录状态;LoginContext.getInstance().setState(new LogoutState());此时在点击转发和评论操作时就会跳到用户登录界面。

public class MainActivity extends Activity {
  private Button btn_forward;
  private Button btn_commit;
  private Button btn_logout;
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    initView();
    initListener();
  }
  private void initView() {
    btn_forward = (Button) findViewById(R.id.btn_forward);
    btn_commit = (Button) findViewById(R.id.btn_commit);
    btn_logout = (Button) findViewById(R.id.btn_logout);
  }
  private void initListener() {
    //转发操作
    btn_forward.setOnClickListener(new OnClickListener() {
      @Override
      public void onClick(View v) {
        //调用LoginContext里面的转发函数
        LoginContext.getInstance().forward(MainActivity.this);
      }
    });
    //评论操作
    btn_commit.setOnClickListener(new OnClickListener() {
      @Override
      public void onClick(View v) {
        //调用LoginContext里面的转发函数
        LoginContext.getInstance().commit(MainActivity.this);
      }
    });
    //注销操作
    btn_logout.setOnClickListener(new OnClickListener() {
      @Override
      public void onClick(View v) {
        //设置为注销状态
        LoginContext.getInstance().setState(new LogoutState());
      }
    });
  }
}

七、总结

状态模式的关键点在于不同的状态下对于同一行为有不同的响应,这其实就是一个将if-else用多态来实现的一个具体示例。在if-else或者switch-case形式下根据不同的状态进行判断,如果是状态A那么执行方法A,状态B执行方法B,但这种实现使得逻辑耦合在一起,易于出错,通过状态模式能够很好的消除这类”丑陋“的逻辑处理,当然并不是任何出现if-else的地方都应该通过状态模式重构,模式的运用一定要考虑所处的情景以及你要解决的问题,只有符合特定的场景才建议使用对应的模式。

优点:

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

缺点:

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

更多关于Android相关内容感兴趣的读者可查看本站专题:《Android开发入门与进阶教程》、《Android调试技巧与常见问题解决方法汇总》、《Android基本组件用法总结》、《Android视图View技巧总结》、《Android布局layout技巧总结》及《Android控件用法总结》

希望本文所述对大家Android程序设计有所帮助。

(0)

相关推荐

  • Android设计模式系列之组合模式

    Android中对组合模式的应用,可谓是泛滥成粥,随处可见,那就是View和ViewGroup类的使用.在android UI设计,几乎所有的widget和布局类都依靠这两个类. 组合模式,Composite Pattern,是一个非常巧妙的模式.几乎所有的面向对象系统都应用到了组合模式. 1.意图 将对象View和ViewGroup组合成树形结构以表示"部分-整体"的层次结构(View可以做为ViewGroup的一部分). 组合模式使得用户对单个对象View和组合对象ViewGrou

  • Android编程设计模式之单例模式实例详解

    本文实例讲述了Android编程设计模式之单例模式.分享给大家供大家参考,具体如下: 一.介绍 单例模式是应用最广的模式之一,也可能是很多初级工程师唯一会使用的设计模式.在应用这个模式时,单例对象的类必须保证只有一个实例存在.许多时候整个系统只需要拥有一个全局对象,这样有利于我们协调系统整体的行为. 二.定义 确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例. 三.使用场景 确保某个类有且只有一个对象的场景,避免产生多个对象消耗过多的资源,或者某种类型的对象只应该有且只有一个.例

  • Android编程设计模式之工厂方法模式实例详解

    本文实例讲述了Android编程设计模式之工厂方法模式.分享给大家供大家参考,具体如下: 一.介绍 工厂方法模式(Factory Pattern),是创建型设计模式之一.工厂方法模式是一种结构简单的模式,其在我们平时开发中应用很广泛,也许你并不知道,但是你已经使用了无数次该模式了,如Android中的Activity里的各个生命周期方法,以onCreate方法为例,它就可以看作是一个工厂方法,我们在其中可以构造我们的View并通过setContentView返回给framework处理等,相关内

  • Android设计模式之策略模式详解

    策略模式 一个功能的效果,有不同的算法与策略,根据不同的选择选择不同的结果. 简单来说,只要你写过程序就用过策略模式,不要说没用过,难道if-else(switch)没用过吗-.. if-else在其实就是一个策略模式的体现,根据不同的选择处理不同的结果. 问题 如果把所有的方法全部用if-else(switch)来处理,从功能上说没问题,但是冲代码层面的维护与使用来说,if-else多了之后会让类变的过于庞大,阅读不利,修改困难 解决问题 使用策略模式,定义统一接口,每一个不同的功能(if-e

  • Android编程设计模式之Builder模式实例详解

    本文实例讲述了Android编程设计模式之Builder模式.分享给大家供大家参考,具体如下: 一.介绍 Builder模式是一步一步创建一个复杂对象的创建型模式,它允许用户在不知道内部构建细节的情况下,可以更精细的控制对象的构造流程.该模式是为了将构建复杂对象的过程和它的部件解耦,使得构建过程和部件的表示隔离开来. 因为一个复杂的对象有很多大量组成部分,例如车,有车轮.方向盘.发动机,还有各种小零件等,如何将这些部件装配成一辆汽车,这个装配过程很漫长,也很复杂,对于这种情况,为了在构建过程中对

  • Android编程设计模式之观察者模式实例详解

    本文实例讲述了Android编程设计模式之观察者模式.分享给大家供大家参考,具体如下: 一.介绍 观察者模式是一个使用率非常高的模式,它最常用的地方是GUI系统.订阅--发布系统.因为这个模式的一个重要作用就是解耦,将被观察者和观察者解耦,使得它们之间的依赖性更小,甚至做到毫无依赖.以GUI系统来说,应用的UI具有易变性,尤其是前期随着业务的改变或者产品的需求修改,应用界面也会经常性变化,但是业务逻辑基本变化不大,此时,GUI系统需要一套机制来应对这种情况,使得UI层与具体的业务逻辑解耦,观察者

  • Android编程设计模式之策略模式详解

    本文实例讲述了Android编程设计模式之策略模式.分享给大家供大家参考,具体如下: 一.介绍 在软件开发中也常常遇到这样的情况:实现某一个功能可以有多种算法或者策略,我们根据实际情况选择不同的算法或者策略来完成该功能.例如,排序算法,可以使用插入排序.归并排序.冒泡排序等. 针对这种情况,一种常规的方法是将多种算法写在一个类中.例如,需要提供多种排序算法,可以将这些算法写到一个类中,每一个方法对应一个具体的排序算法:当然,也可以将这些排序算法封装在一个统一的方法中,通过if-else-或者ca

  • android设计模式之单例模式详解

    这是我们最常见的一类模式,对这一类模式有一个通用的特点就是: 封装创建的方式和过程. 这里所谓封装就是隐藏的意思,对对象的创建方法和过程不可见,或者是虚拟的过程. 隐藏创建方式,就是如单例,工厂方法,隐藏创建过程则是指builder,原型,至于抽象工厂,我认为他包含了以上两种. 我们想想一个对象的创建有哪些步骤? 1.创建什么东西?--接口定义 2.谁创建?        --决策类or帮助类 3.如何创建?     --how,创建过程 4.什么时候创建?    --创建时机的触发 由此可知,

  • Android编程设计模式之原型模式实例详解

    本文实例讲述了Android编程设计模式之原型模式.分享给大家供大家参考,具体如下: 一.介绍 原型模式是一个创建型的模式.原型二字表明了该模型应该有一个样板实例,用户从这个样板对象中复制出一个内部属性一致的对象,这个过程也就是我们俗称的"克隆".被复制的实例就是我们所称的"原型",这个原型也是可定制的.原型模型多用于创建复杂的或者构造耗时的实例,因为这种情况下,复制一个已经存在的实例可使程序运行更高效. 二.定义 用原型实例指定创建对象的种类,并通过拷贝这些原型创

  • Android编程设计模式之抽象工厂模式详解

    本文实例讲述了Android编程设计模式之抽象工厂模式.分享给大家供大家参考,具体如下: 一.介绍 抽象工厂模式(Abstract Factory Pattern),也是创建型设计模式之一.前一节我们已经了解了工厂方法模式,那么这个抽象工厂又是怎么一回事呢?大家联想一下现实生活中的工厂肯定都是具体的,也就是说每个工厂都会生产某一种具体的产品,那么抽象工厂意味着生产出来的产品是不确定的,那这岂不是很奇怪?抽象工厂模式起源于以前对不同操作系统的图形化解决方案,如不同操作系统中的按钮和文本框控件其实现

随机推荐