65.9K
CodeProject 正在变化。 阅读更多。
Home

理解和实现 C# 中的建造者模式

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.93/5 (48投票s)

2012年10月4日

CPOL

4分钟阅读

viewsIcon

179035

downloadIcon

1369

本文讨论了 Builder 设计模式,何时可以使用此模式以及何时应该实现。然后,我们将看到 C# 中 Builder 模式的一个小型基本实现。

引言

本文讨论了 Builder 设计模式,何时可以使用此模式以及何时应该实现。然后,我们将看到 C# 中 Builder 模式的一个小型基本实现。

背景

当我们的应用程序需要创建一个必须使用许多不同对象构建的对象时,我们会发现客户端代码充斥着各种 Part 对象需要组合在一起以创建最终对象的细节。

为了说明以上几点,让我们以手机制造系统为例。假设我们在一家手机供应商处安装了一个系统。现在,制造商可以决定根据触摸屏、操作系统、电池和手写笔等参数来创建手机。现在,如果我们拥有所有这些部件的对象,那么创建具有上述任何部件组合的产品将导致客户端应用程序(即决定需要构建哪种手机的模块)中出现非常复杂且难以管理的代码。

Builder 模式旨在解决此类问题。GoF 将 Builder 模式定义为

将复杂对象的构建与其表示分离,以便相同的构建过程可以创建不同的表示. 

这意味着我们必须这样设计系统,即客户端应用程序只需指定应该用于创建复杂对象的参数,而构建器将负责构建复杂对象。让我们可视化 Builder 模式的类图。


现在让我们看看上述类图中的每个类是做什么用的

  • ConcreteBuilder:将创建复杂 Product 的具体类。这将跟踪它创建了什么 Product,即组装了哪些部件,客户端将使用它来获取 Product 对象。
  • Builder:这是用于创建实际产品的接口
  • Director:这是客户端代码,它将指定需要组合在一起以创建实际具体 Product 的部件。
  • Product:这是将通过组装许多部件而创建的对象。

使用代码

现在让我们按照 Builder 模式中定义的相同思路,通过解决我们之前讨论过的类似的手机制造商问题来尝试实现一个基本的 Builder 模式。

让我们从拥有指定部件的机制开始。让我们为每个部件简单地定义一些枚举,以便我们可以通过组装各种部件类型来创建 Product

// Some helper enums to identify various parts
public enum ScreenType
{
    ScreenType_TOUCH_CAPACITIVE,
    ScreenType_TOUCH_RESISTIVE,
    ScreenType_NON_TOUCH
};

public enum Battery
{
    MAH_1000,
    MAH_1500,
    MAH_2000
};

public enum OperatingSystem
{
    ANDROID,
    WINDOWS_MOBILE,
    WINDOWS_PHONE,
    SYMBIAN
};

public enum Stylus
{
    YES,
    NO
};

现在让我们看看 Product 类。我们需要有一个 Product 可以通过组装这些部件来创建,所以让我们创建一个名为 MobilePhone 的类,它将作为我们的 Product 类。

// This is the "Product" class
class MobilePhone
{
    // fields to hold the part type
    string phoneName;       
    ScreenType phoneScreen;
    Battery phoneBattery;
    OperatingSystem phoneOS;
    Stylus phoneStylus;

    public MobilePhone(string name)
    {
        phoneName = name;
    }

    // Public properties to access phone parts

    public string PhoneName
    {
        get { return phoneName; }            
    }

    public ScreenType PhoneScreen
    {
        get { return phoneScreen; }
        set { phoneScreen = value; }
    }        

    public Battery PhoneBattery
    {
        get { return phoneBattery; }
        set { phoneBattery = value; }
    }        

    public OperatingSystem PhoneOS
    {
        get { return phoneOS; }
        set { phoneOS = value; }
    }       

    public Stylus PhoneStylus
    {
        get { return phoneStylus; }
        set { phoneStylus = value; }
    }

    // Methiod to display phone details in our own representation
    public override string ToString()
    {
        return string.Format("Name: {0}\nScreen: {1}\nBattery {2}\nOS: {3}\nStylus: {4}",
            PhoneName, PhoneScreen, PhoneBattery, PhoneOS, PhoneStylus);
    }
}

现在我们已经准备好了 Product 类,让我们开始创建 BuilderBuilder 应该提供创建任何手机的每个部件的功能。所以让我们为 Builder 创建一个接口,即 IPhoneBuilder ,并看看它。

