【再谈设计模式】装配器模式 ~复杂结构构建的巧匠
一、引言
在软件开发过程中,创建复杂对象往往是一项具有挑战性的任务。传统的直接实例化对象的方式在面对复杂对象构建时,可能会导致代码的可维护性、可读性和扩展性变差。这时候,我们就需要一些设计模式来优雅地解决这些问题,装配器设计模式就是其中一种强大的解决方案。它提供了一种有条理的方式来构建复杂对象,使得对象的构建过程更加清晰、灵活并且易于管理。
二、定义与描述
装配器设计模式是一种创建复杂对象的设计模式,它将对象的构建过程从对象本身分离出来,通过一个装配器(Builder)来逐步构建复杂对象的各个部分,最终组装成完整的对象。
这种模式允许我们按照特定的步骤和顺序构建对象,并且可以在构建过程中对各个部分进行定制化操作。例如,在构建一个复杂的汽车对象时,我们可以先构建引擎部分,然后是车身部分,接着是内饰部分,最后将这些部分组装在一起形成完整的汽车。
三、抽象背景
在许多应用场景中,复杂对象的构建往往涉及到多个不同类型的组件或者属性的设置。这些组件可能具有复杂的初始化逻辑或者依赖关系。如果将所有的构建逻辑都放在对象的构造函数中,那么构造函数会变得非常臃肿,难以理解和维护。而且,当需要对对象的构建过程进行修改或者扩展时,直接修改构造函数可能会影响到其他使用该对象的代码。
装配器设计模式的出现就是为了将这些复杂的构建逻辑从对象本身抽象出来,通过一个独立的装配器来管理对象的构建过程,从而提高代码的可维护性和扩展性。
四、适用场景与现实问题解决
(一)适用场景
- 复杂对象构建
- 当需要创建的对象具有多个属性,并且这些属性的设置具有一定的顺序或者依赖关系时,装配器设计模式非常适用。例如,构建一个数据库连接对象,需要设置数据库类型、主机地址、端口号、用户名和密码等多个属性,而且这些属性的设置顺序和正确性对数据库连接的成功与否至关重要。
- 对象构建过程的定制化
- 如果对于同一个对象,根据不同的需求或者条件需要有不同的构建方式,装配器设计模式可以很好地满足这种需求。比如,构建一个用户界面组件,根据不同的用户权限或者设备类型(桌面端、移动端),可以定制不同的构建过程,如添加不同的功能按钮或者布局方式。
(二)现实问题解决
- 提高代码可读性
- 在没有装配器设计模式时,复杂对象的构建逻辑可能分散在多个地方或者嵌套在一个庞大的构造函数中。使用装配器设计模式后,构建过程被清晰地分解为多个步骤,每个步骤都有明确的目的,使得代码的可读性大大提高。
- 易于维护和扩展
- 当需要对复杂对象的构建逻辑进行修改或者添加新的属性时,只需要在装配器中进行相应的修改,而不需要在对象的构造函数或者其他使用该对象的地方进行大规模的改动。
五、装配器模式的现实生活的例子
- 建筑房屋
- 建筑一座房屋可以看作是构建一个复杂对象。首先,建筑工人会构建地基部分(类似于对象的一个组件构建),然后是框架结构,接着是墙壁、屋顶等部分。每个部分都有其特定的构建过程和要求,最后将这些部分组合在一起就形成了完整的房屋。这里的建筑工人就可以看作是装配器,他们按照一定的顺序和方法构建房屋的各个部分并将其组装起来。
- 组装电脑
- 组装一台电脑时,我们首先会安装CPU(可能需要涂抹散热硅脂等操作),然后是安装内存、硬盘、主板等组件,最后将机箱组装起来并连接各种线缆。这个过程中,电脑组装员就像是装配器,他们逐个安装电脑的各个组件,最终构建出一台完整的电脑。
六、初衷与问题解决
- 初衷
- 装配器设计模式的初衷是为了将复杂对象的构建过程从对象本身分离出来,以提高代码的可维护性、可读性和扩展性。通过将构建逻辑封装在一个独立的装配器中,使得对象的构建过程更加清晰,易于理解和管理。
- 问题解决
- 解决了复杂对象构建时构造函数过于臃肿的问题。在传统方式下,复杂对象的构造函数可能需要接受多个参数并进行复杂的初始化逻辑,这使得构造函数难以维护和扩展。装配器设计模式将这些逻辑分散到各个构建步骤中,使得代码结构更加清晰。同时,也解决了构建过程定制化的问题,通过不同的装配器实现或者装配器的不同使用方式,可以轻松实现对象构建的定制化。
七、代码示例
(一)Java示例
类图:
代码:
// 产品类:汽车
class Car {
private String engine;
private String body;
private String interior;
public Car(String engine, String body, String interior) {
this.engine = engine;
this.body = body;
this.interior = interior;
}
public String getEngine() {
return engine;
}
public String getBody() {
return body;
}
public String getInterior() {
return interior;
}
}
// 装配器接口
interface CarBuilder {
CarBuilder buildEngine(String engine);
CarBuilder buildBody(String body);
CarBuilder buildInterior(String interior);
Car getCar();
}
// 具体装配器实现
class ConcreteCarBuilder implements CarBuilder {
private String engine;
private String body;
private String interior;
@Override
public CarBuilder buildEngine(String engine) {
this.engine = engine;
return this;
}
@Override
public CarBuilder buildBody(String body) {
this.body = body;
return this;
}
@Override
public CarBuilder buildInterior(String interior) {
this.interior = interior;
return this;
}
@Override
public Car getCar() {
return new Car(engine, body, interior);
}
}
// 导演类(可选,用于控制构建过程)
class Director {
public Car constructCar(CarBuilder builder) {
return builder.buildEngine("V8")
.buildBody("SUV Body")
.buildInterior("Luxury Interior")
.getCar();
}
}
Car
类表示最终要构建的产品,它有发动机、车身和内饰等私有属性,并提供了相应的构造函数和获取属性的方法。CarBuilder
接口定义了构建汽车各个部件的方法以及获取最终构建好的汽车的方法。ConcreteCarBuilder
类实现了CarBuilder
接口,具体实现了构建汽车各个部件的逻辑,并能根据已设置的部件信息返回构建好的Car
实例。Director
类是可选的,用于控制构建过程,它通过调用CarBuilder
的一系列构建方法来构建出特定配置的汽车。
通过这种设计模式,可以将复杂对象的构建过程与其表示分离,使得同样的构建过程可以创建不同表示的对象,并且构建过程更加灵活和可维护。
使用流程图:
(二)C++示例
#include <iostream>
#include <string>
// 产品类:汽车
class Car {
private:
std::string engine;
std::string body;
std::string interior;
public:
Car(const std::string& engine, const std::string& body, const std::string& interior) :
engine(engine), body(body), interior(interior) {}
const std::string& getEngine() const {
return engine;
}
const std::string& getBody() const {
return body;
}
const std::string& getInterior() const {
return interior;
}
};
// 装配器类
class CarBuilder {
protected:
std::string engine;
std::string body;
std::string interior;
public:
virtual CarBuilder& buildEngine(const std::string& engine) {
this->engine = engine;
return *this;
}
virtual CarBuilder& buildBody(const std::string& body) {
this->body = body;
return *this;
}
virtual CarBuilder& buildInterior(const std::string& interior) {
this->interior = interior;
return *this;
}
virtual Car build() = 0;
};
// 具体装配器实现
class ConcreteCarBuilder : public CarBuilder {
public:
Car build() override {
return Car(engine, body, interior);
}
};
// 导演类(可选,用于控制构建过程)
class Director {
public:
Car constructCar(CarBuilder* builder) {
return builder->buildEngine("V8")
->buildBody("SUV Body")
->buildInterior("Luxury Interior")
->build();
}
};
(三)Python示例
# 产品类:汽车
class Car:
def __init__(self, engine, body, interior):
self.engine = engine
self.body = body
self.interior = interior
def get_engine(self):
return self.engine
def get_body(self):
return self.body
def get_interior(self):
return self.interior
# 装配器类
class CarBuilder:
def __init__(self):
self.engine = None
self.body = None
self.interior = None
def build_engine(self, engine):
self.engine = engine
return self
def build_body(self, body):
self.body = body
return self
def build_interior(self, interior):
self.interior = interior
return self
def get_car(self):
return Car(self.engine, self.body, self.interior)
# 导演类(可选,用于控制构建过程)
class Director:
def construct_car(self, builder):
return builder.build_engine('V8') \
.build_body('SUV Body') \
.build_interior('Luxury Interior') \
.get_car()
(四)Go示例
// 产品类:汽车
type Car struct {
engine string
body string
interior string
}
func NewCar(engine, body, interior string) *Car {
return &Car{
engine: engine,
body: body,
interior: interior,
}
}
func (c *Car) GetEngine() string {
return c.engine
}
func (c *Car) GetBody() string {
return c.body
}
func (c *Car) GetInterior() string {
return c.interior
}
// 装配器接口
type CarBuilder interface {
BuildEngine(string) CarBuilder
BuildBody(string) CarBuilder
BuildInterior(string) CarBuilder
GetCar() *Car
}
// 具体装配器实现
type concreteCarBuilder struct {
engine string
body string
interior string
}
func (c *concreteCarBuilder) BuildEngine(engine string) CarBuilder {
c.engine = engine
return c
}
func (c *concreteCarBuilder) BuildBody(body string) CarBuilder {
c.body = body
return c
}
func (c *concreteCarBuilder) BuildInterior(interior string) CarBuilder {
c.interior = interior
return c
}
func (c *concreteCarBuilder) GetCar() *Car {
return NewCar(c.engine, c.body, c.interior)
}
// 导演类(可选,用于控制构建过程)
type Director struct{}
func (d *Director) ConstructCar(builder CarBuilder) *Car {
return builder.BuildEngine("V8")
.BuildBody("SUV Body")
.BuildInterior("Luxury Interior")
.GetCar()
}
八、装配器模式的优缺点
(一)优点
- 构建过程的清晰性
- 如前面所述,通过将复杂对象的构建过程分解为多个步骤,每个步骤由装配器的一个方法来表示,使得构建过程清晰可见,易于理解。
- 对象构建的灵活性
- 可以根据不同的需求,通过不同的装配器实现或者装配器的不同使用方式,轻松实现对象构建的定制化。例如,可以根据不同的用户需求构建不同配置的产品对象。
- 代码的可维护性和扩展性
- 当需要对对象的构建过程进行修改,如添加新的属性或者修改某个属性的构建逻辑时,只需要在装配器中进行相应的修改,而不需要对整个对象的构造函数或者使用该对象的其他代码进行大规模的改动。
(二)缺点
- 增加代码复杂度
- 引入装配器设计模式会增加一定的代码量,因为需要创建装配器类、产品类以及可能的导演类等。对于简单的对象构建场景,可能会显得过于复杂。
- 需要更多的设计考虑
- 在使用装配器设计模式时,需要仔细考虑装配器与产品类之间的关系、构建步骤的划分以及装配器的复用性等问题,如果设计不当,可能会导致代码难以理解和维护。
九、适配器设计模式的升级版
装配器设计模式可以看作是适配器设计模式的一种升级版(在某种意义上)。适配器设计模式主要解决的是接口不兼容的问题,将一个类的接口转换成客户期望的另一个接口。而装配器设计模式在此基础上更进一步,不仅可以处理接口的转换(在构建复杂对象时可能涉及到不同组件接口的适配),还专注于复杂对象的构建过程。
装配器设计模式通过将对象的构建过程分离出来,在构建过程中可以更灵活地处理不同组件之间的适配关系,并且可以根据不同的需求构建出具有不同特性的对象,这是相比于传统适配器设计模式在处理复杂对象构建方面的一种扩展和升级。