论坛首页 入门技术论坛

状态模式(State)

浏览 8786 次
该帖已经被评为新手帖
作者 正文
   发表时间:2010-06-17  
抛出异常的爱 写道
PS: 这个不是状态模式的例子.

要算也应该算是迭代器的一种变型.仔细看里面有next()方法的.


写这个例子,主要是练习使用State模式和Strategy 模式 做重构,还在学习阶段!希望不要见笑!以后多多指教!
0 请登录后投票
   发表时间:2010-06-17   最后修改:2010-06-17
我自己写了一个另外一个形式的
package com.designPattern.state;

public enum State {
	RED("红灯", 2000) {
		@Override
		public State change() {
			return GREEN;
		}
	},
	GREEN("绿灯", 1000) {
		@Override
		public State change() {
			return YELLOW;

		}
	},
	YELLOW("黄灯", 500) {
		@Override
		public State change() {
			return RED;
		}
	};
	private int wait;
	private String statname;

	public abstract State change();

	public State changeState() {
		System.out.println(statname);
		try {
			Thread.sleep(wait);
		} catch (InterruptedException e) {
			System.out.println("wrong");
		}
		return change();
	}

	private State(String name, int wait) {
		statname = name;
		this.wait = wait;
	}

	public String getStatname() {
		return statname;
	}

	public static void main(String[] args) {
		GREEN.changeState();
		GREEN.changeState().changeState();
		GREEN.changeState().changeState().changeState();
	}


}


可能这也是比较正统的状态模式的实现,但是实际上,枚举和lz的状态模式都有些问题

状态模式最大的特点是能在运行期间动态的改变行为,而作为状态的对象则不依赖于其他对象而独立变化。

枚举的问题很明显,增加修改状态都很麻烦,而将状态切分成不同的类可以更灵活的添加和修改状态。

lz的状态模式我则觉得有些问题,状态模式的好处正是状态对象本身可以通过context独立转换,比如,当红灯结束的时候绿灯亮起,状态的迁移由状态本身来完成。

而lz的状态模式更像是chain,通过预先定义好下一个chain(state)在执行已有的状态迁移逻辑,个人觉得并不能算是动态的改变行为,反而有点画蛇添足,而且也没有state本身来指定nextstate来的灵活。
这样做虽然解耦了 几种状态,但是却降低了可读性,我个人不太推荐
0 请登录后投票
   发表时间:2010-06-17  
抛出异常的爱 写道
PS: 这个不是状态模式的例子.

要算也应该算是迭代器的一种变型.仔细看里面有next()方法的.


老抛说的对, 不过我觉得更像责任链、、
0 请登录后投票
   发表时间:2010-06-17  
iaimstar 写道
我自己写了一个另外一个形式的
package com.designPattern.state;

public enum State {
	RED("红灯", 2000) {
		@Override
		public State change() {
			return GREEN;
		}
	},
	GREEN("绿灯", 1000) {
		@Override
		public State change() {
			return YELLOW;

		}
	},
	YELLOW("黄灯", 500) {
		@Override
		public State change() {
			return RED;
		}
	};
	private int wait;
	private String statname;

	public abstract State change();

	public State changeState() {
		System.out.println(statname);
		try {
			Thread.sleep(wait);
		} catch (InterruptedException e) {
			System.out.println("wrong");
		}
		return change();
	}

	private State(String name, int wait) {
		statname = name;
		this.wait = wait;
	}

	public String getStatname() {
		return statname;
	}

	public static void main(String[] args) {
		GREEN.changeState();
		GREEN.changeState().changeState();
		GREEN.changeState().changeState().changeState();
	}

	public void setWait(int wait) {
		this.wait = wait;
	}

	public int getWait() {
		return wait;
	}
}


可能这也是比较正统的状态模式的实现,但是实际上,枚举和lz的状态模式都有些问题

状态模式最大的特点是能在运行期间动态的改变行为,而作为状态的对象则不依赖于其他对象而独立变化。

枚举的问题很明显,增加修改状态都很麻烦,而将状态切分成不同的类可以更灵活的添加和修改状态。

lz的状态模式我则觉得有些问题,状态模式的好处正是状态对象本身可以通过context独立转换,比如,当红灯结束的时候绿灯亮起,状态的迁移由状态本身来完成。

而lz的状态模式更像是chain,通过预先定义好下一个chain(state)在执行已有的状态迁移逻辑,个人觉得并不能算是动态的改变行为,反而有点画蛇添足,而且也没有state本身来指定nextstate来的灵活。
这样做虽然解耦了 几种状态,但是却降低了可读性,我个人不太推荐



参看了以上的写法.没错,这正是所有的讲解状态模式的典型例子写法,我在前面也是这么写的,但是我觉得状态之间的转换不够灵活,没有将下一个状态写死了,这样可以随时灵活的调整!比如红灯亮结束后,不是绿灯亮了,而是黄灯,或是增加了一种灯(蓝)灯!就不好控制了!修改的地方也多了!
0 请登录后投票
   发表时间:2010-06-17  
BestUpon 写道

