设计模式系列4--生成器模式

image

假设咱们要生产一台手机,为了方便咱们把生产手机的步骤分为三大步:javascript

  1. 生成cpu
  2. 生成其余零配件
  3. 生成屏幕

而后把这三部生成的产品组装起来就生成了一部手机。假设咱们要生成不一样品牌的手机那么就要不断重复着三个步骤去生成不一样的产品而后组装。能够发如今这个过程当中,生成一部手机的步骤永远是这三个步骤不会改变,改变的是每次生成的产品在不断变化,而后用这些产品经过这三个步骤组装出来不一样品牌的手机。html

咱们思考下,在这个过程当中,不变的部分是手机这个复杂对象的生产步骤,变化的是组成手机这个对象的零件部分能够有不一样的表现形式。那么咱们就能够把不变的部分和变化的部分分离开发,对变化的部分进行封装,这就要用到咱们今天讲的生成器模式。这个模式主要是用来将复杂对象的生产过程和表示分离开来,让相同的生成过程能够构建出来不一样表现形式的对象。java


一、定义

将一个复杂对象的构建和表现分离,让相同的构建过程能够建立不一样的表示算法

首先,咱们注意上面的限定词:复杂对象,这表示这个对象的建立过程比较繁琐,能够分为不一样的小步骤建立组成部分,而后经过组合这些小步骤来完成一个完整对象的建立。因此简单的对象建立就不用使用这个模式啦。编程

其次,即便是复杂的对象,可是只有一种表现形式也不必用,只有当你须要建立同一个系列(不少)的复杂对象的时候,你才有必要把构建过程和表现过程分离,分别封装起来,让你的构建过程能够复用,表现过程经过抽象定义每一个步骤的方法,让子类去具体实现这些方法,是每一个步骤差别化,从而构建出来不一样的产品表现。客户端只须要面向接口编程便可,方便切换到不一样的产品。app

二、 UML结构图

image


三、 实际场景使用

3.一、需求分析

如今有一份文档是纯文本格式,须要把它导出为两种不一样的格式:html和xml格式。ide

咱们来分析下,导出为两种不一样的格式就至关于生成两个不一样的对象,若是使用常规的作法,咱们可能会生成两个不一样的类分别实现导出到html和xml的需求。这种作法能够知足目前的需求,可是若是后续要增长导出到word和RTF等格式,那么又须要新加两个类,并且客户端就必须知道全部的这些类,违反开闭原则,也不适合扩展。ui

这个时候咱们能够把文档的生成过程提取出来,假设无论什么文档的生成步骤都是三个步骤:atom

  1. 生成文件头
  2. 生成文件内容
  3. 生成文件尾

而不一样的文件格式仅仅在这三个步骤的表现形式是不一样的,那么咱们就能够把导出文件的过程分离为两部分,不变的是构建过程,变换的是每一个步骤的表现形式。spa

这样之后再添加任何新的文件处处格式,只须要实现这三个步骤就能够了,方便扩展,客户端此时也不须要知道每种格式的具体实现类,咱们会提供一个director类给客户端返回他须要的具体对象,这样也能够对客户端屏蔽产品构建过程,由于客户端不必知道产品构建的具体细节。

下面咱们就来看看具体的代码实现

3.二、代码实现

申明bulider的抽象接口以下:

@protocol bilerInterface <NSObject>

-(void)buildHeader;
-(void)buildBody;
-(void)builFooter;

-(NSString*)getProduct;

@end复制代码

分别实现html和xml的具体bulider

#import <Foundation/Foundation.h>
#import "bulierInterface.h"

@interface htmlBuilder : NSObject<bilerInterface>
- (instancetype)initWithData:(NSString *)data;

@end

================
#import "htmlBuilder.h"

@interface htmlBuilder ()
@property(nonatomic,strong)NSMutableString *data;
@end

@implementation htmlBuilder

- (instancetype)initWithData:(NSString *)data
{
    self = [super init];
    if (self) {
        self.data = [[NSMutableString alloc]initWithString:data];
    }
    return self;
}

-(void)buildHeader{
    [self.data insertString:@"\n<html.headr>\n<body>\n" atIndex:0];
}

-(void)buildBody{
    [self.data appendString:@"\n<\\body>\n"];
}

-(void)builFooter{
    [self.data appendString:@"<html.footer>"];
}

-(NSString *)getProduct{
    return self.data;
}
@end复制代码
#import <Foundation/Foundation.h>
#import "bulierInterface.h"

@interface XMLBuilder : NSObject<bilerInterface>
- (instancetype)initWithData:(NSString *)data;
@end


===============
#import "XMLBuilder.h"

