设计模式Part6——建造者模式

建造者模式是一种较为复杂的创建型模式,它将客户端与包含多个组成部分的复杂对象的创建过程分离,客户端无需知道复杂对象的内部组成部分和装配方式,只需知道所需建造者的类型即可

一、建造者模式概述

无论是在现实世界中还是在软件系统中,都存在一些复杂的对象,它们拥有多个组成部分。

例如汽车:它有车轮、方向盘、发动机等多种部件,对于大部分用户而言,不需知道这些部件的装配细节,而是使用一辆完整的汽车。而如何将这些部件组装成一辆完整的汽车并返回给用户是建造者模式需要解决的问题

学习难度:⭐⭐⭐⭐ 使用频率:⭐⭐

定义:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示

二、建造者模式的结构和实现

1:建造者模式的结构

1)Builder(抽象建造者)

它为创建一个产品Product对象的各个部件指定抽象接口,在该接口中一般声明两个方法,一个是BuilderPartX(),如BuilderPartA、BuilderPartB等,他们用于创建对象的各个部件;另一个方法是GetResult(),他们用于返回复杂对象

2)ConcreteBuilder(具体建造者)

它实现Builder接口,实现各个部件的具体构造和装配方法,定义并明确所创建的复杂对象,还可以提供一个方法返回创建好的复杂产品对象

3)Product(产品)

它是被构建的复杂对象,包含多个组件,具体建造者创建该产品的内部表示并定义它的装配过程

4)Director(指挥者)

它负责安排复杂对象的建造次序,指挥者与抽象建造者之间存在关联,可以在其Constuct()建造方法中调用建造者对象的部件构造与装配方法,完成复杂对象的构建。

客户端一般只需要和指挥者进行交互,在客户端确定具体建造者的类型,并实例化具体建造者对象,然后通过指挥者类的构造函数或Setter方法将对象传入指挥者中

2:建造者模式的实现

产品中的复杂对象是指包含多个成员变量的对象,这些成员也称为部件或零件,一个典型的复杂对象示例的代码如下:

class Product
{
    private string partA;  //定义部件,它可以是任意类型,包括值类型和引用类型
    private string partB;
    private string partC;
    public string PartA
    {
        get{return partA;}
        set{partA=value;}
    }
    public string PartB
    {
        get{return partB;}
        set{partB=value;}
    }
    public string PartC
    {
        get{return partC;}
        set{partC=value;}
    }
}

抽象建造者类中定义了产品的创建方法和返回方法,典型代码如下:

abstract class Builder
{
    //创建产品对象
    protected Product product=new Product();
    public abstract void BuilderPartA();
    public abstract void BuilderPartB();
    public abstract void BuilderPartC();
    //返回产品对象
    public Product GetResult()
    {
        return product;
    }
}

 在抽象类Builder中声明了一系列抽象的BuildPartX()方法用于创建复杂对象的各部件,具体建造过程在ConcreteBuilder中实现,此外,还提供一个GetResult()方法用于返回一个已创建好的完整产品对象

具体建造者中实现了这些BuilderPartX()方法,通过调用Product的Setter方法可以给产品对象的成员变量设值,不同的具体建造者在实现BuilderPartX()方法时将有所区别。典型代码如下:

class ConcreteBuilder:Builder
{
    public override void BuilderPartA()
    {
        product.PartA="A1";
    }
    public override void BuilderPartB()
    {
        product.PartB="B1";
    }
    public override void BuilderPartC()
    {
        product.PartC="C1";
    }
}

指挥者类Director有两个作用:一方面隔离了客户端与创建过程,另一方面控制产品对象的创建过程。指挥者针对抽象者编程,客户端只需知道具体建造者的类型,便可通过指挥者类调用建造者的相关方法,返回一个完整的产品对象。指挥者类的示例代码如下:

class Director
{
    private Builder builder;
    public Director(Builder builder)
    {
        this.builder=builder;
    }
    public void SetBuilder(Builder builder)
    {
        this.builder=builder;
    }
    //产品构建与组装方法
    public Product Construct()
    {
        builder.BuilderPartA();
        builder.BuilderPartB();
        builder.BuilderPartC();
        return builder.GetResult();
    }
}

在指挥者中可以注入一个抽象建造者类型的对象,它提供一个建造方法Construct(),在该方法中调用builder对象的构造部件的方法,最后返回一个产品对象。

通常客户端只需关心具体建造者的类型,无需关心产品对象的具体组装过程,通常,客户端代码如下:

..
Builder builder =new ConcreteBuilder1();//通过配置文件实现
Director director= new Director(builder);
Product product =director.Construct();
...

用户可以通过配置问加你来存储具体建造者类ConcreteBuilder1的类名,使得在更换新的建造者时无需修改源代码,系统扩展方便。

