设计模式之工厂模式实践(修改版)

参考书籍:《Head First Design Patterns》
OO设计原则:
1.多组合,少继承
2.针对接口编程,不对实现编程
3.松耦合
4.类对扩展开放对修改关闭
5.依赖抽象不依赖具体
书中的原版内容就默默地私下背诵吧,我就讲一下我在这个例子中对工厂模式的使用:
实际项目需求:用户数据中心设计,存储一般用户的操作历史,个人信息,收藏等数据。
下图为所有数据类型及功能比较:

Paste_Image.png

先具体的分析需求,将需求归纳比较,找出公共的部分及必须依赖的部分:
相同点:初步的一看,貌似像上图所示的那样,预约数据与收藏数据是数据分页需求的,比如添加一个“显示更多”的按钮,且这两种数据好像是不需要进行排序的,每次加入的数据排到队尾即可,但观看历史和常看频道及搜索历史这三种数据是由明确的排序需求的,我想最近观看的历史必须放到最前面,乍一看的确是这样。但是可以用归纳总结的方式来想一想,预约数据和收藏数据有分页需求,但是观看历史也许很少,并没有分页需求,所以若设计一个能满足所有数据类型需求的大工厂的话,必须讲究更加细则的需求,即为所有的数据提供分页功能,类似于观看历史这样的少量的数据就可以全部放在第一页中了,即第一页数据就放置所有的数据。再说排序功能,既然搜索历史和观看记录这样的对排序有着严格的需求,所以工厂必须提供此方法,至于预约数据与收藏甚至未来会出现的更多的没有排序需求的数据,也得满足排序的需求,而且,我相信从一位用户的角度来看,我将您最近预约的数据放在第一个,相信用户也是不会在意的,说不定体验更好。
不同点:每种数据总归是有很多不同的地方,所以在开始构造工厂框架之前,就要先尽量多的考虑到数据间的不同之处,这些是工厂无法提供包办的地方。比如:数据存贮的最大数量,每页数据的数目等等。
下面先给出我最初的设计思路,这种思路很简单,几乎是听到需求后所有新手都会想到的:
就拿收藏和历史来说吧:
历史数据类m文件:

#import"HistoryDataManager.h"
@interfaceHistoryDataManager()
{
  NSMutableArray*dataArr;
}
@end
@implementationHistoryDataManager
- (void)addData:(NSString*)data{
  [dataArr addObject:data];
}
- (void)deleteData:(NSString*)data{
  [dataArr removeObject:data];
}
@end

收藏数据类m文件:

#import"CollectDataManager.h"
@interfaceCollectDataManager()
{
  NSMutableArray*dataArr;
}
@end
@implementationCollectDataManager
- (void)addData:(NSString*)data{
  [dataArr addObject:data];
}
- (void)deleteData:(NSString*)data{
  [dataArr removeObject:data];
}
@end

使用时将两个类定义为单例来调用,可以清楚地感受到,这样做对业务层来说接口简单,易理解,但是仔细的看两个类的实现中,有大部分功能是完全一样的,甚至接口可以定义的一模一样,那这样,是否能将这部分重复的功能归纳出一个框架呢,那么所有的数据就不用再自己实现一遍这些公有的功能了,这就是我想用工厂模式来解决这个需求的初衷。下面讲讲我是怎样避免大段重复的代码,尽量精简的完成类似功能的类的构建的吧。
首先建立一个工厂,尽量满足所有的数据类的需求,但是也正是因为这一点,这个工厂的拓展性不如我预期的好,关于工厂模式下对于工厂的拓展方面希望能得到更好的指点。同时,考虑到OC中并不存在抽象类,但是我想让所有的数据类都拥有我提供的方法,那么必须用继承来实现了,其次,关于上述中提到的除了共同之处还有很多不同之处存在的,而这些不同就用OC中的协议方式来实现。下面贴出工厂的伪代码:
工厂协议声明:

@protocol ZYDataManagerProtocol 
@required
/**
*子类来创建单例,并在单例中初始化父类,保证每一单例都拥有单独的内存空间
*
*@return返回子类单例对象
*/
+ (instancetype)sharedInstance;
/**
*为maxDataCount的get方法,由子类来重写此方法,设置保存的数据最大数量
*
*@return返回保存的最大数目值
*/
- (NSInteger)maxDataCount;
/**
*为eachPageNum的get方法,由子类来重写此方法,设置每页显示的数据数
*
*@return返回每页返回的数据数目
*/
- (NSInteger)eachPageNum;

