objective-c - trasductoras - maquinas de estado finito matematicas discretas
Cómo hacer una máquina de estados finitos básica en Objective-C (5)
Estoy intentando construir un FSM para controlar un temporizador en el objetivo (iphone sdk) c. Sentí que era un paso necesario, porque de lo contrario estaba terminando con un desagradable código de espagueti que contenía páginas de declaraciones de "si-entonces". La complejidad, la imposibilidad de leer y la dificultad de agregar / cambiar funciones me llevan a intentar una solución más formal como esta.
En el contexto de la aplicación, el estado del temporizador determina algunas interacciones complejas con NSManagedObjects, Core Data, etc. He dejado fuera toda esa funcionalidad por ahora, en un intento por obtener una visión clara del código FSM.
El problema es que no puedo encontrar ningún ejemplo de este tipo de código en Obj-C, y no estoy tan seguro de cómo lo he traducido del código de ejemplo de C ++ que estaba usando. (No conozco C ++ en absoluto, por lo que hay algunas suposiciones involucradas). Estoy basando esta versión de un diseño de patrón de estado en este artículo: http://www.ai-junkie.com/architecture/state_driven/tut_state1.html . No estoy haciendo un juego, pero este artículo describe conceptos que funcionan para lo que estoy haciendo.
Para crear el código (publicado a continuación), tuve que aprender muchos conceptos nuevos, incluidos los protocolos obj-c, etc. Debido a que estos son nuevos para mí, al igual que el patrón de diseño del estado, espero algunos comentarios sobre esta implementación. ¿Es así como se trabaja efectivamente con objetos de protocolo en obj-c?
Aquí está el protocolo:
@class Timer;
@protocol TimerState
-(void) enterTimerState:(Timer*)timer;
-(void) executeTimerState:(Timer*)timer;
-(void) exitTimerState:(Timer*)timer;
@end
Aquí está el archivo de encabezado del objeto Temporizador (en su forma más simplificada):
@interface Timer : NSObject
{
id<TimerState> currentTimerState;
NSTimer *secondTimer;
id <TimerViewDelegate> viewDelegate;
id<TimerState> setupState;
id<TimerState> runState;
id<TimerState> pauseState;
id<TimerState> resumeState;
id<TimerState> finishState;
}
@property (nonatomic, retain) id<TimerState> currentTimerState;
@property (nonatomic, retain) NSTimer *secondTimer;
@property (assign) id <TimerViewDelegate> viewDelegate;
@property (nonatomic, retain) id<TimerState> setupState;
@property (nonatomic, retain) id<TimerState> runState;
@property (nonatomic, retain) id<TimerState> pauseState;
@property (nonatomic, retain) id<TimerState> resumeState;
@property (nonatomic, retain) id<TimerState> finishState;
-(void)stopTimer;
-(void)changeState:(id<TimerState>) timerState;
-(void)executeState:(id<TimerState>) timerState;
-(void) setupTimer:(id<TimerState>) timerState;
Y la implementación del objeto Timer:
#import "Timer.h"
#import "TimerState.h"
#import "Setup_TS.h"
#import "Run_TS.h"
#import "Pause_TS.h"
#import "Resume_TS.h"
#import "Finish_TS.h"
@implementation Timer
@synthesize currentTimerState;
@synthesize viewDelegate;
@synthesize secondTimer;
@synthesize setupState, runState, pauseState, resumeState, finishState;
-(id)init
{
if (self = [super init])
{
id<TimerState> s = [[Setup_TS alloc] init];
self.setupState = s;
//[s release];
id<TimerState> r = [[Run_TS alloc] init];
self.runState = r;
//[r release];
id<TimerState> p = [[Pause_TS alloc] init];
self.pauseState = p;
//[p release];
id<TimerState> rs = [[Resume_TS alloc] init];
self.resumeState = rs;
//[rs release];
id<TimerState> f = [[Finish_TS alloc] init];
self.finishState = f;
//[f release];
}
return self;
}
-(void)changeState:(id<TimerState>) newState{
if (newState != nil)
{
[self.currentTimerState exitTimerState:self];
self.currentTimerState = newState;
[self.currentTimerState enterTimerState:self];
[self executeState:self.currentTimerState];
}
}
-(void)executeState:(id<TimerState>) timerState
{
[self.currentTimerState executeTimerState:self];
}
-(void) setupTimer:(id<TimerState>) timerState
{
if ([timerState isKindOfClass:[Run_TS class]])
{
secondTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(currentTime) userInfo:nil repeats:YES];
}
else if ([timerState isKindOfClass:[Resume_TS class]])
{
secondTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(currentTime) userInfo:nil repeats:YES];
}
}
-(void) stopTimer
{
[secondTimer invalidate];
}
-(void)currentTime
{
//This is just to see it working. Not formatted properly or anything.
NSString *text = [NSString stringWithFormat:@"%@", [NSDate date]];
if (self.viewDelegate != NULL && [self.viewDelegate respondsToSelector:@selector(updateLabel:)])
{
[self.viewDelegate updateLabel:text];
}
}
//TODO: releases here
- (void)dealloc
{
[super dealloc];
}
@end
No te preocupes de que faltan cosas en esta clase. Todavía no hace nada interesante. Actualmente estoy luchando para conseguir la sintaxis correcta. Actualmente compila (y funciona) pero las llamadas al método isKindOfClass provocan advertencias del compilador (el método no se encuentra en el protocolo). No estoy realmente seguro de que quiera usar isKindOfClass de todos modos. Estaba pensando en darle a cada objeto id<TimerState>
una cadena de nombre y usarla en su lugar.
En otra nota: todas las declaraciones de id<TimerState>
fueron originalmente declaraciones de TimerState *. Parecía tener sentido retenerlos como propiedades. No estoy seguro si tiene sentido con los id<TimerState>
.
Aquí hay un ejemplo de una de las clases estatales:
#import "TimerState.h"
@interface Setup_TS : NSObject <TimerState>{
}
@end
#import "Setup_TS.h"
#import "Timer.h"
@implementation Setup_TS
-(void) enterTimerState:(Timer*)timer{
NSLog(@"SETUP: entering state");
}
-(void) executeTimerState:(Timer*)timer{
NSLog(@"SETUP: executing state");
}
-(void) exitTimerState:(Timer*)timer{
NSLog(@"SETUP: exiting state");
}
@end
Una vez más, hasta ahora no hace nada excepto anunciar que fase (o subestado) está en. Pero ese no es el punto.
Lo que espero aprender aquí es si esta arquitectura está compuesta correctamente en el lenguaje obj-c. Un problema específico que estoy encontrando es la creación de los objetos de identificación en la función de inicio del temporizador. Como puede ver, comenté los lanzamientos, porque estaban causando una advertencia de "lanzamiento no encontrado en el protocolo". No estaba seguro de cómo manejar eso.
Lo que no necesito son comentarios acerca de que este código sea un formalismo excesivo o sin sentido, o lo que sea. Vale la pena aprender esto, incluso si esas ideas son ciertas. Si ayuda, considérelo como un diseño teórico para un FSM en obj-c.
Gracias de antemano por cualquier comentario útil.
(Esto no ayudó demasiado: máquina de estados finitos en Objective-C )
Cuando utiliza un protocolo como modificador de tipo, puede proporcionar una lista de protocolos separados por comas. Así que todo lo que necesita hacer para deshacerse de la advertencia del compilador es agregar NSObject a la lista de protocolos así:
- (void)setupTimer:(id<TimerState,NSObject>) timerState {
// Create scheduled timers, etc...
}
Si desea una implementación Objective-C muy simple de una máquina de estado, acabo de lanzar TransitionKit , que proporciona una API bien diseñada para implementar máquinas de estado. Está completamente probado, bien documentado, es muy fácil de usar y no requiere ninguna generación de código ni herramientas externas.
Soy bastante nuevo en Objective-C, pero sugeriría que considere la implementación directa de ANSI C para State Machine.
El hecho de que esté utilizando Cocoa no significa que tenga que usar los mensajes de Objective-C aquí.
En ANSI C, una implementación de máquina de estado puede ser muy sencilla y legible.
Mi última implementación en C de un FSM especificó #define STATE_x
o enumera tipos para los estados y tenía una tabla de punteros a funciones para ejecutar cada estado.
Sugiero utilizar el compilador de State Machine , que generará el código Objective-C . He tenido un gran éxito en Java y Python usando esto.
No debe escribir el código de la máquina de estado a mano, debe usar algo para generar el código para usted. SMC generará un código claro que puede ver si desea aprender de él, o simplemente puede usarlo y terminar con él.
Yo sugeriría verificar en Statec que tiene un pequeño dsl para hacer FSM y generar código ObjC. Es una especie de mogenerador para máquinas de estado.