建造者模式和抽象工厂模式都是较为复杂的创建型模式,创建型模式返回一个完整的复杂产品,而抽象工厂返回一系列相关的产品。在抽象工厂模式中,客户端通过选择具体工厂来生成所需对象,而在建造者模式中,客户端通过指定具体建造者类来指导Director类如何去生成对象,侧重逐步构造一个复杂对象,然后将结果返回

如果抽象工厂模式看成一个汽车配件生产厂,生成不同类型的汽车配件,那么建造者模式就是一个汽车组装厂,通过对配件进行组装返回一辆完整的汽车

三、建造者模式的应用实例

实例说明:某游戏软件公司决定开发一款基于角色扮演的多人在线的网络游戏,玩家可以在游戏中扮演虚拟世界中的一个特定角色,角色根据不同的游戏情节和统计数据具有不同的能力,角色也会随着升级而拥有更强大的能力

不同类型的角色,其性别,面容,发型等外观特性有所差异,但是无论是什么造型的角色,它的创建步骤都大同小异,都需要逐步创建其组成部分,再将组成部分装配成一个完整的游戏角色。

现在使用创建者模式来实现游戏角色的创建

Actor:充当复杂产品,游戏角色类

namespace BuilderSample
{
    class Actor
    {
        private string type;
        private string sex;
        private string face;
        private string costume;
        private string haircut;
        public string Type
        {
            get{return type;}
            set{type=value;}
        }
        public string Sex
        {
            get{return sex;}
            set{sex=value;}
        }
        public string Face
        {
            get{return face;}
            set{face=value;}
        }
        public string Costume
        {
            get{return costume;}
            set{costume=value;}
        }
        public string Haircut
        {
            get{return haircut;}
            set{haircut=value;}
        }
    }
}

ActorBuilder:充当抽象创建者,游戏角色建造者

namespace BuilderSample
{
    //角色创建者
    abstract class ActorBuilder
    {
        protected Actor actor=new Actor();
        public abstract void BuildType();
        public abstract void BuildSex();
        public abstract void BuildFace();
        public abstract void BuildCostume();
        public abstract void BuildHaircut();
        //工厂方法,返回一个完整的游戏角色对象
        public Actor CreateActor()
        {
            return actor;
        }
    }
}

HeroBuilder、AngelBuilder、DevilBuilder充当具体建造者

namespace BuilderSample
{
    class HeroBuilder: ActorBuilder
    {
        public override void BuildType()
        {
            actor.Type="英雄";
        }
        public override void BuildSex()
        {
            actor.Sex="男";
        }
        public override void BuildFace()
        {
            actor.Face="英俊";
        }
        public override void BuildCostume()
        {
            actor.Costume="盔甲";
        }
        public override void BuildHaircut()
        {
            actor.Haircut="飘逸";
        }
    }
}
namespace BuilderSample
{
    class AngelBuilder: ActorBuilder
    {
        public override void BuildType()
        {
            actor.Type="天使";
        }
        public override void BuildSex()
        {
            actor.Sex="女";
        }
        public override void BuildFace()
        {
            actor.Face="漂亮";
        }
        public override void BuildCostume()
        {
            actor.Costume="白裙";
        }
        public override void BuildHaircut()
        {
            actor.Haircut="披肩长发";
        }
    }
}

namespace BuilderSample
{
    class DevilBuilder: ActorBuilder
    {
        public override void BuildType()
        {
            actor.Type="恶魔";
        }
        public override void BuildSex()
        {
            actor.Sex="妖";
        }
        public override void BuildFace()
        {
            actor.Face="丑陋";
        }
        public override void BuildCostume()
        {
            actor.Costume="黑衣";
        }
        public override void BuildHaircut()
        {
            actor.Haircut="光头";
        }
    }
}

ActorController充当指挥者

namespace BuilderSample
{
    class ActorController
    {
        //逐步构建复杂产品对象
        public Actor Construct(ActorBuilder ab)
        {
            Actor actor;
            ab.BuildType();
            ab.BuildSex();
            ab.BuildFace();
            ab.BuildCostume();
            ab.BuildHaircut();
            actor=ab.CreateActor();
            return actor;
        }
    }
}

配置文件App.config:在配置文件中存储了具体建造者类的类名


<?xml version = "1.0" encoding = "utf - 8"?>
<configuration>
  <appSettings>
    <add key ="builder"value="BuilderSample.AngelBuilder"/>
  </appSettings>
</configuration>

Program:客户端测试类

