Design Mode | Builder Mode

1 | Overview of Builder Mode

Whether in the real world or in a software system, there are some complex objects that have multiple components (parts), such as a car, which includes wheels, steering wheels, engines, and other components. For most users, they don't know the assembly details of these parts, and they hardly use a single part, but use a complete car.

Thinking: In the face of the above scenario, how to assemble these parts into a complete car and return it to the user, and this scenario happens to be the problem that the builder mode needs to solve. The builder mode can separate the parts themselves from their assembly process, focusing on how to create a complex object that contains multiple components step by step. Users only need to specify the type of the complex object to get the object without knowing the specifics inside. Build details.

Definition of builder mode

  • Builder mode: Separate the construction of a complex object from its representation, so that the same construction process can create different representations.
  • Builder Pattern: Separate the construction of a complex object from its representation so that the same construction process can create different representations.

The builder mode is an object creation mode that separates the client from the creation process of a complex object containing multiple parts. The client does not need to know the internal components and assembly methods of the complex object, but only needs to know the required builder's Just type. The builder mode focuses on how to gradually create a complex object. Different builders define different creation processes, and the specific builders are independent of each other. It is very convenient to replace and add builders, and the system has good scalability.

2 | The structure and realization of the builder pattern

2.1 The structure of the builder mode

  • (1) Builder (abstract builder) : It specifies an abstract interface for each part that creates a Product object. Generally, two types of methods are declared in the interface. One type of method is BuildPartX() (for example, BuildPartA in Figure 6-2 (), BuildPartB(), etc.), which are used to create various parts of complex objects; another type of method is GetResult(), which is used to return complex objects. Builder can be either an abstract class or an interface.
  • (2) ConcreteBuilder (concrete builder) : It implements the Builder interface, realizes the specific construction and assembly methods of each component, defines and clarifies the complex objects created, and can also provide a method to return the created complex product objects (the method It can also be implemented by an abstract builder).
  • (3) Product (product) : it is a complex object to be constructed, including multiple component parts, the concrete builder creates the internal representation of the product and defines its assembly process.
  • (4) Director (director) : The director is also called the director class, which is responsible for arranging the construction sequence of complex objects. There is an association relationship between the director and the abstract builder, and the builder can be called in its Construct() construction method The component structure and assembly method of the object to complete the construction of complex objects. The client generally only needs to interact with the commander, determine the type of the specific builder on the client, and instantiate the specific builder object (also through configuration files and reflection mechanisms), and then use the constructor or Setter method of the commander class Pass the object into the conductor class.

2.2 Implementation of the builder mode

The complex object is mentioned in the definition of the construction mode, so what is a complex object? Simply put, a complex object refers to an object that contains multiple member variables, and these member objects are also called components or parts. For example:

  • Automobiles (complex objects) include: steering wheels, lights, engines, tires, seats and other components;
  • Emails (complex objects) include: sender, recipient, subject, content, attachments and other components;

Code design of builder mode

using System;

namespace BuilderPattern
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Hello BuilderPattern!");

            {
                Builder builder = new ConcreteBuilder1();
                Director director = new Director(builder);
                Product product = director.Construct(); //构建复杂对象
                Console.WriteLine($"【复杂对象】=> PartA:{product.PartA},PartB:{product.PartB},PartC:{product.PartC}");
            }
        }
    }

    #region BuilderPattern-Demo
    /// <summary>
    /// 产品
    /// </summary>
    class Product
    {
        public string PartA { get; set; }
        public string PartB { get; set; }
        public string PartC { get; set; }
    }

    /// <summary>
    /// 构建着 & 抽象类
    /// </summary>
    abstract class Builder
    {
        //创建产品对象
        protected readonly Product product = new Product();

        public abstract void BuildPartA();
        public abstract void BuildPartB();
        public abstract void BuildPartC();

        /// <summary>
        /// 返回产品对象
        /// </summary>
        /// <returns></returns>
        public Product GetResult()
        {
            return product;
        }
    }

    /// <summary>
    /// 具体构建者
    /// </summary>
    class ConcreteBuilder1 : Builder
    {
        public override void BuildPartA()
        {
            product.PartA = "A1";
        }

        public override void BuildPartB()
        {
            product.PartB = "B1";
        }

        public override void BuildPartC()
        {
            product.PartC = "C1";
        }
    }

    /// <summary>
    /// 指挥者
    /// </summary>
    class Director
    {
        private Builder _builder;
        public Director(Builder builder)
        {
            _builder = builder;
        }

        public void SetBuilder(Builder builder)
        {
            _builder = builder;
        }

        /// <summary>
        /// 产品构建与组装方法
        /// </summary>
        /// <returns></returns>
        public Product Construct()
        {
            _builder.BuildPartA();
            _builder.BuildPartB();
            _builder.BuildPartC();

            return _builder.GetResult();
        }
    }
    #endregion
}

