乐趣区

关于设计模式:设计模式学习笔记二十三状态模式

1 概述

1.1 引言

状态模式用于解决零碎中简单对象的状态转换以及不同状态下行为的封装问题。当零碎中的某个对象存在多个状态,这些状态之间能够进行转换,而且对象在不同状态下行为不雷同时能够应用状态模式。

状态模式将一个对象的状态从该对象中分离出来,封装到专门的状态类中,使得对象状态能够灵便变动,对于客户端而言,毋庸关怀对象状态的转换以及对象所处的以后状态,无论对于何处状态对象,客户端都能够一致性地解决。

1.2 定义

状态模式:容许一个对象在其外部状态扭转时扭转它的行为,对象看起来仿佛是批改了它的类。

状态模式是一种对象行为型模式。

1.3 结构图

1.4 角色

  • Context(环境类):环境类角色又称上下文类,是领有多种状态的对象,环境类的状态存在多样性且在不同状态下对象的行为有所不同。在环境类中保护一个形象状态类 State 的实例,定义以后状态,实现时将具体状态类注入其中
  • State(形象状态类):申明各种不同状态对应的办法,封装具体状态类的独特办法
  • ConcreteState(具体状态类):形象状态类的子类,实现其中的具体状态行为办法

2 典型实现

2.1 步骤

  • 定义环境类:环境类蕴含一个形象状态成员,能够通过构造方法或者 setter 注入具体状态,同时蕴含业务办法,该业务办法会调用形象状态类的解决状态办法
  • 定义形象状态类:申明状态解决办法,应用环境类作为参数
  • 定义具体状态类:实现 / 继承形象状态类,按理论须要实现其中的状态解决办法

2.2 环境类

class Context
{private State state = new ConcreteState1();

    public void setState(State state)
    {this.state = state;}

    public void request()
    {state.handle(this);
    }
}

具备默认初始化状态,能够通过 setter 批改状态,request为环境类业务办法,其中调用了形象状态类的状态解决办法。

2.3 形象状态类

interface State
{void handle(Context context);
}

这里设计为接口,应用环境类作为参数。

2.4 具体状态类

class ConcreteState1 implements State
{
    @Override
    public void handle(Context context)
    {System.out.println("具体状态 1 办法");
        context.setState(new ConcreteState2());
    }
}

class ConcreteState2 implements State
{
    @Override
    public void handle(Context context)
    {System.out.println("具体状态 2 办法");
        context.setState(new ConcreteState1());
    }
}

两个具体状态类,执行完具体状态类外面的代码后,通过环境类参数将环境类切换到另一个状态。

2.5 客户端

public static void main(String[] args)
{Context context = new Context();
    context.request();
    context.request();}

客户端间接调用环境类业务办法,输入如下:

3 实例

设计一个银行账户,账户容许不同的状态,余额大于 0 处于失常状态,余额大于 -2000 小于 0 处于透支状态,余额小于等于 -2000 解决受限状态。依据余额不同状态可产生转换,应用状态模式进行设计。

设计如下:

  • 环境类:Account
  • 形象状态类:State
  • 具体状态类:NormalState+OverdraftState+RestrictedState

首先是环境类:

class Account
{private State state = new NormalState(this);
    private double balance = 0.0;

    public void setState(State state)
    {this.state = state;}

    public void deposit(double amount)
    {
        balance += amount;
        state.check();}

    public void withdraw(double amount)
    {
        balance -= amount;
        state.check();}

    public double getBalance()
    {return balance;}

    @Override
    public String toString()
    {return "以后状态:"+state;}

}

初始化为失常状态,余额为 0,在贷款 / 取款中调用形象状态类的状态查看办法,也就是状态转换交由状态类解决。

接着是形象状态类:

abstract class State
{
    protected Account account;
    public abstract void check();}

领有一个环境类的援用,以便应用环境类的 setState 扭转状态办法。

最初是具体状态类:

class NormalState extends State
{public NormalState(Account account)
    {this.account = account;}

    @Override
    public void check()
    {if(account.getBalance() >= -2000 && account.getBalance() < 0)
            account.setState(new OverdraftState(account));
        else if(account.getBalance() < -2000)
            account.setState(new RestrictedState(account));
    }

    @Override
    public String toString()
    {return "失常状态";}
}

class OverdraftState extends State
{public OverdraftState(Account account)
    {this.account = account;}

    @Override
    public void check()
    {if(account.getBalance() >= 0)
            account.setState(new NormalState(account));
        else if(account.getBalance() < -2000)
            account.setState(new RestrictedState(account));
    }

    @Override
    public String toString()
    {return "透支状态";}
}

class RestrictedState extends State
{public RestrictedState(Account account)
    {this.account = account;}

    @Override
    public void check()
    {if(account.getBalance() >= 0)
            account.setState(new NormalState(account));
        else if(account.getBalance() >= -2000 && account.getBalance() < 0)
            account.setState(new OverdraftState(account));
    }

    @Override
    public String toString()
    {return "受限状态";}
}

三个具体状态类别离示意三种不同状态,在其中的 check 办法中进行状态查看以及切换状态。

测试:

public static void main(String[] args)
{Account account = new Account();
    account.deposit(1000);
    System.out.println(account);        
    account.withdraw(2000);
    System.out.println(account);
    account.withdraw(2000);
    System.out.println(account);
    account.deposit(10000);
    System.out.println(account);
}

客户端不须要理睬具体状态,按失常流程操作即可,上面是输入:

