DEV Community

菜皮日记
菜皮日记

Posted on

创建型设计模式-建造者 Builder

简介

建造者模式可将对象的初始化转变成一步步配置的过程。如当对象的初始化时有很多可选参数,建造者模式可以定制参数实现对象的创建。好处有:

  1. 定制对象参数
  2. 针对不同参数,做不同的校验,如当设置了三角形的两个边长,设置第三个边时必须满足两边之和大于第三边的条件。

角色

  • Builder 类

    定义建造一个Product分几个步骤

  • 具体 Builder 类

    实现不同的步骤

  • Director

    属于快速建造某一种产品的方法,如 Director 提供了创建自动挡和手动挡汽车两种方法,创建自动挡汽车中其实是调用 setA setB,而创建自动单汽车中调用setC setD。也可以不使用 Director,直接使用 Builder 的 setA setB 去设置属性

  • 要被实例化的类

    初始化时可定制的参数较多,如 setA setB setC...

类图

图中的 Director 在 make 方法中封装了设置产品属性的步骤,通过传入不同的 builder 类,实现不同的实现步骤,创建不同的产品。

类图

代码

interface Builder
{
    public function producePartA(): void;

    public function producePartB(): void;

    public function producePartC(): void;
}

class ConcreteBuilder1 implements Builder
{
    private $product;

    public function __construct()
    {
        $this->reset();
    }

    public function reset(): void
    {
        $this->product = new Product1();
    }

    public function producePartA(): void
    {
        $this->product->parts[] = "PartA1";
    }

    public function producePartB(): void
    {
        $this->product->parts[] = "PartB1";
    }

    public function producePartC(): void
    {
        $this->product->parts[] = "PartC1";
    }

    public function getProduct(): Product1
    {
        $result = $this->product;
        $this->reset();

        return $result;
    }
}

/**
 * EN: It makes sense to use the Builder pattern only when your products are
 * quite complex and require extensive configuration.
 *
 * Unlike in other creational patterns, different concrete builders can produce
 * unrelated products. In other words, results of various builders may not
 * always follow the same interface.
 *
 * RU: Имеет смысл использовать паттерн Строитель только тогда, когда ваши
 * продукты достаточно сложны и требуют обширной конфигурации.
 *
 * В отличие от других порождающих паттернов, различные конкретные строители
 * могут производить несвязанные продукты. Другими словами, результаты различных
 * строителей могут не всегда следовать одному и тому же интерфейсу.
 */
class Product1
{
    public $parts = [];

    public function listParts(): void
    {
        echo "Product parts: " . implode(', ', $this->parts) . "\n\n";
    }
}

class Director
{
    private $builder;

    public function setBuilder(Builder $builder): void
    {
        $this->builder = $builder;
    }

    public function buildMinimalViableProduct(): void
    {
        $this->builder->producePartA();
    }

    public function buildFullFeaturedProduct(): void
    {
        $this->builder->producePartA();
        $this->builder->producePartB();
        $this->builder->producePartC();
    }
}

function clientCode(Director $director)
{
    $builder = new ConcreteBuilder1();
    $director->setBuilder($builder);

    echo "Standard basic product:\n";
    $director->buildMinimalViableProduct();
    $builder->getProduct()->listParts();

    echo "Standard full featured product:\n";
    $director->buildFullFeaturedProduct();
    $builder->getProduct()->listParts();

    // 不使用 Director,直接操作 builder 来创建产品
    echo "Custom product:\n";
    $builder->producePartA();
    $builder->producePartC();
    $builder->getProduct()->listParts();
}

$director = new Director();
clientCode($director);
Enter fullscreen mode Exit fullscreen mode

output:

Standard basic product:
Product parts: PartA1

Standard full featured product:
Product parts: PartA1, PartB1, PartC1

Custom product:
Product parts: PartA1, PartC1
Enter fullscreen mode Exit fullscreen mode

AWS GenAI LIVE image

Real challenges. Real solutions. Real talk.

From technical discussions to philosophical debates, AWS and AWS Partners examine the impact and evolution of gen AI.

Learn more

Top comments (0)

Sentry image

See why 4M developers consider Sentry, “not bad.”

Fixing code doesn’t have to be the worst part of your day. Learn how Sentry can help.

Learn more

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay