Mais conteúdo relacionado Refactoring to the State Design Pattern2. Motivation
• Improve the design of the GameStats
application
• Make it easier to understand at a glance
• Make it easier to make improvements
3. Backup Backup
Replica Replica
Primary
Replica
Backup Backup
Replica Replica
4. Primary-Backup
• Bonjour can guarantee no more than one
Primary replica can publish its service
• Multiple Backup replicas communicate with
the Primary to keep all replicas synchronized
• Writes are made to the Primary and
propagated back to the Backup replicas
• If the Primary fails, a Backup can take over.
The remaining backups sync to the new
Primary
5. failed to
initial become
primary
start primary failed to start
trying to trying to
become connect to
primary primary
backup did start
primary did start backup failed to start
failed to
primary connect to backup
primary
stop stop
stopping stopping
error
primary backup
primary did stop backup did stop
stopped
6. Context State
Request() Handle()
state->Handle()
ConcreteStateA ConcreteStateB
Handle() Handle()
7. State
Context
TransitionTo(Context c, State s)
Enter(Context c)
SetState(State s)
Leave(Context c)
Foo()
Bar()
Foo(StateContext c)
Baz()
Bar(StateContext c)
....
Baz(StateContext c)
...
state->Foo()
BaseState
this->Leave(c)
TransitionTo(Context c, State s) c->SetState(s)
Enter(Context c) s->Enter(c)
Leave(Context c)
Foo(StateContext c)
Bar(StateContext c)
Baz(StateContext c)
...
ConcreteStateA ConcreteStateB
TransitionTo
Enter(Context c) Foo(StateContext c)
(c, ConcreteStateA)
Baz(StateContext c) Bar(StateContext c)
9. Original Code
- (void) stop
{
! if (self.state == GSGameControllerStatePrimary) {
! ! self.state = GSGameControllerStateStopping;
! ! [self uninstallServerTargets];
! ! [_server stop];
! ! self.state = GSGameControllerStateStopped;
! } else if (self.state == GSGameControllerStateBackup) {
! ! self.state = GSGameControllerStateStopping;
! ! [_memberManager stopMonitoring:_primaryService];
! ! [_clientToPrimary stop];
! ! self.state = GSGameControllerStateStopped;
! } else if (self.state != GSGameControllerStateStopped) {
! ! self.state = GSGameControllerStateError;
! }
}
10. First Refactoring
- (oneway void) stop
{
self.state = GSGameControllerStateStopping;
}
11. The Devil (is in the details)
- (void) setState: (GSGameControllerState)newState
{
! GSGameControllerState oldState = _state;
! _state = newState;
! if ( _state == GSGameControllerStateTryingToFindPrimary) {
! ! // [self findPrimary];
! ! self.state = GSGameControllerStateTryingToBecomePrimary; // TODO: remove the findprimary state
! } else if (_state == GSGameControllerStateTryingToBecomePrimary) {
! ! [self startPrimaryServer];
! } else if (_state == GSGameControllerStateFailedToBecomePrimary) {
! ! [self tearDownPrimary];
! ! // FIXME: this could be an endless loop of failing to become primary, put in a limit or something
! ! self.state = GSGameControllerStateTryingToBecomePrimary;
! } else if (_state == GSGameControllerStateTryingToConnectToPrimary) {
! ! [self connectToPrimary];
! } else if (_state == GSGameControllerStateFailedToConnectToPrimary) {
! ! [self tearDownBackup];
! ! self.state = GSGameControllerStateTryingToBecomePrimary;
! } else if (_state == GSGameControllerStatePrimary) {
! ! [self installServerTargets];
! ! [self tellDelegate:_delegate performSelectorWithSelf:@selector(gameControllerDidBecomePrimary:)];
! } else if (_state == GSGameControllerStateBackup) {
! ! [self tellPrimaryWhoIAm];
! ! [self monitorPrimary];
! ! [self synchronizeWithPrimaryFromVersion:_game.version];
! ! [self tellDelegate:_delegate performSelectorWithSelf:@selector(gameControllerDidBecomeBackup:)];
! } else if (_state == GSGameControllerStateStopping) {
! ! if (oldState == GSGameControllerStatePrimary) {
! ! ! self.state = GSGameControllerStateStoppingPrimary;
! ! } else if (oldState == GSGameControllerStateBackup) {
! ! ! self.state = GSGameControllerStateStoppingBackup;
12. The Devil (is in the details)
! ! } else if (oldState != GSGameControllerStateError) {
! ! ! self.state = GSGameControllerStateStopped;
! ! }
! } else if (_state == GSGameControllerStateStoppingPrimary) {
! ! [self stopServicingBackups];
! ! [_server stop];
! ! // TODO: actually monitor the stop instead of just setting state to GSGameControllerStateStopped
! ! self.state = GSGameControllerStateStopped;
! } else if (_state == GSGameControllerStateStoppingBackup) {
! ! [self stopMonitoringPrimary];
! ! [_clientToPrimary stop];
! ! // TODO: actually monitor the stop instead of just setting state to GSGameControllerStateStopped
! ! self.state = GSGameControllerStateStopped;
! } else if (_state == GSGameControllerStateStopped) {
! ! if (oldState == GSGameControllerStatePrimary) {
! ! ! [self tearDownPrimary];
! ! } else ! if (oldState == GSGameControllerStateStoppingPrimary) {
! ! ! [self tearDownPrimary];
! ! } else if (oldState == GSGameControllerStateBackup) {
! ! ! [self tearDownBackup];
! ! } else if (oldState == GSGameControllerStateStoppingBackup) {
! ! ! [self tearDownBackup];
! ! }
! ! [self tellDelegate:_delegate performSelectorWithSelf:@selector(gameControllerDidStop:)];
! } else if (_state == GSGameControllerStateError) {
! ! if (oldState == GSGameControllerStateTryingToFindPrimary) {
! ! ! [self tearDownPrimary];
! ! } else if (oldState == GSGameControllerStateTryingToBecomePrimary) {
! ! ! [self tearDownPrimary];
! ! ! [self tellDelegate:_delegate performSelectorWithSelf:@selector(gameControllerFailedToStart:)];
! ! } else if (oldState == GSGameControllerStateTryingToConnectToPrimary) {
13. The Devil (is in the details)
! ! ! [self tearDownBackup];
! ! ! [self tellDelegate:_delegate performSelectorWithSelf:@selector(gameControllerFailedToStart:)];
! ! } else if (oldState == GSGameControllerStatePrimary) {
! ! ! [self tearDownPrimary];
! ! ! // TODO: should this tellDelegate gameControllerDidStop: ?
! ! } else if (oldState == GSGameControllerStateBackup) {
! ! ! [self tearDownBackup];
! ! ! // TODO: should this tellDelegate gameControllerDidStop: ?
! ! } else if (oldState == GSGameControllerStateStopping) {
! ! ! [self tellDelegate:_delegate performSelectorWithSelf:@selector(gameControllerFailedToStop:)];
! ! ! // TODO: should this tellDelegate gameControllerDidStop: ?
! ! } else if (oldState == GSGameControllerStateStoppingPrimary) {
! ! ! [self tearDownPrimary];
! ! ! // TODO: should this tellDelegate gameControllerDidStop: ?
! ! } else if (oldState == GSGameControllerStateStoppingBackup) {
! ! ! [self tearDownBackup];
! ! ! // TODO: should this tellDelegate gameControllerDidStop: ?
! ! }
! ! /* else ??? */ [self tellDelegate:_delegate
performSelectorWithSelf:@selector(gameControllerErrorOccurred:)];
! }
}
14. First Refactoring
- (oneway void) stop
{
self.state = GSGameControllerStateStopping;
}
- (void) setState: (GSGameControllerState)newState
{
! GSGameControllerState oldState = _state;
! _state = newState;
15. } else if (_state == GSGameControllerStateStopping) {
if (oldState == GSGameControllerStatePrimary) {
self.state = GSGameControllerStateStoppingPrimary;
} else if (oldState == GSGameControllerStateBackup) {
self.state = GSGameControllerStateStoppingBackup;
} else if (oldState != GSGameControllerStateError) {
self.state = GSGameControllerStateStopped;
}
}
16. } else if (_state == GSGameControllerStateStoppingPrimary) {
[self stopServicingBackups];
[_server stop];
self.state = GSGameControllerStateStopped;
}
17. } else if (_state == GSGameControllerStateStopped) {
if (oldState == GSGameControllerStatePrimary) {
[self tearDownPrimary];
} else if (oldState == GSGameControllerStateStoppingPrimary) {
[self tearDownPrimary];
} else if (oldState == GSGameControllerStateBackup) {
[self tearDownBackup];
} else if (oldState == GSGameControllerStateStoppingBackup) {
[self tearDownBackup];
}
[self tellDelegate:_delegate
performSelectorWithSelf:
@selector(gameControllerDidStop:)];
}
19. Stopping a primary
@implementation GSGameControllerStatePrimary
- (void) enter: (GSGameController *)gc
{
! [gc installServerTargets];
}
- (void) error: (GSGameController *)gc
{
! [gc tearDownPrimary];
! [super error: gc];
}
- (void) incrementIntegerForKey: (id)aKey context: (GSGameController *)gc
{
! [gc primaryIncrementIntegerForKey: aKey];
}
- (void) stop: (GSGameController *)gc
{
! [self transition: gc to: [GSGameControllerStateStoppingPrimary state]];
}
@end
20. Only I know how
@implementation GSGameControllerStateStoppingPrimary
- (void) enter: (GSGameController *)gc
{
! [gc stopServicingBackups];
}
- (void) error: (GSGameController *)gc
{
! [gc tearDownPrimary];
! [super error: gc];
}
- (void) primaryDidStop: (GSGameController *)gc
{
! [gc tearDownPrimary];
! [self transition: gc to: [GSGameControllerStateStopped state]];
}
@end
21. Transition to stopped
@implementation GSGameControllerStateStoppingPrimary
- (void) enter: (GSGameController *)gc
{
! [gc stopServicingBackups];
}
- (void) error: (GSGameController *)gc
{
! [gc tearDownPrimary];
! [super error: gc];
}
- (void) primaryDidStop: (GSGameController *)gc
{
! [gc tearDownPrimary];
! [self transition: gc to: [GSGameControllerStateStopped state]];
}
@end
23. Same result on stop
} else if (_state == GSGameControllerStateStopped) {
if (oldState == GSGameControllerStatePrimary) {
[self tearDownPrimary];
} else if (oldState == GSGameControllerStateStoppingPrimary) {
[self tearDownPrimary];
} else if (oldState == GSGameControllerStateBackup) {
[self tearDownBackup];
} else if (oldState == GSGameControllerStateStoppingBackup) {
[self tearDownBackup];
}
[self tellDelegate:_delegate
performSelectorWithSelf:
@selector(gameControllerDidStop:)];
}
24. Result
• GSGameController only knows its own
operations
• Not states or transitions
• Separation of concerns
• Each state is a black box
• Doesn’t know details of other states
25. Motivation
• Improve the design of the GameStats
application
• Make it easier to understand at a glance
• Make it easier to make improvements