关于设计模式:设计模式学习笔记八建造者模式

51次阅读

共计 7295 个字符,预计需要花费 19 分钟才能阅读完成。

1 建造者模式

1.1 定义

建造者模式:将一个简单的对象的构建与它的示意拆散,使得同样的构建过程能够创立不同的示意。
建造者模式是一种对象创立型模式。

建造者模式是较为简单的创立型模式,它将客户端与蕴含多个组成部分(或部件)的简单对象的创立过程拆散,客户端毋庸晓得简单对象的外部组成部分与拆卸形式,只须要晓得所需的建造者类型即可。建造者模式关注一步一步地创立一个简单对象,不同的具体建造者定义了不同的创立过程,且具体建造者互相独立,减少新的建造者十分不便,毋庸批改已有代码,零碎具备较好的扩展性。

1.2 简单对象

建造者模式中用到了简单对象这个概念。
简单对象就是指那些蕴含多个成员变量的对象,这些成员变量也叫部件或者整机 ,例如汽车包含方向盘,发动机,轮胎等,
汽车就是简单对象,方向盘,发动机以及轮胎就是汽车的部件。

1.3 结构图

1.4 角色

建造者模式蕴含以下四个角色:

  • Builder(形象建造者):为创立一个产品 Product 对象的各个部件指定形象接口,在该接口中个别申明两类办法,一类是 buildXXX() 办法,用于创立简单对象的各个局部(部件),另一类是是 getResult(),用于返回简单对象。Builder 既能够是抽象类,也能够是接口
  • ConcreteBuilder(具体建造者):实现了 Builder 接口或者继承了 Builder 类,实现各个部件的具体结构和拆卸办法,定义并明确其所创立的简单对象,也能够提供一个办法返回创立好的简单对象
  • Product(产品角色):是被构建的简单对象,蕴含多个组成部件,具体建造者创立该产品的外部示意并定义其拆卸过程
  • Director(指挥者):指挥者又叫导演类,简单安顿简单对象的建造秩序,指挥者与形象建造者之间存在关联关系,能够在其 construct() 建造办法中调用建造者对象的部件结构以及拆卸办法,实现简单对象的建造。客户端个别只须要与导演类进行交互,在客户端确定具体建造者的类型,并实例化具体建造者对象,而后通过导演类的构造函数或者 setter 将该对象传入导演类中

1.4.1 Product

一般来说 Product 是一个简单对象,典型的定义如下:

class Product
{
    private type1 part1;
    private type2 part2;
    private type3 part3;
    //getter + setter ...
}

其中 type1type2 等指各种不同的类型,一般来说会有嵌套类。

1.4.2 Builder

形象建造者的典型定义如下:

abstract class Builder
{protected Product product = new Product();
    public abstract void buildPart1();
    public abstract void buildPart2();
    public abstract void buildPart3();
    public Product getResult()
    {return product;}
}

形象建造者中申明了一系列 buildXXX 办法,用于创立 Product 的各个部件,具体创立过程在 ConcreteBuilder 中实现,getResult()返回已创立实现的Product

1.4.3 ConcreteBuilder

ConcreteBuilder实现了 Builder 中的 buildXXX 办法,通过调用 Product 的 setter 来实现给产品对象的各局部赋值。
不同的 ConcreteBuilder 在实现 buildXXX 是将有所区别,比方传入 Product 的 setter 参数的不同。
另外在有些 ConcreteBuilder 中某些 buildXXX 毋庸实现(提供一个空实现),这些对客户端来说毋庸关怀,客户端只须要晓得具体建造者的类型即可。
典型定义如下:

class ConcreteBuilder extends Builder
{public void buildPart1()
    {product.setPart1("part1");
    }
    public void buildPart2()
    {product.setPart2("part2");        
    }
    public void buildPart3()
    {product.setPart3("part3");
    }
    public Product getResult()
    {return product;}
}

1.4.4 Director

Director类次要有两个作用:

  • 隔离了客户与创立过程
  • 管制产品的创立过程,包含某个 buildXXX 办法是否被调用,以及调用时的先后秩序等等