4 共享状态

有时候多个环境对象可能须要共享同一个状态,这时须要将状态对象定义为环境类的动态成员,例子如下:

某个零碎要求两个开关对象必须处于一样的状态,要么同时关,要么同时开,开关能够自在切换状态,应用状态模式进行设计。

设计如下:

  • 环境类:Switch
  • 形象状态类:State
  • 具体状态类:OffState+OnState

环境类:

class Switch
{
    private State state;
    private static final State onState;
    private static final State offState;

    private String name;

    static
    {onState = new OnState();
        offState = new OffState();}

    public Switch(String name)
    {
        this.name = name;
        this.state = offState;
    }

    public String getName()
    {return name;}

    public void setState(State state)
    {this.state = state;}

    public static State getOnState()
    {return onState;}

    public static State getOffState()
    {return offState;}

    public void on()
    {state.on(this);
    }

    public void off()
    {state.off(this);
    }
}

环境类初始化两个动态的开与关状态,在 on 以及 off 中调用状态办法。

形象状态:

interface State
{void on(Switch s);
    void off(Switch s);
}

具体状态:

class OnState implements State
{
    @Override
    public void on(Switch s)
    {System.out.println("开关"+s.getName()+"曾经关上");
    }

    @Override
    public void off(Switch s)
    {System.out.println("开关"+s.getName()+"敞开");
        s.setState(Switch.getOffState());
    }
}

class OffState implements State
{
    @Override
    public void on(Switch s)
    {System.out.println("开关"+s.getName()+"开启");
        s.setState(Switch.getOnState());
    }

    @Override
    public void off(Switch s)
    {System.out.println("开关"+s.getName()+"曾经敞开");
    }
}

实现形象状态的开关办法,处于关状态时调用 on 会切换到开状态,调用 off 则不解决,处于开状态同理。

测试:

public static void main(String[] args)
{Switch a = new Switch("A");
    Switch b = new Switch("B");
    a.on();
    b.on();
    a.off();
    b.off();
    a.on();
    b.on();}

输入:

5 环境类实现状态切换

在下面的例子中,都是通过具体状态类进行状态切换,比方:

class RestrictedState extends State
{
    //...
    @Override
    public void check()
    {if(account.getBalance() >= 0)
            account.setState(new NormalState(account));
        else if(account.getBalance() >= -2000 && account.getBalance() < 0)
            account.setState(new OverdraftState(account));
    }
}
class OnState implements State
{
    //...   
    @Override
    public void off(Switch s)
    {System.out.println("开关"+s.getName()+"敞开");
        s.setState(Switch.getOffState());
    }
}

状态切换也能够由环境类进行对立解决,然而如果减少新的状态类可能须要批改环境类代码。例子如下:

设计一个放大镜工具,单击一次放大一倍,单击两次再放大一杯,第三次就复原默认大小,应用状态模式进行设计。

设计如下:

  • 环境类:Screen
  • 形象状态类:State
  • 具体状态类:NormalState+LargerState+LargestState

环境类:

class Screen
{
    private State state;
    private static final State normalState;
    private static final State largerState;
    private static final State largestState;

    static
    {normalState = new NormalState();
        largerState = new LargerState();
        largestState = new LargestState();}

    public Screen()
    {this.state = normalState;}

    public void onClick()
    {if(state == normalState)
            state = largerState;
        else if(state == largerState)
            state = largestState;
        else
            state = normalState;
        state.display();}
}

首先初始化各个状态,而后在 onClick() 中由环境类管制状态切换。

状态类:

interface State
{void display();
}

class NormalState implements State
{
    @Override
    public void display()
    {System.out.println("失常大小");
    }
}

class LargerState implements State
{
    @Override
    public void display()
    {System.out.println("两倍大小");
    }
}

class LargestState implements State
{
    @Override
    public void display()
    {System.out.println("四倍大小");
    }
}

测试:

public static void main(String[] args)
{Screen screen = new Screen();
    screen.onClick();
    screen.onClick();
    screen.onClick();}

输入如下:

6 次要长处

  • 封装转换规则:状态模式中能够将状态的转换代码封装在环境类或具体状态类中,能够将状态转换代码进行集中管理,而不是扩散在一个个业务办法中
  • 通过注入状态批改行为:将所有与某个状态无关的行为放到一个类中,只须要注入一个不同的状态对象即可使环境领有不同的行为
  • 状态转换逻辑与状态对象一体化:容许状态转换逻辑与状态对象合成一体,而不是提供一个微小的条件语句块,状态模式能够防止应用宏大的条件语句来将业务办法和状态转换代码交错在一起

7 次要毛病

  • 增大运行开销:应用状态模式会减少零碎中类和对象的个数
  • 实现简单:状态模式的程序结构实现简单,如果使用不当将导致程序结构和代码凌乱,减少零碎设计难度
  • 对 OCP 反对不好:减少新的状态类须要批改那些负责状态转换的源代码,否则无奈转换到新增状态,而且批改某个状态类的行为也须要批改对应的源代码

8 实用场景

  • 对象的行为依赖于它的状态,状态的扭转会导致行为的变动
  • 在代码中蕴含大量与对象状态无关的条件语句,这些条件语句的呈现会导致代码可维护性和灵活性变差,不能不便地减少和删除状态,并且导致客户类与类库之间的耦合度加强

9 总结

如果感觉文章难看,欢送点赞。

同时欢送关注微信公众号:氷泠之路。

退出移动版