Design Pattern: State 模式
 

2009-09-18 来源:riabook.cn

 

如果您不了解TCP的连线方式,在看 Gof 的书介绍State模式时,大概会看得一头雾水吧!TCP的连线状态图,光是要了解就要花点精神了,它的连线状态很多,用来说明状态模式确实很适合,但不适合教导初学模式的人。

由简单的开始会比较好理解状态模式的作用,先来看一个例子,如果您有一个只能顺时针转动的瓦斯开关,转动一次的状态为off、 small fire、medium fire与large fire,您如何在程式中控制状态的变化与行为呢?一个最简单的方式就是用if..else或是switch流程来控制,例如:

  • State.java
    public class State { 
        private int state;
    
        public State() { 
            state = 0; 
        } 
    
        public void switchFire() { 
            if (state == 0) { 
                state = 1; 
                System.out.println( "small fire" ); 
            } else if (state == 1) { 
                state = 2; 
                System.out.println( "medium fire" ); 
            } else if (state == 2) { 
                state = 3; 
                System.out.println( "large fire" ); 
            } else { 
                state = 0; 
                System.out.println( "turning off" ); 
            } 
        } 
    }  
    
  • Main.java
    public class Main { 
         public static void main(String[] args) { 
            State state = new State();
    
            state.switchFire(); 
            state.switchFire(); 
            state.switchFire(); 
            state.switchFire(); 
        } 
    } 
    

这个方法很简单,每个人都会,但如果您的状态变化并不是流水式的变化,而是像TCP连线状态一样,会是一个网络图的时候,用 if...else或switch来写的话,您的程式就会乱的不像话了;来考虑如何让物件控制自己的状态转换与所应表现的行为,这个程式可以这样改写:

  • IState.java
    public interface IState { 
        public void switchFire(FireSwitch sw); 
    } 
    
  • OffState
    public class OffState implements IState { 
        public void switchFire(FireSwitch sw) { 
            sw.setState(new SmallState()); 
            System.out.println( "small fire" ); 
        } 
    } 
    
  • SmallState.java
    public class SmallState implements IState { 
        public void switchFire(FireSwitch sw) { 
            sw.setState(new MediumState()); 
            System.out.println( "medium fire" ); 
        } 
    }  
    
  • MediumState.java
    public class MediumState implements IState { 
        public void switchFire(FireSwitch sw) { 
            sw.setState(new LargeState()); 
            System.out.println( "large fire" ); 
        } 
    }  
    
  • LargeState.java
    public class LargeState implements IState { 
        public void switchFire(FireSwitch sw) { 
            sw.setState(new OffState()); 
            System.out.println( "off fire" ); 
        } 
    }  
    
  • FireSwitch.java
    public class FireSwitch { 
        private State current;
    
        public FireSwitch() { 
            current = new OffState(); 
        }
    
        public void setState(State s) { 
            current = s; 
        }
    
        public void switchFire() { 
            current.switchFire(this); 
        }
    } 
    
  • Main.java
    public class Main { 
        public static void main(String[] args) { 
            FireSwitch fireSwitch = new FireSwitch();
            fireSwitch.switchFire(); 
            fireSwitch.switchFire(); 
            fireSwitch.switchFire(); 
            fireSwitch.switchFire(); 
        } 
    } 
    

程式执行结果与上一个例子是一样的,但这次并没有用流程控制来进行状态转换,而由物件自行控制自己的状态,与必须表现的行为,这个方式就是State 模式,将这个例子的 UML 类别结构画出就如下所示:

State

再进一步考虑开关可以顺时针与逆时针转动,这时如果您仍以if...else或switch来写,就会让流程显示复杂,来看看如何使用状态模式来撰写:
 

  • IState.java
    public interface IState { 
        public void switchClockWise(FireSwitch sw); 
        public void switchCountClock(FireSwitch sw); 
    }  
    
  • OffState.java
    public class OffState implements IState { 
        public void switchClockWise(FireSwitch sw) { 
            sw.setState(new SmallState()); 
            System.out.println("small fire"); 
        }
    
        public void switchCountClock(FireSwitch sw) { 
            sw.setState(new LargeState()); 
            System.out.println("large fire"); 
        } 
    }  
    
  • SmallState.java
    public class SmallState implements IState { 
        public void switchClockWise(FireSwitch sw) { 
            sw.setState(new MediumState()); 
            System.out.println("medium fire"); 
        } 
    
        public void switchCountClock(FireSwitch sw) { 
            sw.setState(new OffState()); 
            System.out.println("off fire"); 
        } 
    }  
    
  • MediumState.java
    public class MediumState implements IState { 
        public void switchClockWise(FireSwitch sw) { 
            sw.setState(new LargeState()); 
            System.out.println("large fire"); 
        }
     
        public void switchCountClock(FireSwitch sw) { 
            sw.setState(new SmallState()); 
            System.out.println("small fire"); 
        } 
    }  
    
  • LargeState.java
    public class LargeState implements State { 
        public void switchClockWise(FireSwitch sw) { 
            sw.setState(new OffState()); 
            System.out.println("off fire"); 
        }
    
        public void switchCountClock(FireSwitch sw) { 
            sw.setState(new MediumState()); 
            System.out.println("medium fire"); 
        } 
    }  
    
  • FireSwitch.java
    public class FireSwitch { 
        private State current;
    
        public FireSwitch() { 
            current = new OffState(); 
        }
    
        public void setState(State s) { 
            current = s; 
        }
    
        public void switchClockWise() { 
            current.switchClockWise(this); 
        }
    
        public void switchCountClock() { 
           current.switchCountClock(this); 
        } 
    }  
    
  • Main.java
    public class Main { 
        public static void main(String[] args) { 
            FireSwitch fireSwitch = new FireSwitch();
    
            fireSwitch.switchClockWise(); 
            fireSwitch.switchClockWise(); 
            fireSwitch.switchClockWise(); 
            fireSwitch.switchClockWise(); 
    
            System.out.println();
    
            fireSwitch.switchCountClock(); 
            fireSwitch.switchCountClock(); 
            fireSwitch.switchCountClock(); 
            fireSwitch.switchCountClock(); 
        } 
    } 
    

接下来您可以任意的转动开关了,无论是顺时针转动或是逆时针转动,状态的转换都由物件自己来表现,这是双向状态转换下的例子,如果一个状态可能转换至三个以上的状态,使用State模式就更可以看出它的好处了,就像Gof的TCP连线例子一样,如果您了解TCP连线,可以看看原书是如何实现TCP连线之间的状态转换的。

State模式的UML结构图如下:

State

火龙果软件/UML软件工程组织致力于提高您的软件工程实践能力,我们不断地吸取业界的宝贵经验,向您提供经过数百家企业验证的有效的工程技术实践经验,同时关注最新的理论进展,帮助您“领跑您所在行业的软件世界”。
资源网站: UML软件工程组织