指挥者针对形象建造者编程,客户端只须要晓得具体建造者的类型,即可通过指挥者调用建造者的相干办法,返回一个残缺的产品对象。典型实现如下:

class Director
{
    private Builder builder;
    public Director(Builder builder)
    {this.builder = builder;}
    
    public void setBuilder(Builder builder)
    {this.builder = builder;}
    
    public Product construct()
    {builder.buildPart1();
        builder.buildPart2();
        builder.buildPart3();
        return builder.getResult();}
}

2 实例

游戏角色的创立:不同的角色具备差异极大的内部特色,而且要求随着游戏的进行会一直呈现新的角色,也就是说扩展性要好。这里例子简化就创立三个角色:英雄,天使与恶魔。

思路:

  • Actor为简单产品
  • ActorController为指挥者
  • ActorBuilder为形象建造者
  • HeroBuilder/AngelBuilder/DevilBuilder为具体建造者
// 简单产品
class Actor
{
    private String type;
    private String face;
    private String costume;
    private String hairstyle;
    //getter and setter ...
}

// 形象建造者
abstract class ActorBuilder
{protected Actor actor = new Actor();

    public abstract void buildType();
    public abstract void buildFace();
    public abstract void buildCostume();
    public abstract void buildHairstyle();

    public Actor createActor()
    {return actor;}
}

// 具体建造者
class HeroBuilder extends ActorBuilder
{public void buildType(){actor.setType("英雄"); }
    public void buildFace(){ actor.setFace("英俊"); }
    public void buildCostume(){ actor.setCostume("盔甲"); }
    public void buildHairstyle(){ actor.setHairstyle("飘逸"); }
}

class AngleBuilder extends ActorBuilder
{public void buildType(){actor.setType("天使"); }
    public void buildFace(){ actor.setFace("丑陋"); }
    public void buildCostume(){ actor.setCostume("白裙"); }
    public void buildHairstyle(){ actor.setHairstyle("披肩长发"); }
}

class DevilBuilder extends ActorBuilder
{public void buildType(){actor.setType("恶魔"); }
    public void buildFace(){ actor.setFace("帅气"); }
    public void buildCostume(){ actor.setCostume("黑衣"); }
    public void buildHairstyle(){ actor.setHairstyle("红色"); }
}

// 指挥者类
class ActorController
{public Actor construct(ActorBuilder builder)
    {builder.buildType();
        builder.buildFace();
        builder.buildHairstyle();
        builder.buildCostume();
        return builder.createActor();}
}

测试类:

public class Test
{public static void main(String[] args) {ActorBuilder builder = new AngleBuilder();
        ActorController controller = new ActorController();
        Actor actor = controller.construct(builder);
        System.out.println(actor.getType());
        System.out.println(actor.getCostume());
        System.out.println(actor.getHairstyle());
        System.out.println(actor.getFace());
    }
}

3 优化

3.1 省略Director

其实 Director 是能够省略的,间接与 Builder 合并,在 Builder 中提供相似 Direcotr 中的 construct() 办法,并定义为静态方法,如:

abstract class ActorBuilder
{protected static Actor actor = new Actor();

    public abstract void buildType();
    public abstract void buildFace();
    public abstract void buildCostume();
    public abstract void buildHairstyle();

    public static Actor build(ActorBuilder builder)
    {builder.buildType();
        builder.buildFace();
        builder.buildHairstyle();
        builder.buildCostume();
        return actor;
    }
}

同时客户端代码批改如下:

Actor actor = ActorBuilder.build(new AngleBuilder());
//Actor actor = ActorBuilder.build(new HeroBuilder());
//Actor actor = ActorBuilder.build(new DevilBuilder());

再简略一点的能够省略 createActor 中的参数:

abstract class ActorBuilder
{protected Actor actor = new Actor();

    public abstract void buildType();
    public abstract void buildFace();
    public abstract void buildCostume();
    public abstract void buildHairstyle();

    public Actor build()
    {buildType();
        buildFace();
        buildHairstyle();
        buildCostume();
        return actor;
    }
}

同时客户端简化如下:

Actor actor = new AngleBuilder().build();