using System;
using System.Configuration;
using System.Reflection;
namespace BuilderSample
{
    class Program
    {
        static void Main(string[] args)
        {
            ActorBuilder ab;   //针对抽象建造者编程
            //读取配置文件
            string BuilderType=ConfigurationManager.AppSettings["builder"];
            //反射生成对象
            ab=(ActorBuilder)Assembly.Load("BuilderSample").CreateInstance(builderType);
            ActorController ac=new ActorController();
            Actor actor;
            actor=ac.Construct(ab);   //通过指挥者创建完整的建造者对象

            Console.WriteLine("{0}的外观",actor.Type);
            Console.WriteLine("性别:{0}",actor.Sex);
            Console.WriteLine("面容:{0}",actor.Face);
            Console.WriteLine("服饰:{0}",actor.Costume);
            Console.WriteLine("发型:{0}",actor.Haircut);
            Console.Read();
        }
    }
}

输出结果如下:
天使的外观:

性别:女

面容:漂亮

服装:白裙

发型:披肩长发

如果需要更换具体角色创建者,只需修改配置文件即可,例如将配置文件中key为“builder”的键值对的value改成"BuilderSample.HeroBuilder",再次运行即可得到:

英雄的外观:

性别:男

面容:英军

服装:盔甲

发型:飘逸

四、指挥者类的深入讨论

1:省略Director

有些情况下,为了简化系统结构,可以将Director和抽象建造者Builder合并,在Builder中提供了逐步构建复杂产品对象的Construct()方法。

如果将游戏角色实例中的指挥者类ActorController省略,ActorBuilder类的代码修改如下:

namespace BuilderSample
{
    //角色创建者
    abstract class ActorBuilder
    {
        protected static Actor actor=new Actor();
        public abstract void BuildType();
        public abstract void BuildSex();
        public abstract void BuildFace();
        public abstract void BuildCostume();
        public abstract void BuildHaircut();
        //工厂方法,返回一个完整的游戏角色对象
        public static Actor CreateActor(ActorBuilder ab)
        {
            ab.BuildType();
            ab.BuildSex();
            ab.BuildFace();
            ab.BuildCostume();
            ab.BuildHaircut();
            return actor;
        }
    }
}

此时对应的客户端代码也发生修改,代码片段如下:

using System;
using System.Configuration;
using System.Reflection;
namespace BuilderSample
{
    class Program
    {
        static void Main(string[] args)
        {
            ActorBuilder ab;   //针对抽象建造者编程
            //读取配置文件
            string BuilderType=ConfigurationManager.AppSettings["builder"];
            //反射生成对象
            ab=(ActorBuilder)Assembly.Load("BuilderExtend").CreateInstance(builderType);
            Actor actor;
            actor=ActorBuilder.Construct(ab);   //通过指挥者创建完整的建造者对象

            Console.WriteLine("{0}的外观",actor.Type);
            Console.WriteLine("性别:{0}",actor.Sex);
            Console.WriteLine("面容:{0}",actor.Face);
            Console.WriteLine("服饰:{0}",actor.Costume);
            Console.WriteLine("发型:{0}",actor.Haircut);
            Console.Read();
        }
    }
}

除此之外还有一种更简便的方法,可以将Construct()方法中的参数去掉,直接在Construct()方法中调用BuildPartX()方法,代码如下:

namespace BuilderSample
{
    //角色创建者
    abstract class ActorBuilder
    {
        protected Actor actor=new Actor();
        public abstract void BuildType();
        public abstract void BuildSex();
        public abstract void BuildFace();
        public abstract void BuildCostume();
        public abstract void BuildHaircut();
        //工厂方法,返回一个完整的游戏角色对象
        public Actor CreateActor()
        {
            this.BuildType();
            this.BuildSex();
            this.BuildFace();
            this.BuildCostume();
            this.BuildHaircut();
            return actor;
        }
    }
}

客户端代码如下:

using System;
using System.Configuration;
using System.Reflection;
namespace BuilderSample
{
    class Program
    {
        static void Main(string[] args)
        {
            ActorBuilder ab;   //针对抽象建造者编程
            //读取配置文件
            string BuilderType=ConfigurationManager.AppSettings["builder"];
            //反射生成对象
            ab=(ActorBuilder)Assembly.Load("BuilderExtend").CreateInstance(builderType);
            Actor actor;
            actor=ab.Construct();   //通过指挥者创建完整的建造者对象

            Console.WriteLine("{0}的外观",actor.Type);
            Console.WriteLine("性别:{0}",actor.Sex);
            Console.WriteLine("面容:{0}",actor.Face);
            Console.WriteLine("服饰:{0}",actor.Costume);
            Console.WriteLine("发型:{0}",actor.Haircut);
            Console.Read();
        }
    }
}

2:钩子方法的引入

创建者模式除了逐步构建一个复杂产品对象外,还可以通过Director类来更加精细地控制产品地创建过程,例如增加一个类称为钩子方法(Hook Method)的特殊方法来控制是否调用某个BuildPartX()方法 

钩子方法的返回类型通常为bool类型,方法名一般为IsXXX(),钩子方法定义在抽象建造者类中。例如可以在游戏角色的抽象建造者类ActorBuilder中定义一个方法IsBareheaded(),用于判断某个角色是否为“光头”,在ActorBuilder中为之提供一个默认实现,返回值为false。代码如下:

