meeting -*- Outline -*- make handouts for the code * State (design patterns book, p. 305 ff., Larman section 34.16) ** motivation want to make sure the changes to the database - either happen completely, or not all - are persistent (stay around even if the machine crashes) in this case study we'll assume that: - persistent objects can be inserted, deleted, or modified - changes to the database must be committed explicitly Draw figure 34.12 (ovals around state names below) * * | [not in DB] | [in DB] | insert | get v v /--------\ commit /--------------\ save /-----------\ | New |-------------->| OldClean |-------------->| OldDirty | \--------/ | |<--------------| | | | commit | | \--------------/<--------------\-----------/ | ^ rollback,reload | | | | delete | | rollback v | |---------------------/-------------\ |---------------------->| OldDelete | delete \-------------/ /-----------\ | | Deleted |<----------------------------------| \-----------/ commit ** problem Suppose in the persistence layer we have a PersistentObject class ------------------------------------------ PROBLEM MOTIVATING THE STATE PATTERN public class PeristentObject { private OID oid; private DateTime timeStamp; private int state; public void commit() { switch (state) { case OLD_DIRTY: // ... break; case OLD_CLEAN: // ... break; } } public void rollback() { } } ------------------------------------------ Q: What would the code for rollback look like? pretty similar, want to avoid this duplication ** solution ------------------------------------------ Problem: how to avoid repeating code to interpret state transitions Soluton: create an interface for states, and subclasses for each particular state. ------------------------------------------ ** example see figure 34.14 ------------------------------------------ EXAMPLE public class PeristentObject { private OID oid; private DateTime timeStamp; private PObjState state; public void commit() { state.commit(this); } public void delete() { state.delete(this); } public void rollback() { state.rollback(this); } public void save() { } protected void setState(PObjState s) { state = s; } } ------------------------------------------ Q: How does the save method body get filled in? ------------------------------------------ public class PObjState { public void commit(PersistentObject o) { } public void delete(PersistentObject o) { } public void rollback(PersistentObject o) { } public void save(PersistentObject o) { } } public class OldDirtyState { private static instance; private OldDirtyState() {} public static synchronized OldDirtyState getInstance() { if (instance == null) { instance = new OldDirtyState(); } return instance; } public void commit(PersistentObject o) { PersistenceFacade.getInstance() .update(o); o.setState( OldCleanState.getInstance()); } public void rollback(PersistentObject o) { PersistenceFacade.getInstance() .reload(o); o.setState( OldCleanState.getInstance()); } public void delete(PersistentObject o) { o.setState( OldDeleteState.getInstance()); } } ------------------------------------------ Q: How would you code OldCleanState? NewState? OldDeleteState? ** discussion *** applicability use the state pattern when: - operations have a large, multipart conditional statements that depend on the object's state. - you want to avoid repeating the interpreter for the states *** benefits - makes interpretation of the state easier to change - makes transitions explicit - may be more efficient than a table lookup - localizes state specific behavior - partitions code for different states *** related patterns state objects are often Singletons the Flyweight pattern explains when and how state objects can be shared