An object of abstract builder type can be injected into the conductor class. It provides a constructor method Construct() in which the method of constructing parts of the builder object is called, and finally a product object is returned.

For customers, they only need to care about the specific types of builders, not the specific assembly process of the product objects.

Users can store the name of the concrete builder class ConcreteBuilder1 through the configuration file, so that the source code does not need to be modified when changing to a new builder, and the system expansion is more convenient.

2.3 The similarities, differences and associations between the builder pattern and the abstract factory pattern

  • Common ground: The builder pattern and the abstract factory pattern are both more complex and creative patterns.
  • Difference: the focus is different. The builder model returns a complete complex product, while the abstract factory model returns a series of related products. In the abstract factory model, the client selects a concrete factory to generate the required objects. In the builder mode, the client guides the Director on how to generate an object by specifying a specific builder type, focusing on constructing a complex object step by step, and then returning the result.
  • Relevance: If the abstract factory model is regarded as an auto parts production plant that generates different types of auto parts, then the builder model is an auto assembly plant that returns a complete car by assembling parts.

2.4 Application examples of the builder mode

2.4.1 Example description

A game software company decided to develop a multiplayer online game based on role-playing. Players can play a specific role in the virtual world. The role is based on different game plots and statistics (such as power, magic, skills, etc.) With different abilities, the characters will have more powerful abilities as they continue to upgrade.
As an important part of the game, the game characters need to be designed, and new characters will be added as the game is upgraded. Through analysis, it is found that a game character is a complex object, which contains multiple components such as gender and face. Different types of game characters have different external characteristics such as gender, face, clothing, and hairstyle. For example, "angels" have beautiful His face and long hair in a shawl, and he wore a white dress; while the "devil" was extremely ugly, with a bald head and wearing a dazzling black dress.
No matter what kind of game character it is, the steps to create it are the same. You need to create its components step by step, and then assemble each component into a complete game character. Now use the builder mode to realize the creation of game characters.

2.4.2 Example role structure diagram

2.4.3 Example code

(1) Actor: Game role class, acting as a complex product object. Here is a case explanation, simplifying the data types of member variables, using string uniformly, and the specific types in the real environment are defined according to the actual situation.

/// <summary>
/// 游戏角色类,充当复杂产品对象。
/// </summary>
class Actor
{
    /// <summary>
    /// 角色类型
    /// </summary>
    public string Type{ get; set; }

    /// <summary>
    /// 性别
    /// </summary>
    public string Sex{ get; set; } 

    /// <summary>
    /// 面容
    /// </summary>
    public string Face{ get; set; }

    /// <summary>
    /// 服装
    /// </summary>
    public string Costume{ get; set; } 

    /// <summary>
    /// 发型
    /// </summary>
    public string Hairstyle{ get; set; } 
}

(2) ActorBuilder: Game character builder, acting as an abstract builder.

/// <summary>
/// 角色建造者:抽象建造者
/// </summary>
abstract class ActorBuilder
{
    protected readonly Actor actor = new Actor();

    public abstract void BuildType();
    public abstract void BuildSex();
    public abstract void BuildFace();
    public abstract void BuildCostume();
    public abstract void BuildHairstyle();

    //工厂方法,返回一个完整的游戏角色对象
    public Actor CreateActor()
    {
        return actor;
    }
}

