子类化iOS模型对象 - 适当的设计模式

时间:2013-05-23 22:42:50

标签: objective-c design-patterns

我担心这是一个相当简单的问题,但经过大量的谷歌搜索后,我想我已经超出了我的预期结果。我相信我的问题与设计模式有关,但是我可能错了。

我的应用程序调用RESTful API并返回相当于NSDictionary所代表的模型对象列表的内容。我将致电NNEntity。 (概念上)存在多种不同的NNEntity子类型。 NNEntity的所有子类型都共享entityID的属性,但每个子类都有自己独特的属性。 NNEntity的所有实例都有一个名为readFromDict:(NSDictionary *)d的方法,用于填充各自的属性。此方法由所有NNEntity个子类型符合的协议强制执行。它看起来像这样:

//NNEntity.h

@interface NNEntity : NSObject <NNReadFromDictProtocol>

@property (nonatomic, strong) NSString *entityID;

@end

//NNEntity.m

@implementation NNEntity

- (void)readFromDict:(NSDictionary *)d {
    //set common properties from values in d
    self.entityID = [d objectForKey:@"ID"];
}
@end

//NNSubEntity1.h

@interface NNSubEntity1 : NSEntity <NNReadFromDictProtocol>

@property (nonatomic, strong) NSString *favoriteColor;

@end

//NNSubEntity1.m

@implementation NNSubEntity1

- (void)readFromDict:(NSDictionary *)d {
    [super readFromDict:d];
    //set unique properties from values in d
    self.favoriteColor = [d objectForKey:@"colorPreference]:
}
@end

//NNSubEntity2.h

@interface NNSubEntity2 : NSEntity <NNReadFromDictProtocol>

@property (nonatomic, strong) NSString *middleName;

@end

//NNSubEntity2.m

@implementation NNSubEntity2

- (void)readFromDict:(NSDictionary *)d {
    [super readFromDict:d];
    //set unique properties from values in d
    self.middleName = [d objectForKey:@"middleName]:
}
@end

我已经阅读了有关使用Factory或Builder Desing模式的类似用例的各种内容,但我很好奇在这个相当简单的情况下是否有必要。例如,如果我打算调用类似这样的内容,我当前的代码最终是否同时创建了NNEntityNNSubEntity2的实例:

NNEntity *newEntity = [[NNSubEntity2 alloc] init];
//assume dict exists already and is properly keyed
[newEntity readFromDict:dict];

我假设没有,但newEntity是否同时具有entityID的公共属性以及middleName的唯一属性?此外,如果您对更好或更有效的设计方法有所了解,我们非常感谢。

2 个答案:

答案 0 :(得分:2)

这看起来就像你应该如何做到这一点。您有一个读取公共属性的基类,以及读取其特定属性的子类。

  

例如,我当前的代码最终是否同时创建了NNEntity和NNSubEntity2的实例? NNEntity *newEntity = [[NNSubEntity2 alloc] init];

不。当你运行它时,你实例化NNSubEntity2并将结果存储在由它的超类键入的变量中,这是完全有效的。这允许您调用超类上定义的任何方法,但实际的实例仍然是子类。

  

newEntity是否同时具有entityID的公共属性以及middleName的唯一属性设置?

肯定会的。它继承了超类中的实例变量,属性和方法。


请放心,据我所知,这看起来很合理,是我以前用过的模式。

答案 1 :(得分:1)

我是这样做的。

// NNEntity.h
@interface NNEntity : NSObject
@property (nonatomic, retain) NSString *entityId;
@end;

// NNEntity.m
@implementation NNEntity
@end;


// NNEntity+KVC.h
@interface NNEnity (KVC)
-(void)setValue:(id)value forUndefinedKey:(NSString *)key {
@end

// NNEntity+KVC.m
@implementation NNEntity (KVC)
-(void)setValue:(id)value forUndefinedKey:(NSString *)key {
   // Handle this as appropriate to your app.
   // A minimal implementation will throw an exception.
}
@end

同样适合您的各种子类。您(不一定)需要子类上的类别。

然后,给你NSDictionary *dict你的东西:

NNEntity *entity = [[NNEntity alloc] init];
[entity setValuesForKeysWithDictionary:dict];

中提琴!你完成了。对这种方法有一些批评,但鉴于setValue:forUndefinedKey:的强大实施,我认为这是安全且非常灵活的。

秘密在Apple的美丽Key-Value Coding技术中。从本质上讲,setValuesForKeysWithDictionary:会迭代你给它的dict键,并且每次调用它的接收器setValue:forKey:。它看起来像这样(虽然我确信Apple在引擎盖下对它进行了优化):

-(void)setValuesForKeysWithDictionary:(NSDictionary *)dictionary {
   NSArray *keys = [dictionary allKeys];
   for (NSString* key in keys) {
      [self setValue:[dictionary valueForKey:key] forKey:key];
   }
}

我也喜欢这种方法,因为转换为CoreData很简单;当你告诉CoreData“渲染”你的模型时,它只是覆盖你的存根模型类,保持你的KVC类别完好无损。更重要的是,如果您的setValue:forUndefinedKey:实现顺利,您可以对后端进行模型更改而不会导致应用程序崩溃(这有点不可,但与您的工厂解决方案没有太大区别)。

当然,我没有解决您有选择地选择要实例化的类的需求。但这是一个更大的设计问题,甚至可能会受到API和后端设计的影响。所以我推迟。

此外,正如您在下面的评论中所述,属性名称必须匹配。对于一些开发人员来说,这是一个显示阻碍,特别是如果你无法控制后端和客户端。

试一试。欢迎提供反馈。