首页 > 代码库 > 建造者模式(Builder)

建造者模式(Builder)

定义

(本人在阅读完本书的中英两个版本之后发现对建造者模式十窍懂了九窍,所以直接去网上搜别人的分析了,所以本章大部分是根据网上的资料收集来的,此外,代码例子表示本书的例子不合适,也换了,所以本章和这本书关系不大。)

建造者模式是将一个复杂的对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。建造者模式最重要的两个角色:抽象建造者(Builder)和指导者(Director),其中建造者知道怎么(How)去建造产品,而指导者知道去制造什么(What)产品,建造者模式用建房子的例子来说明是比较生动的。楼房是千差万别的,楼房的外形、层数、内部房间的数量、房间的装饰等等都不一样,但是对于建造者来说,抽象出来的建筑流程是确定的,往往建筑一座楼房包括下面的步骤:(1)打桩,建立基础(2)建立框架等。建造者模式的本质和建造楼房是一致的:即流程不变,但每个流程实现的具体细节则是经常变化的。建造者模式的好处就是保证了流程不会变化,流程即不会增加、也不会遗漏或者产生流程次序错误,这是非常重要的。


建造者模式除了上述的两个角色外,还包括产品(Product)和具体建造者(ConcreteBuilder),其大致关系如下所示:

技术分享

这些角色的具体作用及简介如下所示:

  • builder:给出一个抽象接口,以规范产品对象的各个组成成分的建造。这个接口规定要实现复杂对象的哪些部分的创建,并不涉及具体的对象部件的创建。
  • ConcreteBuilder:实现Builder接口,针对不同的商业逻辑,具体化复杂对象的各部分的创建。 在建造过程完成后,提供产品的实例。
  •  Director:调用具体建造者来创建复杂对象的各个部分,在指导者中不涉及具体产品的信息,只负责保证对象各部分完整创建或按某种顺序创建。
  • Product:要创建的复杂对象。


抽象建造者抽象出固定的方法,然后具体建造者分别实现自身的方法,而指导者通过命令construct建造产品,最后通过方法getResult获取建造的产品。该过程的调用时序图,可归纳为:

技术分享

从时序图可以看出,创需要的产品不是立即就能够获取到的,需要在最后一步才能取得所需的产品,这和工厂模式的立即获取产品不同。其实工厂模式和建造者模式还是有很多相同点的,都是封装对象的创建,使得产品的创建透明化,但是也有不同点,如下表所示:

建造者模式和抽象工厂模式
建造者模式抽象工厂模式
构建一个复杂对象构建简单或者复杂对象
分多步构建一个的对象直接一步构建对象
多种方式构建一个对象仅有一种方式构建对象
分多步最后一步才返回创建的对象立即返回构建的对象
注重一个对象的创建偏重一系列对象

在实践中,主要用到较早者模式的情况为:

  1. 创建一些复杂的对象时,这些对象的内部组成构件间的建造顺序是稳定的,但是对象的内部组成构件面临着复杂的变化。
  2. 要创建的复杂对象的算法,独立于该对象的组成部分,也独立于组成部分的装配方法时。


代码示例

本例拿机器人制造来演示建造模式的,假设机器人的制造过程是:造头、造身体、造手、造脚,如此我们可以抽象出机器人的四个制造方法,其制造顺序姑且认为是头、身体、手脚,基于这样的场景,我们可以使用建造者模式来创建不同的机器人了。本例的具体的需要制造的机器人为超人机器人和蝙蝠侠机器人,因此关于建造者的类可以这样设计了。其中抽象建造者的类的定义如下:
#import <Foundation/Foundation.h>

// 抽象建造者
@interface Roboter : NSObject
{
    @protected
    NSString* _header;
    NSString* _body;
    NSString* _hand;
    NSString* _feet;
}
@property (nonatomic, readonly) NSString* header;
@property (nonatomic, readonly) NSString* body;
@property (nonatomic, readonly) NSString* hand;
@property (nonatomic, readonly) NSString* feet;

