State???
State模式中,我们用类来表示状态。以类来表示状态后,我们就能通过切换类来方便地改变对象的状态。当需要增加新的状态时,如何修改代码这个问题也会很明确。
- 直接用状态代替硬编码
依赖于状态的处理,来执行具体的操作
理清职责
- 实现功能:
·有一个金库
·金库与警报中心相连
·金库里有警铃和正常通话用的电话·金库里有时钟,监视着现在的时间
·白天的时间范围是9:00~16:59,晚上的时间范围是17:00~23:59和0:00~8:59
·金库只能在白天使用
·白天使用金库的话,会在警报中心留下记录
·晚上使用金库的话,会向警报中心发送紧急事态通知
·任何时候都可以使用警铃
·使用警铃的话,会向警报中心发送紧急事态通知
·任何时候都可以使用电话(但晚上只有留言电话)
·白天使用电话的话,会呼叫警报中心
·晚上用电话的话,会呼叫警报中心的留言电话
名字=======》》》》》说明
State ||表示金库状态的接口
DayState ||表示“白天”状态的类。它实现了State接口
NightState ||表示“晚上”状态的类。它实现了State接口
Context ||表示管理金库状态,并与警报中心联系的接口
SafeFrame ||实现了Context接口。在它内部持有按钮和画面显示等UI信息
Main || 测试程序行为的类
- 使用与不使用状态模式对比
- 不使用
使用金库时被调用的方法(){
if(白天){
向警报中心报告使用记录
]elseif(晚上){
向警报中心报告紧急事态
警铃响起时被调用的方法(){
向警报中心报告紧急事态
正常通话时被调用的方法(){
if(白天){
呼叫警报中心
}elseif(晚上){
呼叫警报中心的留言电话
}
- 使用
表示百天的状态的类{
使用金库时被调用的方法(){
向警报中心报告使用记录
警铃响起时被调用的方法(){
向警报中心报告紧急事态
正常通话时被调用的方法(){
呼叫警报中心
表示晚上的状态的类{
使用金库时被调用的方法(){
向警报中心报告紧急事态
警铃响起时被调用的方法(){
向警报中心报告紧急事态
正常通话时被调用的方法(){
呼叫警报中心的留言电话
- 相关设计模式
◆Singleton模式(第5章)Singleton 模式常常会出现在ConcreteState角色中。在示例程序中,我们就使用了Singleton模式。这是因为在表示状态的类中并没有定义任何实例字段(即表示实例的状态的字段)。
◆Flyweight模式(第20章)在表示状态的类中并没有定义任何实例字段。因此,有时我们可以使用Flyweight模式在多个Context 角色之间共享ConcreteState角色。
UML
时序图:
Code
- DayState NightState State
public interface State {
//设置时间
void doclock(Context context, int hour);
// 使用金库
void doUse(Context context);
// 按下警铃
void doAlarm(Context context);
// 正常通话
void dophone(Context context);
}
public class NightState implements State {
private NightState() {
}
private static NightState singleton = new NightState();
public static State getInstance() {
return (State) singleton;
}
@Override
public void doclock(Context context, int hour) {
if (hour >= 9 && hour < 17) {
context.changeState(DayState.getInstance());
}
}
@Override
public void doUse(Context context) {
context.recordLog("使用金库[晚上]");
}
@Override
public void doAlarm(Context context) {
context.callSecurityCenter("按下警铃[晚上]");
}
@Override
public void dophone(Context context) {
context.recordLog("正常通话[晚上]");
}
@Override
public String toString() {
return "DayState{晚上}";
}
}
public class DayState implements State {
/**
* 这里使用单例模式,因为每次改变一次状态都会生成一次实例,非常浪费内存与时间
*/
private DayState() {
}
private static DayState singleton = new DayState();
public static State getInstance() {
return singleton;
}
@Override
public void doclock(Context context, int hour) {
if (hour < 9 || hour >= 17) {
context.changeState(NightState.getInstance());
}
}
@Override
public void doUse(Context context) {
context.recordLog("使用金库[白天]");
}
@Override
public void doAlarm(Context context) {
context.callSecurityCenter("按下警铃[白天]");
}
@Override
public void dophone(Context context) {
context.recordLog("正常通话[白天]");
}
@Override
public String toString() {
return "DayState{白天}";
}
}
- Context 、SateFrame 、MainT
···
public class MainT {
public static void main(String[] args) {
SateFrame frame = new SateFrame("Safe Smaple");
// 24个小时制
while (true){
for (int i = 0; i < 24; i++) {
frame.setClock(i);
try {
Thread.sleep(1000);
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
}
}
public interface Context {
//设置时间
void setClock(int hour);
// 改变状态
void changeState(State state);
// 联系警报中心
void callSecurityCenter(String msg);
// 在警报中心留下记录
void recordLog(String msg);
}
public class SateFrame extends Frame implements ActionListener,Context {
// 显示时间
private TextField textClock=new TextField(60);
// 显示警报中心的记录
private TextArea textScreen=new TextArea(10,60);
private Button buttonUse=new Button("使用金库");
private Button buttonALarm=new Button("按下警铃");
private Button buttonPhone=new Button("正常通话");
private Button buttonExit=new Button("退出");
// 初始状态为白天
private State state=DayState.getInstance();
public SateFrame(String title) throws HeadlessException {
super(title);
setBackground(Color.lightGray);
setLayout(new BorderLayout());
add(textClock,BorderLayout.NORTH);
textClock.setEditable(false);
add(textScreen,BorderLayout.CENTER);
textScreen.setEditable(false);
Panel panel = new Panel();
panel.add(buttonUse);
panel.add(buttonALarm);
panel.add(buttonPhone);
panel.add(buttonExit);
add(panel,BorderLayout.SOUTH);
pack();
show();
buttonUse.addActionListener(this);
buttonALarm.addActionListener(this);
buttonPhone.addActionListener(this);
buttonExit.addActionListener(this);
}
/**
* 可以看出这里的操作就简化很多了:
* 基本只有业务逻辑代码:
* 判断状态相关的代码可以直接由相关的状态代码实现,
* 即为由类的状态代替了if else代码
*/
@Override
public void actionPerformed(ActionEvent e) {
if(e.getSource()==buttonUse){
state.doUse(this);
}else if(e.getSource()==buttonALarm){
state.doAlarm(this);
}else if(e.getSource()==buttonPhone){
state.dophone(this);
}else if(e.getSource()==buttonExit){
System.exit(0);
}else{
System.out.println("?");
}
}
@Override
public void setClock(int hour) {
String clockstring="现在时间是:";
if(hour<10){
clockstring+="0"+hour+":00";
}else{
clockstring+=hour+":00";
}
System.out.println(clockstring);
textClock.setText(clockstring);
state.doclock(this,hour);
}
@Override
public void changeState(State state) {
System.out.println("从"+this.state+"状态变为了"+state+"状态。");
this.state=state;
}
@Override
public void callSecurityCenter(String msg) {
textScreen.append("调用---"+msg+"
");
}
@Override
public void recordLog(String msg) {
textScreen.append("记录---"+msg+"
");
}
}
···