namespace BuilderSample
{
    //角色创建者
    abstract class ActorBuilder
    {
        protected Actor actor=new Actor();
        public abstract void BuildType();
        public abstract void BuildSex();
        public abstract void BuildFace();
        public abstract void BuildCostume();
        public abstract void BuildHaircut();
        //钩子方法
        public virtual bool IsBareheaded()
        {
            return false;
        }
        public Actor CreateActor()
        {
            return actor;
        }
    }
}

如果某个角色无须构建头发部件,例如“恶魔”,则对应的具体建造者DevilBuilder将覆盖IsBareheaded方法,并将返回值改为true,代码如下:

namespace BuilderSample
{
    class DevilBuilder: ActorBuilder
    {
        public override void BuildType()
        {
            actor.Type="恶魔";
        }
        public override void BuildSex()
        {
            actor.Sex="妖";
        }
        public override void BuildFace()
        {
            actor.Face="丑陋";
        }
        public override void BuildCostume()
        {
            actor.Costume="黑衣";
        }
        public override void BuildHaircut()
        {
            actor.Haircut="光头";
        }
        //覆盖钩子方法
        public override bool IsBareheaded()
        {
            return true;
        }
    }
}

同时,指挥者类ActorController的代码修改如下:

namespace BuilderSample
{
    class ActorController
    {
        //逐步构建复杂产品对象
        public Actor Construct(ActorBuilder ab)
        {
            Actor actor;
            ab.BuildType();
            ab.BuildSex();
            ab.BuildFace();
            ab.BuildCostume();
            //通过钩子方法来控制产品的构建
            if(!ab.IsBareheaded())
            {
                ab.BuildHaircut();
            }
            actor=ab.CreateActor();
            return actor;
        }
    }
}

当在客户端代码中指定具体建造者类型并通过指挥者来实现产品的逐步构建时,将调用钩子方法IsBareheaded()来判断角色是否有头发,如果为true,将跳过构建发型的方法BuildHairCut(),否则执行它

五、建造者模式的优缺点和适用环境

1:建造者模式的优点

1)在建造者模式中,客户端不必直到产品内部组成的细节,将产品本身与产品的创建过程解耦

2)每个具体建造者都相对独立,与其他的具体建造者无关,因此可以很方便的替换具体建造者或新增具体建造者

3)用户可以更加精密的控制产品的创建过程,将复杂产品的创建步骤分解在不同的方法中,使得创建过程更加清晰

2:建造者模式的缺点

1)如果产品之间的差异性很大,例如很多组成部分不相同,则不适用建造者模式

2)如果产品的内部变化复杂,可能会需要定义很多具体建造者类来实现这种变化,导致系统庞大

3:建造者模式的适用环境

1)需要生成的产品对象有复杂的内部结构,这些产品对象通常包含多个成员变量

2)需要生成产品对象的属性相互依赖

3)对象的创建过程独立于创建该对象的类,引入指挥者类将创建过程封装在指挥者类中,而不再建造者类和客户端中

4)隔离复杂对象的创建和使用,并使得相同的创建过程创建不同的产品

  • 5
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
建造者设计模式是一种创建对象的设计模式,旨在将复杂对象的构建过程和其表示相互分离,使得同样的构建过程可以创建不同的表示形式。下面是一个使用Java语言实现的建造者设计模式的示例代码: ``` public class Product { private String partA; private String partB; private String partC; public void setPartA(String partA) { this.partA = partA; } public void setPartB(String partB) { this.partB = partB; } public void setPartC(String partC) { this.partC = partC; } public String getParts() { return "PartA: " + partA + ", PartB: " + partB + ", PartC: " + partC; } } public interface Builder { void buildPartA(); void buildPartB(); void buildPartC(); Product getProduct(); } public class ConcreteBuilder implements Builder { private Product product = new Product(); public void buildPartA() { product.setPartA("PartA"); } public void buildPartB() { product.setPartB("PartB"); } public void buildPartC() { product.setPartC("PartC"); } public Product getProduct() { return product; } } public class Director { public void construct(Builder builder) { builder.buildPartA(); builder.buildPartB(); builder.buildPartC(); } } public class Client { public static void main(String[] args) { Director director = new Director(); Builder builder = new ConcreteBuilder(); director.construct(builder); Product product = builder.getProduct(); System.out.println(product.getParts()); } } ``` 这个示例代码中,Product表示最终构建出来的对象,Builder定义了如何构建Product对象的抽象接口,ConcreteBuilder实现了具体的构建方法,Director负责使用Builder来构建Product对象,Client负责创建Director和Builder并执行构建过程。这种实现方式可以在不同的场景中创建不同的Product对象。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Laker404

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值