@interface XMLBuilder()
@property(nonatomic,strong)NSMutableString *data;

@end


@implementation XMLBuilder

- (instancetype)initWithData:(NSString *)data
{
    self = [super init];
    if (self) {
        self.data = [[NSMutableString alloc]initWithString:data];
    }
    return self;
}

-(void)buildHeader{
    [self.data insertString:@"\n<xml.headr>\n<body>\n" atIndex:0];
}

-(void)buildBody{
    [self.data appendString:@"\n<\\body>\n"];
}

-(void)builFooter{
    [self.data appendString:@"<xml.footer>"];
}
-(NSString *)getProduct{
    return self.data;
}


@end复制代码

建立director来定义转换文档的算法

#import <Foundation/Foundation.h>
#import "bulierInterface.h"

@interface bulierDirector : NSObject
- (instancetype)initWithBulider:(id<bilerInterface>)bulider;
-(NSString *)constructProduct;
@end


===============

#import "bulierDirector.h"
@interface bulierDirector()
@property(strong,nonatomic)id<bilerInterface> bulider;
@end

@implementation bulierDirector
- (instancetype)initWithBulider:(id<bilerInterface>)bulider
{
    self = [super init];
    if (self) {
        self.bulider = bulider;
    }
    return self;
}

-(NSString *)constructProduct{
    [self.bulider buildHeader];
    [self.bulider buildBody];
    [self.bulider builFooter];
    return  [self.bulider getProduct];
}
@end复制代码

client调用,使用xml转换格式:

#import <Foundation/Foundation.h>
#import "bulierInterface.h"
#import "bulierDirector.h"
#import "htmlBuilder.h"
#import "XMLBuilder.h"


int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // insert code here...

        id<bilerInterface> bulider ;
        NSString *data = @"生产者模式使用实践";
//        bulider =  [[htmlBuilder alloc]initWithData:data];
        bulider = [[XMLBuilder alloc]initWithData:data];

        bulierDirector *director = [[bulierDirector alloc]initWithBulider:bulider];
        NSString *str = [director constructProduct];
        NSLog(@"%@", str);
    }
    return 0;
}复制代码

若是须要使用html转换格式输出,只须要以下代码:

bulider =  [[htmlBuilder alloc]initWithData:data];
改成:
  bulider = [[XMLBuilder alloc]initWithData:data];复制代码

这个时候若是还须要增长其余转换格式,只要构建步骤相似,均可以扩展出新的类。


四、思考

生成器模式的主要做用就是分步骤构建复杂产品,可是要注意一点,这个模式的使用场景:这些产品的构建步骤必须固定不变,把这个不变的部分放到director里面,独立出来。变化的是每一个步骤的表现形式,放到bulider里面。这样相同的构建步骤就能够构建出来不一样的产品。

因此咱们能够看出生成器模式的意图在于:

分离构建算法和具体的构建过程,从而使得构建算法能够重用。而具体的构建过程能够方便的扩展和切换,从而构建出不一样的产品。

其实现实场景中的director可能不只仅是调用bulider的几个方法来组合一个产品,director可能须要进行额外的运算,而后根据须要去调用bulider的部件构造方法,从而构建出具体的产品。

好比说,在director的构建方法constructProduct里面先进行一些运算,而后根据须要调用bulider的bulidHeader方法把本身计算的结果当作参数传递给该方法,而后把该方法的返回值在进行一系列运算,而后得出一个结果再传递到下个bulider方法,就这样穿插调用bulider方法,而后才真正生成须要的产品。


五、对比其余模式

  • 和工厂模式

    这两个模式能够组合使用,由于在具体的bulider实现里面每一个步骤一般须要生成具体的部件对象,若是有多个同一些列的部件对象,那么就能够经过工厂方法来获取,而后在组装这些部件

  • 和抽象工厂模式

    这两个模式比较相似,都是定义一个抽象接口而后让具体类去实现。不过抽象工厂的实现是建立一系列相关的具体产品,而生成器的具体实现不只仅是建立部件,还要组装他们,而后生成一个具体的产品。前者是为了生成一系列相关的产品家族,后者是为了分步骤组装部件构成一个具体的产品。

    可是两者也能够结合使用,bulider模式须要建立许多部件而后组装,这些部件一般都是有关联的对象,那么就可使用抽象工厂来完成建立过程,而后再组装。

  • 和模板方法模式

    两者构成很相似,都是定义一个算法骨架,而后让其余类去实现具体的算法。不过bulider模式是经过委托方式,template模式是经过继承方式。最主要的区别是二者的目的彻底不一样,前者是为了构建复杂对象,后者是为了定义算法骨架。


六、Demo下载地址

建造者模式demo

相关文章
相关标签/搜索