我是一名新手程序员,我刚刚开始阅读有关决策表的内容。我已经阅读了Code Complete中的第18章,这非常具有启发性。我环顾网络试图在Objective-C中找到任何决策表的例子,我无法找到任何样板或现实世界中如何实现这一点的例子。
我在业余时间使用Objective-C编写游戏,而且我一直在处理游戏规则日益复杂的问题。有一些有点深度嵌套的if-else语句,以及一些已经有10个或更多案例需要处理的switch语句。我认为使用决策表会更容易,但我不知道如何在Objective-C中实现这一点,以实现游戏逻辑等非平凡的东西。
例如,我需要不同的方法来执行不同的状态组合。我如何在Objective-C中实现一个决策表,它可以将状态的不同组合作为键,并根据它们的组合运行特定的逻辑?
答案 0 :(得分:0)
我考虑了Objective-C中的决策表,并提出了实现基本决策表的解决方案。我不会在这里发布整个代码,只是使决策表工作的片段及其基本目的。我在Code Review SE发布了这个帖子,如果您想查看完整代码以及如何改进它的一些很好的建议。我现在发布这个是因为有人发表评论要求我这样做,但我肯定会最终改进这一点并整合评论中的建议。无论如何,这是代码。
首先在初始化方法之前,我建立了一些NSString常量,这些常量将用作NSDictionary中的键。
//Two options for the decision table, either access the dictionary directly with 0-x, the enum values, or make strings for their names
//the advantage of strings is that it is more extensible, and the position in the enum doesnt matter
NSString* const kEnemyMovementStateJustSpawned = @"enemyMovementStateJustSpawned";
NSString* const kEnemyMovementStateIdle = @"enemyMovementStateIdle";
NSString* const kEnemyMovementStateNeedsMoving = @"enemyMovementStateNeedsMoving";
NSString* const kEnemyMovementStateToFloor = @"enemyMovementStateToFloor";
NSString *const kEnemyMovementStateAtDestinationFloor = @"enemyMovementStateAtDestinationFloor";
NSString* const kEnemyMovementStateToFloorExit = @"enemyMovementStateToFloorExit";
NSString* const kEnemyMovementStateToAttackWalls = @"enemyMovementStateToAttackWalls";
NSString* const kEnemyMovementStateToAttackFloor = @"enemyMovementStateToAttackFloor";
NSString* const kEnemyMovementStateToAttackRoom = @"enemyMovementStateToAttackRoom";
然后我使用这些常量以及类中方法的名称来构建NSDictionary:
-(void) setupDecisionTable {
//the string objects are the names of methods in the class
_decisionTable = @{kEnemyMovementStateJustSpawned: @"doEnemyJustSpawned",
kEnemyMovementStateIdle: @"doEnemyIdle",
kEnemyMovementStateNeedsMoving: @"doEnemyNeedsMoving",
kEnemyMovementStateToFloorExit: @"doFloorMovement",
kEnemyMovementStateToFloor: @"doVerticalMovement",
kEnemyMovementStateAtDestinationFloor: @"doEnemyAtDestinationFloor",
kEnemyMovementStateToAttackWalls: @"doFloorMovement",
kEnemyMovementStateToAttackFloor: @"doFloorMovement",
kEnemyMovementStateToAttackRoom: @"doFloorMovement"
};
}
然后每个tick都调用此方法,该方法使用从字典中提取的对象的名称执行方法:
-(void) doMovement {
//the selector is formed from a string inside the decision table dictionary
SEL methodToCallName = NSSelectorFromString([_decisionTable objectForKey:[self stringForState:self.state]]);
if (methodToCallName) {
IMP functionPointer = [self methodForSelector:methodToCallName];
void (*methodToCall)(id, SEL) = (void *)functionPointer;
methodToCall(self, methodToCallName);
}
}
-(NSString *) stringForState:(EnemyMovementState)state {
switch (state) {
case EnemyMovementStateJustSpawned:
return kEnemyMovementStateJustSpawned;
case EnemyMovementStateIdle:
return kEnemyMovementStateIdle;
case EnemyMovementStateNeedsMoving:
return kEnemyMovementStateNeedsMoving;
case EnemyMovementStateToFloor:
return kEnemyMovementStateToFloor;
case EnemyMovementStateAtDestinationFloor:
return kEnemyMovementStateAtDestinationFloor;
case EnemyMovementStateToFloorExit:
return kEnemyMovementStateToFloorExit;
case EnemyMovementStateToAttackWalls:
return kEnemyMovementStateToAttackWalls;
case EnemyMovementStateToAttackFloor:
return kEnemyMovementStateToAttackFloor;
case EnemyMovementStateToAttackRoom:
return kEnemyMovementStateToAttackRoom;
default:
return nil;
}
}
最后,这里有几个执行的方法,只是为了一个完整的例子:
-(void) doEnemyIdle {
if ([self checkFloorsForJobs]) {
self.state = EnemyMovementStateNeedsMoving;
} else {
[self doIdleMovement];
}
}
-(void) doEnemyNeedsMoving {
[self calculateFloorExitPositionByFloor];
self.state = EnemyMovementStateToFloorExit;
}
这是一个非常简单的实现。目前它只能处理一个输入,更好的决策表将能够评估多个输入并提供适当的输出。我认为它可以通过一个中间方法扩展,该方法将状态与其他变量结合起来从字典中选择适当的对象。
完成所有这些后,我不确定决策表是否值得在Objective-C中付出努力。我不知道代码是否比switch语句更容易理解。为了向代码添加新逻辑,必须在比switch语句似乎需要的更多位置修改它。我提供此代码作为示例,如果有人在Objective-C中查看其他版本的决策表,那将会很酷。