前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >面向对象设计的设计模式(五):生成器模式

面向对象设计的设计模式(五):生成器模式

作者头像
用户2932962
发布2019-01-09 11:39:18
8410
发布2019-01-09 11:39:18
举报
文章被收录于专栏:程序员维他命

定义

生成器模式(Builder Pattern):也叫创建者模式,它将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。

具体点说就是:有些对象的创建流程是一样的,但是因为自身特性的不同,所以在创建他们的时候需要将创建过程和特性的定制分离开来。

下面我们看一下该设计模式的适用场景。

适用场景

当创建复杂对象的算法应该独立于该对象的组成部分以及它们的装配方式时比较适合使用生成器模式。

一些复杂的对象,它们拥有多个组成部分(如汽车,它包括车轮、方向盘、发送机等各种部件)。而对于大多数用户而言,无须知道这些部件的装配细节,也几乎不会使用单独某个部件,而是使用一辆完整的汽车。而且这些部分的创建顺序是固定的,或者是需要指定的。

在这种情况下可以通过建造者模式对其进行设计与描述,生成器模式可以将部件和其组装过程分开,一步一步创建一个复杂的对象。

成员与类图

成员

建造者模式包含4个成员:

  1. 抽象建造者(Builder):定义构造产品的几个公共方法。
  2. 具体建造者(ConcreteBuilder):根据不同的需求来实现抽象建造者定义的公共方法;每一个具体建造者都包含一个产品对象作为它的成员变量。
  3. 指挥者(Director):根据传入的具体建造者来返回其所对应的产品对象。
  4. 产品角色(Product):创建的产品。

下面通过类图来看一下各个成员之间的关系:

模式类图

生成器模式类图

需要注意的是:

  • Builder类中的product成员变量的关键字为protected,目的是为了仅让它和它的子类可以访问该成员变量。
  • Director类中的constructProductWithBuilder(Builder builder)方法是通过传入不同的builder来构造产品的。而且它的getProduct()方法同时也封装了Concrete Builder类的getProduct()方法,目的是为了让客户端直接从Director拿到对应的产品(有些资料里面的Director类没有封装Concrete Builder类的getProduct()方法)。

代码示例

场景概述

模拟一个制造手机的场景:手机的组装需要几个固定的零件:CPU,RAM,屏幕,摄像头,而且需要CPU -> RAM ->屏幕 -> 摄像头的顺序来制造。

场景分析

我们使用建造者设计模式来实现这个场景:首先不同的手机要匹配不同的builder;然后在Director类里面来定义制造顺序。

代码实现

首先我们定义手机这个类,它有几个属性:

代码语言:javascript
复制
//================== Phone.h ==================
@interface Phone : NSObject

@property (nonatomic, copy) NSString *cpu;
@property (nonatomic, copy) NSString *capacity;
@property (nonatomic, copy) NSString *display;
@property (nonatomic, copy) NSString *camera;

@end

然后我们创建抽象builder类:

代码语言:javascript
复制
//================== Builder.h ==================
#import "Phone.h"

@interface Builder : NSObject
{
    @protected Phone *_phone;
}

- (void)createPhone;

- (void)buildCPU;
- (void)buildCapacity;
- (void)buildDisplay;
- (void)buildCamera;


- (Phone *)obtainPhone;

@end

抽象builder类声明了创建手机各个组件的接口,也提供了返回手机实例的对象。

接下来我们创建对应不同手机的具体生成者类:

IPhoneXR手机的builder:IPhoneXRBuilder

代码语言:javascript
复制
//================== IPhoneXRBuilder.h ==================
@interface IPhoneXRBuilder : Builder

@end



//================== IPhoneXRBuilder.m ==================
@implementation IPhoneXRBuilder


- (void)createPhone{

    _phone = [[Phone alloc] init];
}


- (void)buildCPU{

    [_phone setCpu:@"A12"];
}

- (void)buildCapacity{

    [_phone setCapacity:@"256"];
}


- (void)buildDisplay{

    [_phone setDisplay:@"6.1"];
}

- (void)buildCamera{

    [_phone setCamera:@"12MP"];
}

- (Phone *)obtainPhone{
    return _phone;
}

@end

小米8手机的builder:MI8Builder

代码语言:javascript
复制
//================== MI8Builder.h ==================
@interface MI8Builder : Builder

@end