参看了以上的写法.没错,这正是所有的讲解状态模式的典型例子写法,我在前面也是这么写的,但是我觉得状态之间的转换不够灵活,没有将下一个状态写死了,这样可以随时灵活的调整!比如红灯亮结束后,不是绿灯亮了,而是黄灯,或是增加了一种灯(蓝)灯!就不好控制了!修改的地方也多了!


然后当状态多了以后,你就开始怀念典型的状态模式了。

而且没有什么不灵活的,修改发生变化的状态就可以了,而不用去关心其他没有变化的状态

0 请登录后投票
   发表时间:2010-06-17   最后修改:2010-06-17
iaimstar 写道
BestUpon 写道

参看了以上的写法.没错,这正是所有的讲解状态模式的典型例子写法,我在前面也是这么写的,但是我觉得状态之间的转换不够灵活,没有将下一个状态写死了,这样可以随时灵活的调整!比如红灯亮结束后,不是绿灯亮了,而是黄灯,或是增加了一种灯(蓝)灯!就不好控制了!修改的地方也多了!


然后当状态多了以后,你就开始怀念典型的状态模式了。

而且没有什么不灵活的,修改发生变化的状态就可以了,而不用去关心其他没有变化的状态


状态模式一般使用都是有多个对应动作的例子才好看....
只有一个change方法的例子叫他状态模式那多不合算

你可以试试里面有三四个方法.
的状态模式.
用其它的方式来代换的代价会大的多.

PS:放在一个list里顺序调用是观查者模式的特点
0 请登录后投票
   发表时间:2010-06-17  
BestUpon 写道
lj9799616 写道
在你的Client里面
trafficLight.change(trafficLight, states[index], index == states.length - 1 ? states[index] : states[index + 1]);

这句当index==3时所指的nextState是不是不对啊  指向了currentState


package org.bestupon.dp.state.refactor;
/**
*
* @author BestUpon
* @email bestupon@foxmail.com
* @date 2010-6-15下午05:23:47
* @ask 状态模式客户端
* @answer
*/
class Client {
public static void main(String[] args) {

TrafficLight trafficLight = new TrafficLight();
LightState states[] = StateHolder.getStates();
int index = 0;
while (true) {
if(index != states.length)
{
trafficLight.change(trafficLight, states[index], index == states.length - 1 ? states[0] : states[index+1]);
index++;
}else
{
index=0;
}


}
}
}

目的就是在列出的状态中间循环,如果是最后一个状态的话,就返回到第一个状态在做循环!是按照前面的没有重构之前的意图有意这样写的,要不循环就停止了!


trafficLight.change(trafficLight, states[index], index == states.length - 1 ? states[index] : states[index + 1]);
方法中第三个参数应该是没有用的,所以index == states.length - 1 ? states[index] : states[index + 1]怎么写结果都是一样的。O(∩_∩)O~
0 请登录后投票
   发表时间:2010-06-18   最后修改:2010-06-18
zjshan 写道

trafficLight.change(trafficLight, states[index], index == states.length - 1 ? states[index] : states[index + 1]);
方法中第三个参数应该是没有用的,所以index == states.length - 1 ? states[index] : states[index + 1]怎么写结果都是一样的。O(∩_∩)O~


写第三个参数的意思是传递的是下一个状态的待以后重构的时候扩充的(当初的想法是:灵活的支出上一个状态(第一个状态),下一个状态!代码没有重构,就直接发了!惭愧!)。这里写states[index+1],只是举例,说明了一种情况,不一定非要是states[index+1],也可以是states[index%2]等等,还可以手动的写!new LightState()的对象,至于你使用if...else 语句是没有错,但是依据个人习惯,个人习惯是只写if很少使用if..else,将错误遏制在萌芽阶段!
也可以写成:
trafficLight.change(trafficLight, states[index]);还可以写成:trafficLight.change(trafficLight,index == states.length - 1 ? states[0] : states[index + 1]);
见谅!
0 请登录后投票
   发表时间:2010-06-18  
抛出异常的爱 写道
BestUpon 写道
iaimstar 写道
枚举各种万能
既然你用了枚举,为啥不用枚举来写状态模式


枚举虽然遍历的时候也是以自然顺序的,但是直观上数组的顺序性比较直观!也容易理解上一个状态和下一个状态的关系!



enum Light{
    红灯(1000),黄灯(5000),绿灯(5000);    //顺序调整.
    int sleepping;
    private Light(int x) {
        this.sleepping = x ;        
    } 
    public Light change(){
        System.out.println(name());
        sleep();
        return next();
    }
	
    public Light next(){
        return values()[((ordinal()+1)%(values().length))];
    }
    public static void main(String[] args) {
		Light light = Light.红灯;
    	while(true){
			light = light.change();
		}
	}
    private void sleep() {
		try {
			Thread.sleep(this.sleepping);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}



异常哥有宝贝儿了?恭喜恭喜恭喜~
0 请登录后投票
   发表时间:2010-06-18  
我觉得楼主的写法和把状态存在配置文件或是数据库里的效果是一样的,
抛出的写的不错!
0 请登录后投票
论坛首页 入门技术版

跳转论坛:
Global site tag (gtag.js) - Google Analytics