// 抽象出来的建造方法
- (void) makeHeader;
- (void) makeBody;
- (void) makeHand;
- (void) makeFeet;
- (Roboter*) getRoboter;

@end
由于是抽象出来的类,其实现部分可以为空的,但是本例将实现部分当做默认的使用,其实现如下所示:
#import "Roboter.h"

// 抽象建造者实现的默认方法
@implementation Roboter
- (void) makeHeader
{
    _header = @"header";
}
- (void) makeBody
{
    _body = @"body";
}
- (void) makeHand
{
    _hand = @"hand";
}
- (void) makeFeet
{
    _feet = @"feet";
}
- (Roboter*) getRoboter
{
    return self;
}
@end
而两个具体建造者需要继承抽象的建造类,然后重载抽象出来的制造方法,其实现分别为:
// 具体建造者-超人机器人生产商
// 重载建造方法
@implementation SuperManRoboter
- (void) makeHeader
{
    _header = @"super man header";
}
- (void) makeBody
{
    _body = @"super man body";
}
- (void) makeHand
{
    _hand = @"super man hand";
}
- (void) makeFeet
{
    _feet = @"super man feet";
}
@end

// 具体建造者-蝙蝠侠机器人生产商
// 重载建造方法
@implementation BatMenRoboter
- (void) makeHeader
{
    _header = @"bat man header";
}
- (void) makeBody
{
    _body = @"bat man body";
}
- (void) makeHand
{
    _hand = @"bat man hand";
}
- (void) makeFeet
{
    _feet = @"bat man feet";
}
@end
两个建造者都在自己的实现中实现机器人各部分的制造,然后就是需要创建指导者(Director)了,该类了解抽象出的制造者类,其唯一的方法就是下命令要求创建者开始制造,然后调用抽象出来的制造方法,其实现如下所示:
@implementation RobotMaker
- (void) make:(Roboter*)roboter
{
    // director-指导者
    // 生产机器人的固定流程
    [roboter makeHeader];
    [roboter makeBody ];
    [roboter makeHand];
    [roboter makeFeet];
}
@end
如此,客户端通过指定建造的机器人类型,即可使用建造者模式创建所需要的机器人,其客户端的调用代码大致如下所示:
@implementation Child
- (void) wantRoboterNamed:(NSString*)name
{
    // client-客户端
    RobotMaker* maker = [[RobotMaker alloc] init];
    Roboter* roboter = nil;
    if ([name isEqualToString:@"BatMan"])
    {
        roboter = [[BatMenRoboter alloc] init];
    }
    else if([name isEqualToString:@"SuperMan"])
    {
        roboter = [[SuperManRoboter alloc] init];
    }
    // 制造机器人
    [maker make:roboter];
    
    NSLog(@"Robboter Info : \n"
          "header -> %@\n"
          "body   -> %@\n"
          "hand   -> %@\n"
          "feet   -> %@\n",
          roboter.header, roboter.body, roboter.hand, roboter.feet);
    
    // 可使用获取方法取得建造的产品
    // [roboter getRoboter];
}
@end
客户端创建了指导者和具体建造者,然后通过指导者下命令开始创建对象,最后通过抽象方法getResult获取创建的对象。

总结

通过上述的学习,我们可以发现,其实产品一开始就由客户端创建了,但是此时的产品不能称之为真正的产品,我们还得给该产品一步步的组装加工,而加工部分被分离到建造者具体的类里面实现,这样实现产品的内部表象与产品的生成过程分割开来,从而可以使一个建造过程生成具有不同的内部表象的产品对象,大大降低复杂对象的创建容易度。使用建造者模式的好处有:

  • 使用建造者模式可以使客户端不必知道产品内部组成的细节。
  • 具体的建造者类之间是相互独立的,对系统的扩展非常有利。
  • 由于具体的建造者是独立的,因此可以对建造过程逐步细化,而不对其他的模块产生任何影响。


参考1,参考2

建造者模式(Builder)