这两种形式简化了系统结构的同时又不影响灵活性以及可扩展性,然而减轻了形象建造者的职责,如果 build 办法较为简单,待构建的产品组成部分较多,倡议还是将其独自封装在 Director 中,这样更加合乎 SRP(繁多权责准则)。

3.2 钩子办法

钩子办法是一种能够管制是否调用某个 buildXXX 的办法,特色如下:

  • 返回类型为boolean
  • 办法名个别为isXXX

例如批改 ActorBuilder 如下:

abstract class ActorBuilder
{protected Actor actor = new Actor();

    public abstract void buildType();
    public abstract void buildFace();
    public abstract void buildCostume();
    public abstract void buildHairstyle();

    public boolean isBareheaded()
    {return false;}

    public Actor createActor()
    {return actor;}
}

并批改DevilBuilder,笼罩默认办法:

class DevilBuilder extends ActorBuilder
{public void buildType(){actor.setType("恶魔"); }
    public void buildFace(){ actor.setFace("帅气"); }
    public void buildCostume(){ actor.setCostume("黑衣"); }
    public void buildHairstyle(){ actor.setHairstyle("红色"); }
    public boolean isBareheaded(){ return true;}
}

最初批改ActorController

class ActorController
{public Actor construct(ActorBuilder builder)
    {builder.buildType();
        builder.buildFace();
        builder.buildCostume();
        if(builder.isBareheaded())
            builder.buildHairstyle();
        return builder.createActor();}
}

相比起之前的 ActorController 多了一次判断,测试如下:

public static void main(String[] args) {ActorController controller = new ActorController();
    Actor actor = controller.construct(new AngleBuilder());
    System.out.println(actor.getType());
    System.out.println(actor.getCostume());
    System.out.println(actor.getHairstyle());
    System.out.println(actor.getFace());
    System.out.println();

    actor = controller.construct(new DevilBuilder());
    System.out.println(actor.getType());
    System.out.println(actor.getCostume());
    System.out.println(actor.getHairstyle());
    System.out.println(actor.getFace());
}

输入如下:

3.3 返回 Builder

在理论利用中 Director 较少呈现,通常只有 Builder 以及 Product,而且Builder 是作为 Product 的外部类,提供一系列 set 办法,这些 set 办法返回一个 Builder 不便后续调用,最初以一个 build() 结尾,比方 OkHttp 中的Request/OkHttpClient

OkHttpClient client = new OkHttpClient.Builder()
.connectTimeout(5000,TimeUnit.MILLISECONDS)
.readTimeout(10,TimeUnit.SECONDS)
.build();

Request request = new Request.Builder()
.url("https://xxx")
.post(requestBody)
.build();

4 次要长处

  • 封装细节:建造者模式中客户端不须要晓得产品外部组成细节,将产品自身与产品的创立过程解耦,使得雷同的创立过程能够创立不同的产品对象
  • 扩展性好:每一个具体建造者都绝对独立,而与其余建造者无关,伊尼茨能够很不便地替换具体建造者或减少新的具体建造者,用户应用不同的具体建造者即可失去不同的产品对象。因为指挥者针对形象建造者编程,减少新的具体建造者毋庸批改原有类库的代码,扩大不便,合乎开闭准则
  • 管制创立过程:将简单产品的创立步骤合成在不同的办法中,使得创立过程更加清晰,更加精密地管制创立过程

5 次要毛病

  • 范畴受限:建造者模式所创立的产品个别具备较多的共同点,其组成部分类似,如果产品之间的差异性很大,例如很多组成部分不雷同,就不适宜应用建造者模式,因而应用范畴收到肯定限度
  • 建造者多:如果产品内部结构复杂多变,可能会须要定义很多具体建造者类来实现这种变动,增大零碎的了解难度与运行老本

6 实用场景

  • 须要生成的产品对象有简单的内部结构,这些产品对象通常蕴含多个成员变量
  • 须要生成的产品对象的属性相互依赖,须要指定其生成程序
  • 对象的创立过程独立于创立该对象的类。在建造者模式中通过引入指挥者类,将创立过程封装在指挥者类中,而不再建造者类或者客户类中
  • 隔离简单对象的创立与应用,并使得雷同的创立过程能够创立不同的产品

7 总结

正文完
 0