//================== MI8Builder.m ==================
@implementation MI8Builder

- (void)createPhone{

    _phone = [[Phone alloc] init];
}


- (void)buildCPU{

    [_phone setCpu:@"Snapdragon 845"];
}

- (void)buildCapacity{

    [_phone setCapacity:@"128"];
}


- (void)buildDisplay{

    [_phone setDisplay:@"6.21"];
}

- (void)buildCamera{

    [_phone setCamera:@"12MP"];
}

- (Phone *)obtainPhone{
    return _phone;
}

@end

从上面两个具体builder的代码可以看出,这两个builder都按照其对应的手机配置来创建其对应的手机。

下面来看一下Director的用法:

代码语言:javascript
复制
//================== Director.h ==================
#import "Builder.h"

@interface Director : NSObject

- (void)constructPhoneWithBuilder:(Builder *)builder;

- (Phone *)obtainPhone;

@end


//================== Director.m ==================
implementation Director
{
    Builder *_builder;
}


- (void)constructPhoneWithBuilder:(Builder *)builder{

    _builder = builder;

    [_builder buildCPU];
    [_builder buildCapacity];
    [_builder buildDisplay];
    [_builder buildCamera];

}


- (Phone *)obtainPhone{

    return [_builder obtainPhone];
}


@end

Director类提供了construct:方法,需要传入builder的实例。该方法里面按照既定的顺序来创建手机。

最后我们看一下客户端是如何使用具体的Builder和Director实例的:

代码语言:javascript
复制
//================== Using by client ==================


//Get iPhoneXR
//1. A director instance
Director *director = [[Director alloc] init];

//2. A builder instance
IPhoneXRBuilder *iphoneXRBuilder = [[IPhoneXRBuilder alloc] init];

//3. Construct phone by director
[director construct:iphoneXRBuilder];

//4. Get phone by builder
Phone *iPhoneXR = [iphoneXRBuilder obtainPhone];
NSLog(@"Get new phone iPhoneXR of data: %@",iPhoneXR);


//Get MI8
MI8Builder *mi8Builder = [[MI8Builder alloc] init];
[director construct:mi8Builder];
Phone *mi8 = [mi8Builder obtainPhone];
NSLog(@"Get new phone MI8      of data: %@",mi8);

从上面可以看出客户端获取具体产品的过程:

  1. 首先需要实例化一个Director的实例。
  2. 然后根据所需要的产品找出其对应的builder。
  3. 将builder传入director实例的construct:方法。
  4. 从builder的obtainPhone获取手机实例。

下面我们看一下该例子对应的 UML类图,可以更直观地看一下各个成员之间的关系:

代码对应的类图

生成器模式代码示例类图

优点

  • 客户端不必知道产品内部组成的细节,将产品本身与产品的创建过程解耦,使得相同的创建过程可以创建不同的产品对象。
  • 每一个具体建造者都相对独立,而与其他的具体建造者无关,因此可以很方便地替换具体建造者或增加新的具体建造者, 用户使用不同的具体建造者即可得到不同的产品对象 。
  • 增加新的具体建造者无须修改原有类库的代码,指挥者类针对抽象建造者类编程,系统扩展方便,符合“开闭原则”。
  • 可以更加精细地控制产品的创建过程 。将复杂产品的创建步骤分解在不同的方法中,使得创建过程更加清晰,也更方便使用程序来控制创建过程。

缺点

  • 建造者模式所创建的产品一般具有较多的共同点,其组成部分相似,如果产品之间的差异性很大,则不适合使用建造者模式,因此其使用范围受到一定的限制。
  • 如果产品的内部变化复杂,可能会导致需要定义很多具体建造者类来实现这种变化,导致系统变得很庞大。

iOS SDK 和 JDK 中的应用

  • 暂未发现iOS SDK中使用生成器设计模式的例子,有知道的小伙伴欢迎留言。
  • JDK中的StringBuilder属于builder,它向外部提供append(String)方法来拼接字符串(也可以传入int等其他类型);而toString()方法来返回字符串。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2018-12-23,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 程序员维他命 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 定义
  • 适用场景
  • 成员与类图
    • 成员
      • 模式类图
      • 代码示例
        • 场景概述
          • 场景分析
            • 代码实现
              • 代码对应的类图
              • 优点
              • 缺点
              • iOS SDK 和 JDK 中的应用
              领券
              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档