iOS设计模式(一)原型模式、工厂模式、抽象工厂模式、建造者模式、单例模式

原型设计模式

在面向对象的应用程序中,有些对象的创建成本比较高。比如一些数据模型,如果有十几二十个属性,而需要创建的对象和已有的对象只有几项数据不同,这时我们可以复制原有对象,并做轻微的改动,事情就变得相对简单了,使用这种复制操作的模式便是原型模式。

原型模式类图:


原型模式类图.png

图中的HeroModel类实现了PrototypeProtocold定义的复制接口,返回自己的实例对象。

HeroModel模型的实现


@implementation HeroModel

- (id)clone {
    
    HeroModel *hero = [[[self class] alloc] init];
    hero.name = self.name;
    hero.profession = self.profession;
    hero.maxHP = self.maxHP;
    hero.position = self.position;
    
    return hero;
}

@end

测试

    HeroModel *hero = [[HeroModel alloc] init];
    
    hero.name = @"Luxanna Crownguard";
    hero.profession = @"Master";
    hero.maxHP = @(600);
    hero.position = [NSMutableArray arrayWithArray:@[@"mid"]];
    
    HeroModel *hero1 = [hero clone];
    hero1.name = @"Brand";
    
    NSLog(@"%@",hero);
    NSLog(@"%@",hero1);
    
    //打印输出
    //<HeroModel: 0x100400270> {name: Luxanna Crownguard,,profession: Master,,maxHP: 600,,position: (mid)}
    //<HeroModel: 0x1004002f0> {name: Brand,,profession: Master,,maxHP: 600,,position: (mid)}

用原型设计模式实现了简单的复制功能,hero和hero1的数据一致,只是复制后改动name属性,便创建了一个全新的HeroModel对象。

注意深拷贝和浅拷贝

在使用原型模式的时候需要注意对象的复制操作,如上的示例中就存在一定的隐患,在打印之前对原型做如下如下操作

    [hero.position addObject:@"support"];

得到打印的结果

    <HeroModel: 0x100508c90> {name: Luxanna Crownguard,,profession: Master,,maxHP: 600,,position: (mid,support)}
    <HeroModel: 0x100508d10> {name: Brand,,profession: Master,,maxHP: 600,,position: (mid,support)}

可见hero的position内容发生了改变,hero1的position也随之改变了。在对象中如果有指针型变量指向了内存中的某个资源时,在复制的时候只复制了指针,那么改变了原型,副本相应的内容也跟着发生了改变,在这里我们就需要使用深拷贝,做好实际资源的复制。如上的实现可改为:

    HeroModel *hero = [[[self class] alloc] init];
    hero.name = self.name;
    hero.profession = self.profession;
    hero.maxHP = self.maxHP;
    hero.position = [NSMutableArray arrayWithArray:self.position];

在Cocoa Touch框架也为NSObject的派生类提供了实现复制的协议,使用方法只需遵守<NSCopying>协议,并实现 - (id)copyWithZone:(nullable NSZone *)zone 方法。

本节工程示例

工厂模式

在创建一些具有相同属性的不同对象的时候,我们可以定制统一的接口行为类,让其子类来指定所生成的具体对象。例如需要生产苹果手机产品,统一定制生产的”协议“,自己可以由自己下面的不同的代工厂生产具体的产品,SE的代工厂生产SE,X生产X。当然需要生产何种产品时交由具体的代工厂来生产。使用工厂模式创建对象比直接创建对象,在给予类变更返回哪一种对象这一点上有更多的灵活性。

工厂模式类图


工厂模式类图.png

图中的IPhoneGenerator类定义了返回IPhone对象的接口,其两个子类重载了接口方法,以返回IPhone的实例。

IPhoneSEGenerator的实现

- (IPhone *)creatIPhone
{
    return [[IPhoneSE alloc] init];
}


IPhoneXGenerator的实现

- (IPhone *)creatIPhone
{
    return [[IPhoneX alloc] init];
}


客户端创建

    IPhoneGenerator *generator_X = [[IPhoneXGenerator alloc] init];
    IPhoneGenerator *generator_SE = [[IPhoneSEGenerator alloc] init];
    IPhone *iphone_X = [generator_X creatIPhone];
    IPhone *iphone_SE = [generator_SE creatIPhone];

其中具体生产哪种类型的Iphone由具体的创建者来决定生产。

本节工程示例

抽象工厂模式

同样的电子产品来说,。

抽象工厂类图


抽象工厂类图.png

图中的IBrandingGenerator类定义了两个返回IPhone对象的接口,其两个子类重载接口方法,以返回IPhone的实例。

BrandingGenerator工厂的实现,在头文件中定义创建APPLE系列还是SAMSUNG系列,具体工厂会依据定义来生成:

+ (BrandingGenerator *)generator
{
#if defined (USS_APPLE)
    return [[AppleBrandingGenerator alloc] init];
#elif defined (USS_SAMSUNG)
    return [[SamsungBrandingGenerator alloc] init];
#else
    return nil;
#endif
}

- (TV *)generatorTV
{
    return nil;
}

- (Phone *)generatorPhone
{
    return nil;
}

- (Computer *)generatorComputer
{
    return nil;
}

AppleBrandingGenerator工厂的实现

- (TV *)generatorTV
{
    return [[AppleTV alloc] init];
}

- (Phone *)generatorPhone
{
    return [[ApplePhone alloc] init];
}

- (Computer *)generatorComputer
{
    return [[AppleComputer alloc] init];
}

SamsungBrandingGenerator工厂的实现

- (TV *)generatorTV
{
    return [[SamsungTV alloc] init];
}

- (Phone *)generatorPhone
{
    return [[SamsungPhone alloc] init];
}

- (Computer *)generatorComputer
{
    return [[SamsungComputer alloc] init];
}

客户端创建

    BrandingGenerator *generator = [BrandingGenerator generator];
    
    TV *tv = [generator generatorTV];
    Phone *phone = [generator generatorPhone];
    Computer *computer = [generator generatorComputer];

在这种模式下如果需要扩展产品,可在工厂父类中新增接口来支持。另外还可以新增产品系列的支持。

本节工程示例

工厂方法和抽象工厂

相比较之下,这两种方式在创建对象时,都不让客户端知晓到底返回了什么确切的具体对象。工厂模式是通过类继承来创建的抽象产品,并且只能创建单一品种,需要新增子类创建者重载工厂方法来支持新的产品创建。而抽象工厂模式是通过对象组合来创建抽象产品,可以实现多系列的产品创建,需要在父类新增接口来支持新的产品创建。

建造者模式

有些对象的创建比较复杂,一个对象包含很多个部件的组成部分,我们将构建过程拆分成指导者-创建者的模式,客户端直接使用指导者的指导方式进行对象的生成。使用这种构建与它的表现分离模式我们称为建造者模式,也称为生成器模式。

建造者模式类图:


建造者模式类图.png

图中的HeroModel类实现了PrototypeProtocold定义的复制接口,返回自己的实例对象。

HeroModel模型的实现

@interface ComputerBuilder ()

@property (nonatomic, strong) Computer *computer;

@end

@implementation ComputerBuilder

- (instancetype)init
{
    if (self = [super init]) {
        _computer = [[Computer alloc] init];
    }
    return self;
}

// 构建CPU
- (ComputerBuilder *)buildCpu:(NSString *)cpu
{
    [_computer setCpu:cpu];
    return self;
}

// 构建显卡
- (ComputerBuilder *)buildDisplay:(NSString *)display
{
    [_computer setDisplay:display];
    return self;
}

// 构建主板
- (ComputerBuilder *)buildMainboard:(NSString *)mainboard
{
    [_computer setMainBoard:mainboard];
    return self;
}

// 构建
- (Computer *)build
{
    return _computer;
}

@end

使用这种多个步骤、多种方式构建对象,在最后一步返回产品,这个过程比单一创建更容易管理与复用。

本节工程示例

单例模式

在我们开发的应用中,那些只能共享而不能复制的资源,也就是在系统只需存在一份实例,我们可以使用单例模式。例如iOS中的UIApplication、NSUSerDefaults中使用单例模式,只存在单一的访问点。例如我们app的登录用户信息也可以设计为单例模式,持有用户,信息共享。

单例模式类图


单例模式类图.png

图中的sharedInstance类定义了返回自身单例对象的接口。
Singleton的实现


@implementation Singleton

//线程安全(多线程下可以运用)
static Singleton* instance = nil;

+(instancetype)sharedInstance{
    static dispatch_once_t once;
    dispatch_once(&once, ^{
        instance = [[Singleton alloc] init];
    });
    return instance;
}

//当我们调用alloc时候回调改方法(保证唯一性)
+(id)allocWithZone:(struct _NSZone *)zone{
    if(instance == nil){
        static dispatch_once_t once;
        dispatch_once(&once, ^{
            instance = [super allocWithZone:zone];
        });
    }
    return instance;
}

@end

通过GCD来保证线程安全,重写allocWithZone保证不会返回新的实例。

本节工程示例

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