工厂类中的添加数据,获取数据以及删除数据方法举例:

- (void)addData:(id)data{
//判断本地是否已有此数据,若有则先删除该条数据
  [self deleteDataIfExistWithData:data];
//判断数组是否已经到达上界,若达到上界,则删除最后的一个
  if(operateDataArray.count==aMaxDataCount) {
    [operateDataArray removeLastObject];
  }
//将新数据插入到最前
   [operateDataArray insertObjectAtIndex:0];
}
- (NSArray*)getDataWithPageNum: (NSInteger)pageNum{
  NSArray *pageArray = [[NSArray alloc] init];
  if(aEachPageNum<1) {
     return pageArray;
  }
  if(!operateDataArray.count) {
    return;
  }
//判断用户请求的页数是否超过本身的页数,若超过返回空,若不超过,返回该页的数据
  if(pageNum <=operateDataArray.count/aEachPageNum+1&& pageNum >0) {
      NSInteger startIndex = (pageNum-1) *self.eachPageNum;
      NSInteger endRange;
      if((operateDataArray.count- startIndex)
      endRange =operateDataArray.count- startIndex;
  }else{
    endRange =aEachPageNum;
  }
  pageArray = [operateDataArray objectsAtIndexes:[NSIndex SetindexSetWithIndexesInRange:NSMakeRange(startIndex, endRange)]];
}
  return pageArray;
}
- (void)deleteData:(id)data{
  [self deleteDataIfExistWithData:data];
}
- (void)deleteAllData{
  [operateDataArray removeAllObjects];
}];
}
- (BOOL)isDataExist:(id)data{
  BOOL ifExist =NO;
  if([operateDataArray containsObject:data]) {
    ifExist =YES;
  }
  return ifExist;
}
#pragma mark - Private Methods
//判断本地是否已有此数据,若有则先删除该条数据
- (void)deleteDataIfExistWithData:(id)data{
//判断是否存在此对象,若存在,则删除对应的对象
  if([operateDataArray containsObject:data]) {
    [operateDataArray removeObject:data];
  }
}

我写了一个demo,详细代码请见demo,放在github上,地址为:https://github.com/shirleyzyy/UserDataCenter.git
子类继承后需要自己实现工厂的协议,就像你要开一家加盟店,但是首先得遵循总店的要求,总店也必须知道你这家加盟店所要买的商品数量等。
子类伪代码如下:

@implementation ZYHistoryDataManager
#pragma mark - DataManagerProtocol
+ (instancetype)sharedInstance{
  static dispatch_once_t predicate;
  static ZYHistoryDataManager *instance =nil;
  dispatch_once(&predicate, ^{
    instance = [[super alloc] initUniqueInstance];
  });
  return instance;
}
-(instancetype) initUniqueInstance {
  return[super init];
}
- (NSInteger)maxDataCount{
  return20;
}
- (NSInteger)eachPageNum{
  return10;
}

即实现单例和父类所定义的协议方法就OK了,在实际的项目中,这个工厂同时为近十种数据提供服务,目前未出现重大的bug,但是如果希望代码得到更好的精简的话我建议将工厂中操控可遍数组的queue设置为其他类的单例,因为所有的数据操作都由这个工厂完成,然而工厂只有一个,换句话说,对于对数据存储效率不高的情况下,完全可以工厂只持有一个queue,而不是为所有的子类新建queue,这个唯一的queue保持串行执行就OK了,这个我也试验过,也是可以的。暂时就记录这些了,也许我说的并不深刻,但是我有一颗虚心学习的心,欢迎各路大侠指点迷境_!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 207,248评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,681评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,443评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,475评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,458评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,185评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,451评论 3 401
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,112评论 0 261
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,609评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,083评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,163评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,803评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,357评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,357评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,590评论 1 261
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,636评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,925评论 2 344

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,527评论 25 707
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,601评论 18 139
  • 二十四节气是中国古代订立的一种用来指导农事的补充历,起源于黄河流域,古代汉族劳动人民长期经验的积累和智慧的结晶。 ...
    凯凯1219阅读 623评论 1 0
  • 若酒儿阅读 162评论 0 0
  • 小哥哥走的第一天 白天没有很想他 不说话的时候没有很想他 出来吃饭也没有 但是摸到衣服里昨天他要给我擦眼泪的纸 哇...
    uaremybelief阅读 333评论 2 0