// This is the "Builder" class
interface IPhoneBuilder
{
    void BuildScreen();
    void BuildBattery();
    void BuildOS();
    void BuildStylus();
    MobilePhone Phone { get;}
}

现在我们已经准备好了 Builder 接口,接下来就是将 ConcreteBuilder 对象就位。让我们假设制造商正在计划一款 Android 手机和一款 Windows Phone,因此我们需要两个 ConcreteBuilder 用于这些手机,即 AndroidPhoneBuilder WindowsPhoneBuilder。在这些构建器中,我们可以指定我们要用于每部手机的部件类型。

// This is the "ConcreteBuilder" class
class AndroidPhoneBuilder : IPhoneBuilder
{
    MobilePhone phone;

    public AndroidPhoneBuilder()
    {
        phone = new MobilePhone("Android Phone");
    }

    #region IPhoneBuilder Members

    public void BuildScreen()
    {
        phone.PhoneScreen = ScreenType.ScreenType_TOUCH_RESISTIVE;
    }

    public void BuildBattery()
    {
        phone.PhoneBattery = Battery.MAH_1500;
    }

    public void BuildOS()
    {
        phone.PhoneOS = OperatingSystem.ANDROID;
    }

    public void BuildStylus()
    {
        phone.PhoneStylus = Stylus.YES;
    }
    
    // GetResult Method which will return the actual phone
    public MobilePhone Phone
    {
        get { return phone; }
    }

    #endregion
}

// This is the "ConcreteBuilder" class
class WindowsPhoneBuilder : IPhoneBuilder
{
    MobilePhone phone;

    public WindowsPhoneBuilder()
    {
        phone = new MobilePhone("Windows Phone");
    }

    #region IPhoneBuilder Members

    public void BuildScreen()
    {
        phone.PhoneScreen = ScreenType.ScreenType_TOUCH_CAPACITIVE;
    }

    public void BuildBattery()
    {
        phone.PhoneBattery = Battery.MAH_2000;
    }

    public void BuildOS()
    {
        phone.PhoneOS = OperatingSystem.WINDOWS_PHONE;
    }

    public void BuildStylus()
    {
        phone.PhoneStylus = Stylus.NO;
    }

    // GetResult Method which will return the actual phone
    public MobilePhone Phone
    {
        get { return phone; }
    }

    #endregion
}

最后,让我们创建 Director 类。让我们创建一个 Director 类,它将拥有 Construct 方法,该方法接受一个 IPhoneBuilder ,然后内部调用 ConcreteBuilders 的相应函数。

// This is the "Director" class
class Manufacturer
{
    public void Construct(IPhoneBuilder phoneBuilder)
    {
        phoneBuilder.BuildBattery();
        phoneBuilder.BuildOS();
        phoneBuilder.BuildScreen();
        phoneBuilder.BuildStylus();
    }
}

现在,我们已经将构建复杂 Products 的功能封装成 Builder 设计模式的形式。现在,当我们查看客户端代码时,我们可以看到创建任何产品是多么干净。

class Program
{
    static void Main(string[] args)
    {
        // Lets create the Director first
        Manufacturer newManufacturer = new Manufacturer();
        // Lets have the Builder class ready
        IPhoneBuilder phoneBuilder = null;

        // Now let us create an android phone
        phoneBuilder = new AndroidPhoneBuilder();
        newManufacturer.Construct(phoneBuilder);
        Console.WriteLine("A new Phone built:\n\n{0}", phoneBuilder.Phone.ToString());

        // Now let us create a Windows Phone
        phoneBuilder = new WindowsPhoneBuilder();
        newManufacturer.Construct(phoneBuilder);
        Console.WriteLine("A new Phone built:\n\n{0}", phoneBuilder.Phone.ToString());
    }
}

现在,如果我们需要创建更多 Products ,只需要一个 ConcreteBuilder ,其余所有代码库将保持不变。使用这种模式,客户端代码也可以轻松创建复杂的产品。让我们看看我们程序的输出。


在结束之前,让我们看看我们应用程序的类图,并将其与 Builder 模式的类图进行比较。


关注点

在本文中,我试图描述 Builder 模式,何时需要它,并提供了 C# 中 Builder 模式的基本实现。

历史

  • 2012 年 10 月 4 日:第一个版本。
© . All rights reserved.