(3) HeroBuilder: Hero character builder, acting as a concrete builder.

/// <summary>
/// 英雄角色建造者,充当具体建造者。
/// </summary>
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 BuildHairstyle()
    {
        actor.Hairstyle = "飘逸";
    }
}

The other two roles AngelBuilder: Angel character builder, DevilBuilder: Devil character builder, both act as concrete builders. It is similar to the above code and will not be repeated.

(4) ActorController: role controller, acting as role commander.

/// <summary>
/// 角色控制器,充当角色指挥者。
/// </summary>
class ActorController
{
    //逐步构建复杂产品对象
    public Actor Construct(ActorBuilder ab)
    {
        Actor actor;
        ab.BuildType();
        ab.BuildSex();
        ab.BuildFace();
        ab.BuildCostume();
        ab.BuildHairstyle();
        actor = ab.CreateActor();
        return actor;
    }
}

(5) The configuration file App.config, which stores the class name of the specific builder class (FullClassName) in the configuration file

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

(6) Calling Main in Client Program

// 游戏角色结构创建
//xml配置文件与反射方式扩展
// 1.读取【App.config】配置文件,key = builder
string builderType = ConfigurationManager.AppSettings["builder"];
// 2.反射生成对象
ActorBuilder ab = (ActorBuilder)Assembly.Load("BuilderPattern").CreateInstance(builderType);
ActorController ac = new ActorController();
Actor actor = ac.Construct(ab); // 通过指挥者创建完整的建造者对象

Console.WriteLine($"{actor.Type}的外观,性别:{actor.Sex},面容:{actor.Face},服装:{actor.Costume},发型:{actor.Hairstyle}");

3 | In  -depth discussion of directors 

3.1 Simplification of the builder mode

Simplified method 1: Omit Director 

In some cases, in order to simplify the system structure, the Director and the abstract builder Builder can be merged, and the Construct() method for gradually constructing complex product objects is provided in the Builder. Since the Builder class is usually an abstract class, the Construct() method can be defined as static so that the client can directly call it. If the commander class ActorController in the game role instance is omitted, the code of ActorBuilder is modified as follows:

abstract class ActorBuilder
{
    // 设置对象静态 & 只读并且实例化
    protected static readonly Actor actor = new Actor();

    public abstract void BuildType();
    public abstract void BuildSex();
    public abstract void BuildFace();
    public abstract void BuildCostume();
    public abstract void BuildHairstyle();

    // 工厂方法,返回一个完整的游戏角色对象
    public Actor CreateActor(ActorBuilder ab)
    {
        ab.BuildType();
        ab.BuildSex();
        ab.BuildFace();
        ab.BuildCostume();
        ab.BuildHairstyle();
        return actor;
    }
}

Simplified method 2: Omit Director and remove the ActorBuilder parameter of the CreateActor(ActorBuilder ab) method, and directly call the this.BuildXXX() method in this method , and modify it as follows:

abstract class ActorBuilder
{
    // 设置对象静态 & 只读并且实例化
    protected static readonly Actor actor = new Actor();

    public abstract void BuildType();
    public abstract void BuildSex();
    public abstract void BuildFace();
    public abstract void BuildCostume();
    public abstract void BuildHairstyle();

    // 工厂方法,返回一个完整的游戏角色对象
    public Actor CreateActor()
    {
        this.BuildType();
        this.BuildSex();
        this.BuildFace();
        this.BuildCostume();
        this.BuildHairstyle();
        return actor;
    }
}

Simplified client call

// 游戏角色结构创建
//xml配置文件与反射方式扩展
// 1.读取【App.config】配置文件,key = builder
string builderType = ConfigurationManager.AppSettings["builder"];
// 2.反射生成对象
ActorBuilder ab = (ActorBuilder)Assembly.Load("BuilderPattern").CreateInstance(builderType);
Actor actor = ab.Construct(); // 通过指挥者创建完整的建造者对象

Console.WriteLine($"{actor.Type}的外观,性别:{actor.Sex},面容:{actor.Face},服装:{actor.Costume},发型:{actor.Hairstyle}");

