Object State

Every object which has attribute data has state. However, state is only interesting from a modelling point of view when it affects behaviour, and in particular when it affects behaviour in the context of a given OO scenario.

Objects which do this are rather a pain, since they complicate your scenarios, and lead to all sorts of validation problems (e.g. (B) calls (C).m1 if it's in state b1, (C).m2 if it's in state b2, and shouldn't have a reference to a (C) at all in any other state; (C).m1 should only be called if it's in state c1 - and so on and so on).

It would be rather nice if this could be avoided.

One suggestion, rather off the top of my head, is to create objects (B1), (B2), (B-other), (C1), (C-other), and to ensure by design that, as well as only (C1) having an m1 method, only (B1) refers to (C1), (B-other) doesn't refer to any sort of C and so on.

The problem with this is that when an object changes state, you've got to tell everyone referring to you to refer to a different object of a different class.

Has anyone run with this at all? Any design patterns addressing this issue?

RichardDevelyn


I guess, the StrategyPattern can help.

In fact, it sounds like what you want might be the StatePattern. Basically, this is as you describe, but hiding the class change behind a proxy. Here's how it might work for your code.

(B) calls (C).m1 if it's in state b1, (C).m2 if it's in state b2, and shouldn't have a reference to a (C) at all in any other state; (C).m1 should only be called if it's in state c1 - and so on and so on).

If you can supply code for your original real problem, it would be possible to construct a more informative example.

(sample code moved to end of page)

As for restricting the actions on C according to the state it is in; hmm. Is it possible to keep C's interface (including what methods are permitted) constant, and vary the behaviour? Possibly not - if file handles are stateful objects, then reading is an operation which is only permitted in the open state. However, if there is a choice of action according to C's state (eg if C is in state 1, call m1a, if it is in state 2, called m1b), then that choice could be pushed into C, and a single method could be provided (eg m1choose() { if (state == 1) m1a() ; else if (state == 2) m1b() ; }).


The state pattern doesn't help because it maintains an object with state - and the whole point of what I'm trying to do is to get rid of state. Let me simplify my example:

Say I've got two classes, (B) and (C), each of which can have two states (b|c)(1|2). I want it to always be the case that B is in b1 when C is in c1, B is in b2 when C is in c2 (this can be complicated a bit later by specifying, say, that B calls C.m1 when they are in states (b1,c1) and so on).

The problem is to take this requirement up to a modelling/design level from a run-time-check level. I make a first proposal of having:

class C1 extends C-interface {

 C-implementation c-impl;

C1( C-implementation c-impl );

B1 b;

void m1();
}

class C2 extends C-interface {

 C-implementation c-impl;

C2( C-implementation c-impl );

B2 b;

void m2();
}

And similarly with classes B1 and B2

Now there is no chance of a B in state b1 referring to a C in state c2. The problem occurs in handling changes of state, which is why I put in that C-interface / C-implementation bit and the constructor, to suggest that one can create a C2 from a C1 (C3, ...., Cn). But this only starts to scratch the surface of the state change problem.

RichardDevelyn


State Pattern Example Code:

 // the original way

class C { public void m1() ; public void m2() ; }

class B {

public static final int STATE_1 ; public static final int STATE_2 ; public static final int STATE_3 ;

private int state ; private C c ;

public void foo() // this may not be exactly what you were thinking of ... { switch (state) { case STATE_1: c.m1() ; break ; case STATE_2: c.m2() ; break ; case STATE_3: doSomethingElse() ; break ; // ... but you get the idea } }

/* note that this doesn't enforce not having a reference to C, and it also doesn't prevent other methods in B from calling the wrong method on C. */

}

// the state-patterned way

class C // as before - i'm not going to try and enforce rules about C's state { public void m1() ; public void m2() ; }

abstract class BState { public abstract void foo() ; }

class BState1 extends BState {

private C c ;

public BState1( C c ) { this.c = c ; }

public void foo() { c.m1() ; } }

class BState2 extends BState {

private C c ;

public BState2( C c ) { this.c = c ; }

public void foo() { c.m2() ; } }

class BState3 extends BState {

public void foo() { doSomethingElse() ; } }

class B {

private BState state ;

public void foo() { state.foo() ; }

}


EditText of this page (last edited November 27, 2006) or FindPage with title or text search