`

4-State: changing object behavior

阅读更多

An object that appears to change its class.

Indications: conditional code in most or all methods.

The State pattern switches from one implementation to another during the lifetime of the surrogate, in order to produce different behavior from the same method call(s). It’s a way to improve the implementation of your code when you seem to be doing a lot of testing inside each of your methods before deciding what to do for that method. For example, the fairy tale of the frog-prince contains an object (the creature) that behaves differently depending on what state it’s in. You could implement this using a boolean that you test:

//: state:KissingPrincess.java

package state;

import junit.framework.*;

 

class Creature {

  private boolean isFrog = true;

  public void greet() {

    if(isFrog)

      System.out.println("Ribbet!");

    else

      System.out.println("Darling!");

  }

  public void kiss() { isFrog = false; }

}

 

public class KissingPrincess extends TestCase  {

  Creature creature = new Creature();

  public void test() {

    creature.greet();

    creature.kiss();

    creature.greet();

  }

  public static void main(String args[]) {

    junit.textui.TestRunner.run(KissingPrincess.class);

  }

} ///:~

 

However, the greet() method, and any other methods that must test isFrog before they perform their operations, ends up with awkward code. By delegating the operations to a State object that can be changed, this code is simplified.

//: state:KissingPrincess2.java

package state;

import junit.framework.*;

 

class Creature {

  private interface State {

    String response();

  }

  private class Frog implements State {

    public String response() { return "Ribbet!"; }

  }

  private class Prince implements State {

    public String response() { return "Darling!"; }

  }

  private State state = new Frog();

  public void greet() {

    System.out.println(state.response());

  }

  public void kiss() { state = new Prince(); }

}

 

public class KissingPrincess2 extends TestCase  {

  Creature creature = new Creature();

  public void test() {

    creature.greet();

    creature.kiss();

    creature.greet();

  }

  public static void main(String args[]) {

    junit.textui.TestRunner.run(KissingPrincess2.class);

  }

} ///:~

 

In addition, changes to the State are automatically propagated throughout, rather than requiring an edit across the class methods in order to effect changes.

Here’s the basic structure of State:

 

//: state:StateDemo.java

// Simple demonstration of the State pattern.

package state;

import junit.framework.*;

 

interface State {

  void operation1();

  void operation2();

  void operation3();

}

 

class ServiceProvider {

  private State state;

  public ServiceProvider(State state) {

    this.state = state;

  }

  public void changeState(State newState) {

    state = newState;

  }

  // Pass method calls to the implementation:

  public void service1() {

    // ...

    state.operation1();

    // ...

    state.operation3();

  }

  public void service2() {

    // ...

    state.operation1();

    // ...

    state.operation2();

  }

  public void service3() {

    // ...

    state.operation3();

    // ...

    state.operation2();

  }

}

 

class Implementation1 implements State {

  public void operation1() {

    System.out.println("Implementation1.operation1()");

  }

  public void operation2() {

    System.out.println("Implementation1.operation2()");

  }

  public void operation3() {

    System.out.println("Implementation1.operation3()");

  }

}

 

class Implementation2 implements State {

  public void operation1() {

    System.out.println("Implementation2.operation1()");

  }

  public void operation2() {

    System.out.println("Implementation2.operation2()");

  }

  public void operation3() {

    System.out.println("Implementation2.operation3()");

  }

}

 

public class StateDemo extends TestCase  {

  static void run(ServiceProvider sp) {

    sp.service1();

    sp.service2();

    sp.service3();

  }

  ServiceProvider sp =

    new ServiceProvider(new Implementation1());

  public void test() {

    run(sp);

    sp.changeState(new Implementation2());

    run(sp);

  }

  public static void main(String args[]) {

    junit.textui.TestRunner.run(StateDemo.class);

  }

} ///:~

 

Proxy 和 State 总结:

Object decoupling

Both Proxy and State provide a surrogate class that you use in your code; the real class that does the work is hidden behind this surrogate class. When you call a method in the surrogate, it simply turns around and calls the method in the implementing class. These two patterns are so similar that the Proxy is simply a special case of State . One is tempted to just lump the two together into a pattern called Surrogate , but the term “proxy” has a long-standing and specialized meaning, which probably explains the reason for the two different patterns.

The basic idea is simple: from a base class, the surrogate is derived along with the class or classes that provide the actual implementation:


                     

When a surrogate object is created, it is given an implementation to which to send all of the method calls.

Structurally, the difference between Proxy and State is simple: a Proxy has only one implementation, while State has more than one. The application of the patterns is considered (in Design Patterns ) to be distinct: Proxy is used to control access to its implementation, while State allows you to change the implementation dynamically. However, if you expand your notion of “controlling access to implementation” then the two fit neatly together.

 

  • 大小: 2 KB
分享到:
评论

相关推荐

    Effective C#

    **Item 4: Use Conditional Attributes Instead of #if** - **Rationale:** Conditional attributes provide a cleaner approach to conditional compilation. - **Sample Code:** ```csharp [Conditional(...

    USB30 Universal Serial Bus 3.0 Specification pdf it is free

    4312 Introduction to SuperSpeed Packets4-4 44 Generalized Transfer Description4-4 441 Data Bursting4-5 442 IN Transfers4-5 443 OUT Transfers4-6 444 Power Management and Performance4-7 Universal Serial...

    Bochs - The cross platform IA-32 (x86) emulator

    - Enable changing part of CPU functionality at runtime through .bochsrc. - Now you could enable/disable any of SSEx/AES/MOVBE/SYSENTER_SYSEXIT/XSAVE instruction sets using new CPUID option in ....

    servlet2.4doc

    The default behavior of this method is to call addCookie(Cookie cookie) on the wrapped response object. addCookie(Cookie) - Method in interface javax.servlet.http.HttpServletResponse Adds the ...

    A Guide to Physics Problems Part 1

    - **Central Force with Origin on Circle (MIT, Michigan State)**: This problem explores the motion of an object under a central force whose origin lies on a circle, requiring an understanding of polar ...

    python3.6.5参考手册 chm

    Changes in ‘python’ Command Behavior Changes in the Python API Changes in the C API CPython bytecode changes Notable changes in Python 3.6.2 New make regen-all build target Removal of make ...

    Professional.MFC.with.VC6

    Chapter 4: The Document/View Architecture Documents and Views Document/View Designs The Different Views Types of Document Document/View Consciousness What are Document Templates? ...

    Google C++ Style Guide(Google C++编程规范)高清PDF

    In some cases there might be good arguments for changing certain style rules, but we nonetheless keep things as they are in order to preserve consistency. Another issue this guide addresses is that...

    数位板压力测试

    • Is the user allowed to change things that help to customize the work environment, but pre¬vented from changing things over which applications must have control? 2.2 Ease of Programming ...

    Solaris 10 System Administration Essentials

    2.1.5 Modifying Boot Behavior 36 2.1.6 Run Levels 37 2.1.7 Troubleshooting 37 2.2 Service Management Facility 39 2.2.1 enabled 40 2.2.2 state, next_state, and state_time 40 2.2.3 logfile 41 2.2.4 ...

    ViewPager 放大缩小左右移动

    * Callback interface for responding to changing state of the selected page. */ public interface OnPageChangeListener { /** * This method will be invoked when the current page is scrolled, ...

Global site tag (gtag.js) - Google Analytics