The above two simplification methods do not affect the flexibility and scalability of the system, and at the same time simplify the system structure, but increase the responsibilities of the abstract builder class. If the internal structure of the above CreateActor() method is more complicated and the product to be built has many components, it is recommended to encapsulate the CreateActor() method separately in Director, which is more in line with the single responsibility principle.

3.2 Introduction of Hook Method

In addition to gradually building a complex product object, the builder mode can also use the Director class to more finely control the creation process of the product. For example, add a special method called Hook Method to control whether to call a BuildPartX( ) Method.
The return type of the hook method is usually bool type, the method name is generally IsXXX(), and the hook method is defined in the abstract builder class. For example, you can define a method IsBareheaded() in the abstract builder class ActorBuilder of a game character to determine whether a character is "Bareheaded", and provide a default implementation for it in ActorBuilder, and its return value is false. code show as below:

//角色建造者:抽象建造者
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 BuildHairstyle();

    //钩子方法(Hook Method),需要使用 virtual 关键字
    public virtual bool IsBareheaded() 
    {
        return false;
    }

    //工厂方法,返回一个完整的游戏角色对象
    public Actor CreateActor()
    {
        return actor;
    }
}

If a character in a derived class does not need to build a certain part (such as Devil/hair part), the corresponding concrete builder DevilBuilder will override the hook method of the parent class and change its value to true to return.

/// <summary>
/// 英雄角色建造者,充当具体建造者。
/// </summary>
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 BuildHairstyle()
    {
        actor.Hairstyle = "飘逸";
    }

    // 重写覆盖父类的钩子方法
    public override bool IsBareheaded()
    {
        return true;
    }

}

 At the same time, modify the code of the conductor class ActorController as follows:

/// <summary>
/// 角色控制器,充当角色指挥者。
/// </summary>
class ActorController
{
    //逐步构建复杂产品对象
    public Actor Construct(ActorBuilder ab)
    {
        ab.BuildType();
        ab.BuildSex();
        ab.BuildFace();
        ab.BuildCostume();

        // 通过钩子方法来控制产品部件的构建
        if (!ab.IsBareheaded())
        {
            ab.BuildHairstyle();
        }

        return ab.CreateActor();
    }
}

4 | The advantages and disadvantages of the builder model and the applicable environment

The core of the builder model is how to gradually build a complete object containing multiple components, and apply the same construction process to build different products. In software development, if you want to create complex objects and want good flexibility and scalability, you can consider applying the builder model.

4.1 The main advantages of the builder mode

  • (1) In the builder mode, the client does not need to know the details of the internal composition of the product. Different product objects can be created by combining the product itself with Yanpin’s creation process.
  • (2) Each concrete builder is relatively independent and has nothing to do with other concrete builders, so it is convenient to concrete builders or add new concrete builders, and users can get different product pairs by using different concrete builders. Since the commander class is programmed for the image-lifting builder, adding new concrete builders does not need to modify the code of the original class library, and the principle of convenient development and equal cooperation is established.
  • (3) Users can more finely control the creation process of products, and decompose the creation steps of complex products into different annotations to make the creation process clearer and it is more convenient to use programs to control the creation process.

4.2 The main disadvantages of the builder mode

  • (1) The products created by the builder mode generally have more in common and their components are similar. If the differences between the products are large, for example, many components are not the same, the builder mode is not suitable for use. The scope of use is limited.
  • (2) If the internal changes of Sanlu are complex, it may be necessary to define many specific builder classes to achieve this change. As a result, the system becomes very large, which increases the difficulty of understanding the system and the operating cost.

4.3 Applicable environment of builder mode

  • (1) The product objects that need to be generated have a complex internal structure, and these product objects usually contain multiple member variables. (2) The attributes of the product objects to be generated depend on each other, and the order of their generation needs to be specified.
  • (3) The creation process of an object is independent of the class that created the object. In the builder mode, by introducing the leader class, the creation process is encapsulated in the leader class, not in the builder class and the client class.
  • (4) Isolate the creation and use of complex objects, and make the same creation process create different products.

Guess you like

Origin blog.csdn.net/ChaITSimpleLove/article/details/114550800