简单地说就是,在不同的状态下会有不同的行为.
就拿人来举例吧,一个人的心情不同,就会产生不同的行为;
public class Person {
private Mood mood;
public void setMood(Mood mood) {
this.mood = mood;
}
public void behavior(){
mood.behave();
}
}
在这段代码中,我们通过给一个人设置一个心情来改变他的行为.来实现了心情不同,就会产生不同的行为的状态模式,
但是在现实生活中,心情往往是自己改变的而不是通过外部的设置,为了更加符合面向对象的模式,我们为其设置一个配置文件,通过修改配置文件来改变人的心情.
首先我们先将3种心情存储在一个Map集合中,通过改变配置文件来动态的改变任务的心情;
public class Person {
private Mood CurrentMood;
private Map<String,Mood> moods = new HashMap<String, Mood>();
//初始化普通心情
public Person() {
moods.put("default", new Normal());
}
public void setMood(String moodName,Mood mood) {
moods.put(moodName, mood);
}
public void behavior(){
CurrentMood = getMood();
if(CurrentMood == null){
CurrentMood = moods.get("default");
}
CurrentMood.behave();
}
public Mood getMood(){
Config config = Config.getInstance();
String moodName = config.getProperty("mood");
return moods.get(moodName);
}
}
我们这样虽然实现了,动态的改变心情,并且使他的行为也随之改变,但是这里有一些问题不知道大家发现了没有?
那就是如果我们写的是一个多线程的的程序,那么这个当前心情就可能随时被其他线程改变,与之而来的的就是一系列线程安全的问题.
我们怎么来解决这个问题呢?
很简单,有两种方法:
1.不将将当前的心情声明成成员变量,而将其声明成为局部变量,只有在函数体内才可以访问,这样就解决了线程安全问题,但是这同时也带来了另外一个问题,如果我需要在该函数体外访问当前的心情,就无法做到了.
2.为了解决这个问题,我们还是必须把当前的心情设定为全局变量,但是在函数体内在设置一个局部变量当前心情',将当前心情设置为当前心情',通过这个小技巧可以解决线程安全的问题:
public void behavior(){
currentMood = getMood();
if(currentMood == null){
currentMood = moods.get("default");
}
Mood currentMood_ = currentMood;
currentMood_.behave();
}
这种情况下,因为执行behave()行为的是currentMood_对象,所以如果currentMood发生改变并不会引发混乱.
但是这只是一种理想的情况,现实情况也有一些状态的变化是存在先后顺序的,比如说,我们的生命周期把,幼年->青年->中年->老年,我们只能按照这个顺序去变化,不可能直接从幼年直接到老年,也不可能从中年再到青年,这需要怎么解决呢?
先看下面这段代码,我在Test()方法中调用了两次person的live()方法,但是人的状态并没有改变,而是打印了两遍我是小孩;
State state = new ChildState();
Person person = new Person(state);
person.live();
person.live();
我是小孩,过着无忧无虑的童年生活!
我是小孩,过着无忧无虑的童年生活!
我们怎么解决这个问题呢,显然需要在Person类的live()方法中修改我们的状态,在调用了state的live()方法之后将他切换为下一个状态.具体代码:
public void live(){
state.live();
if( state instanceof ChildState){
state = new YouthState();
}else if(state instanceof YouthState){
state = new MiddleState();
}else if(state instanceof MiddleState){
state = new EldState();
}else if(state instanceof EldState){
System.out.println("我已经死了,没有下一个状态了");
}
}
我们再来调用Test()方法进行测试!
State state = new ChildState();
Person person = new Person(state);
person.live();
person.live();
person.live();
我是小孩,过着无忧无虑的童年生活!
我是青年,我对未来充满希望!
我是中年人,我要为全家人而努力打拼!!
我们发现经成功实现了我们的要求.
但是我回过头来再看我们的代码,是不是非常丑呀:
1.如果我们有100个状态,我们是不是需要if else 100次呀,
2.不知道大家发现没有,我们每次切换状态的时候并不是切换回以前的实例,而是又重新创建了一个实例,原来的的实例都被垃圾回收了.比如说我们需要切换100次状态,我们是不是需要创建100个实例,同时在销毁100个实例,这样是不是非常浪费性能呀.
我们如何解决呢?
我们仔细想一想,第一种if else的情况是不是和我们策略模式有点像呢,因为我们的不同的生命周期的状态的切换策略是不一样的,我们可以将切换状态的这个策略交给他持有的对象去实现,而真正的person是并不知道,当前的是哪一种状态,是不是有点像我们的CD机呢? 下面看一下代码实现,有助于我们的理解,我们可以对比CD机一起看.
public class ChildState implements State {
@Override
public void live() {
System.out.println("我是小孩,过着无忧无虑的童年生活!");
}
@Override
public State next() {
return new YouthState();
}
}
在这里,我们为State接口添加了下一个状态的方法next(),我们只需要调用next()方法就可以实现状态的切换,而并不用关心当前究竟是什么状态;
public void live(){
if( state != null){
state.live();
state.next();
}else{
System.out.println("我已经死了,没有下一个状态了");
}
}
第二种问题呢?如何避免我们频繁的创建和销毁对象呢?答案是一个新的知识点--枚举.
我们来看下面的代码:
public enum State {
CHILD(){
@Override
public void live() {
System.out.println("我是小孩,过着无忧无虑的童年生活!");
}
@Override
public State next() {
return YOUNTH;
}
},YOUNTH(){
@Override
public void live() {
System.out.println("我是青年,我对未来充满希望!");
}
@Override
public State next() {
return MIDDLE;
}
},MIDDLE(){
@Override
public void live() {
System.out.println("我是中年人,我要为全家人而努力打拼!!");
}
@Override
public State next() {
return ELD;
}}
,ELD(){
@Override
public void live() {
System.out.println("我是老年人,我要安享晚年!");
}
@Override
public State next() {
return null;
}
};
public abstract void live();
public abstract State next();
}
通过枚举,我们完美的解决了切换对象的问题.