cra*_*igb 14 design-patterns objective-c switch-statement
我正在做一些Objective-C编程,它涉及解析NSXmlDocument并从结果中填充对象属性.
第一个版本看起来像这样:
if([elementName compare:@"companyName"] == 0)
[character setCorporationName:currentElementText];
else if([elementName compare:@"corporationID"] == 0)
[character setCorporationID:currentElementText];
else if([elementName compare:@"name"] == 0)
...
Run Code Online (Sandbox Code Playgroud)
但我不喜欢这种if-else-if-else
模式.望着switch
发言中,我看到,我只能处理ints
,chars
等等,而不是对象......那么有没有更好的实现模式,我不知道的?
BTW我居然想出了设置对象的属性更好的解决方案,但我想对具体知道if
- else
VS switch
在Objective-C模式
jma*_*mah 13
您应该利用键值编码:
[character setValue:currentElementText forKey:elementName];
Run Code Online (Sandbox Code Playgroud)
如果数据不受信任,您可能需要检查密钥是否有效:
if (![validKeysCollection containsObject:elementName])
// Exception or error
Run Code Online (Sandbox Code Playgroud)
Mic*_*ley 12
我希望你们都能原谅我在这里走出困境,但我想解决在Cocoa中解析XML文档而不需要if-else语句这一更普遍的问题.最初陈述的问题将当前元素文本分配给角色对象的实例变量.正如jmah所指出的,这可以使用键值编码来解决.但是,在更复杂的XML文档中,这可能是不可能的.考虑例如以下内容.
<xmlroot>
<corporationID>
<stockSymbol>EXAM</stockSymbol>
<uuid>31337</uuid>
</corporationID>
<companyName>Example Inc.</companyName>
</xmlroot>
Run Code Online (Sandbox Code Playgroud)
有多种方法可以解决这个问题.除了我的头脑,我可以想到两个使用NSXMLDocument.第一个使用NSXMLElement.这是相当简单的,根本不涉及if-else问题.您只需获取根元素并逐个浏览其命名元素.
NSXMLElement* root = [xmlDocument rootElement];
// Assuming that we only have one of each element.
[character setCorperationName:[[[root elementsForName:@"companyName"] objectAtIndex:0] stringValue]];
NSXMLElement* corperationId = [root elementsForName:@"corporationID"];
[character setCorperationStockSymbol:[[[corperationId elementsForName:@"stockSymbol"] objectAtIndex:0] stringValue]];
[character setCorperationUUID:[[[corperationId elementsForName:@"uuid"] objectAtIndex:0] stringValue]];
Run Code Online (Sandbox Code Playgroud)
下一个使用更通用的NSXMLNode,遍历树,并直接使用if-else结构.
// The first line is the same as the last example, because NSXMLElement inherits from NSXMLNode
NSXMLNode* aNode = [xmlDocument rootElement];
while(aNode = [aNode nextNode]){
if([[aNode name] isEqualToString:@"companyName"]){
[character setCorperationName:[aNode stringValue]];
}else if([[aNode name] isEqualToString:@"corporationID"]){
NSXMLNode* correctParent = aNode;
while((aNode = [aNode nextNode]) == nil && [aNode parent != correctParent){
if([[aNode name] isEqualToString:@"stockSymbol"]){
[character setCorperationStockSymbol:[aNode stringValue]];
}else if([[aNode name] isEqualToString:@"uuid"]){
[character setCorperationUUID:[aNode stringValue]];
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
这是消除if-else结构的一个很好的选择,但是像原始问题一样,我们不能简单地在这里使用switch-case.但是,我们仍然可以通过使用performSelector消除if-else.第一步是为每个元素定义一个方法.
- (NSNode*)parse_companyName:(NSNode*)aNode
{
[character setCorperationName:[aNode stringValue]];
return aNode;
}
- (NSNode*)parse_corporationID:(NSNode*)aNode
{
NSXMLNode* correctParent = aNode;
while((aNode = [aNode nextNode]) == nil && [aNode parent != correctParent){
[self invokeMethodForNode:aNode prefix:@"parse_corporationID_"];
}
return [aNode previousNode];
}
- (NSNode*)parse_corporationID_stockSymbol:(NSNode*)aNode
{
[character setCorperationStockSymbol:[aNode stringValue]];
return aNode;
}
- (NSNode*)parse_corporationID_uuid:(NSNode*)aNode
{
[character setCorperationUUID:[aNode stringValue]];
return aNode;
}
Run Code Online (Sandbox Code Playgroud)
神奇发生在invokeMethodForNode:prefix:方法中.我们根据元素的名称生成选择器,并使用aNode作为唯一参数执行该选择器.Presto bango,我们已经消除了对if-else声明的需求.这是该方法的代码.
- (NSNode*)invokeMethodForNode:(NSNode*)aNode prefix:(NSString*)aPrefix
{
NSNode* ret = nil;
NSString* methodName = [NSString stringWithFormat:@"%@%@:", prefix, [aNode name]];
SEL selector = NSSelectorFromString(methodName);
if([self respondsToSelector:selector])
ret = [self performSelector:selector withObject:aNode];
return ret;
}
Run Code Online (Sandbox Code Playgroud)
现在,我们可以简单地编写一行代码,而不是我们更大的if-else语句(区分companyName和corporationID的语句).
NSXMLNode* aNode = [xmlDocument rootElement];
while(aNode = [aNode nextNode]){
aNode = [self invokeMethodForNode:aNode prefix:@"parse_"];
}
Run Code Online (Sandbox Code Playgroud)
现在我道歉,如果我有任何这个错误,已经有一段时间了,因为我已经用NSXMLDocument写了任何东西,它已经很晚了,我实际上没有测试过这段代码.因此,如果您发现任何错误,请发表评论或编辑此答案.
但是,我相信我刚刚展示了如何在Cocoa中使用正确命名的选择器来完全消除if-else语句,就像这样.有一些陷阱和角落案件.performSelector:方法系列只接受0,1或2个参数方法,其参数和返回类型是对象,所以如果参数和返回类型的类型不是对象,或者如果有两个以上的参数,那么你会必须使用NSInvocation来调用它.您必须确保您生成的方法名称不会调用其他方法,特别是如果调用的目标是另一个对象,并且此特定方法命名方案不适用于具有非字母数字字符的元素.你可以通过以某种方式转义方法名称中的XML元素名称来解决这个问题,或者使用方法名称作为键并使用选择器作为值来构建NSDictionary.这可能会占用大量内存并最终耗费更长的时间.像我描述的performSelector调度非常快.对于非常大的if-else语句,此方法甚至可能比if-else语句更快.
敢建议使用宏吗?
#define TEST( _name, _method ) \
if ([elementName isEqualToString:@ _name] ) \
[character _method:currentElementText]; else
#define ENDTEST { /* empty */ }
TEST( "companyName", setCorporationName )
TEST( "setCorporationID", setCorporationID )
TEST( "name", setName )
:
:
ENDTEST
Run Code Online (Sandbox Code Playgroud)
如果你想使用尽可能少的代码,你的元素名称和setter都被命名为如果elementName是@"foo"然后setter是setFoo:,你可以这样做:
SEL selector = NSSelectorFromString([NSString stringWithFormat:@"set%@:", [elementName capitalizedString]]);
[character performSelector:selector withObject:currentElementText];
Run Code Online (Sandbox Code Playgroud)
甚至可能:
[character setValue:currentElementText forKey:elementName]; // KVC-style
Run Code Online (Sandbox Code Playgroud)
虽然这些当然比使用一堆if语句慢一点.
[编辑:第二个选项已被某人提及; 糟糕!]
归档时间: |
|
查看次数: |
30468 次 |
最近记录: |