1.5.1 模式意图:
当面临一个复杂对象的创建时,通常由各个子对象按照一定的顺序组合而成;随着需求的不断变化,复杂对象对应的各个子对象也随之变化,但组合顺序相对稳定。我们需要将一个复杂的构建与其表示相分离,使得同样的构建过程或顺序可以创建不同的复杂对象。使用建造者模式即可达到这样的目的。
1.5.2 模式概念:
将一个复杂的构建与其表示相分离,使得同样的构建过程可以创建不同的表示。使用多个简单的对象一步一步构建成一个复杂的对象。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
1.5.3 模式元素:
- 抽象建造者(AbstractBuilder)
- 具体建造者(HouseBuilder)
- 指挥者(Director)
- 产品角色(House、Cement、Brick等)
1.5.4 代码示例:
写一个建造房子的例子:首先你要准备水泥,钢筋,砖块,然后盖一层、二层、三层直到最高层,通电,通水等等多道“工序”才能完成成品。其中每一道工序都是对产品的全新创建、但是我们只关心最后的终极产品—【房子】。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Custom.Log;
# region BaseClass
///
/// 水泥
///
public class Cement { }
///
/// 砖块
///
public class Brick { }
///
/// 钢筋
///
public class Rebar { }
///
/// 楼层
///
public class Floor { }
///
/// 水
///
public class Water { }
///
/// 电
///
public class Electricity { }
///
/// 房屋
///
public class House { }
#endregion
public abstract class AbstractBuilder
{
public abstract void Build_Cement();
public abstract void Build_Brick();
public abstract void Build_Rebar();
public abstract void Build_Floor();
public abstract void Build_Water();
public abstract void Build_Electricity();
public abstract House Build_House();
}
public class HouseBuilder : AbstractBuilder
{
public override void Build_Brick()
{
this.Log("建造砖块");
}
public override void Build_Cement()
{
this.Log("建造水泥");
}
public override void Build_Electricity()
{
this.Log("建造电力");
}
public override void Build_Floor()
{
this.Log("建造楼层");
}
public override void Build_Rebar()
{
this.Log("建造钢筋");
}
public override void Builde_Water()
{
this.Log("建造水");
}
public override House Build_House()
{
this.Log("房屋建造完成");
return new House();
}
}
public class Director
{
private AbstractBuilder abstractBuilder = null;
public Director(AbstractBuilder tempAbstractBuilder)
{
abstractBuilder = tempAbstractBuilder;
}
public House GetResult()
{
abstractBuilder.Build_Brick();//建造砖块
abstractBuilder.Build_Cement();//建造水泥
abstractBuilder.Build_Rebar();//建造钢筋
abstractBuilder.Build_Floor();//建造楼层
abstractBuilder.Build_Electricity();//电力完成
abstractBuilder.Build_Water();//水源完成
House temp = abstractBuilder.Builde_House();//成品房屋完成
this.Log($"完全品{temp}完成");
return temp;
}
}
调用
public class BuilderComponent : MonoBehaviour
{
void Start()
{
AbstractBuilder abstractBuilder = new HouseBuilder();
Director director = new Director(abstractBuilder);
House house = director.GetResult();
}
}
打印信息
1.5.5 写法对比:
略
1.5.6 模式分析:
下面笔者和大家具体分析一下建造者模式,其实这个和抽象工厂模式特别的相似,都是创建产品,但建造者模式只是把原有工厂的创建函数交给了指挥者(Director)来执行。从写法上讲,我认为【AbstractFactoryPlus】比【Builder】更贴切,就是让多行的创建代码演变成现在的
Director director = new Director(abstractBuilder);
与House house = director.GetResult();
,从结构上看是把原本的创建过程再次的封装和转移,如果想修改创建过程可以直接在Director类中修改。最后通过Director组装,在组装的过程中可以不断的向一个容器内添加,获取最终的产品。从职责划分上看:
【抽象工厂】主要是没有元素或者说一个元素对应一个产品,以这种方式产出多个产品。
【建造者模式】是多个元素建造一个产品。目的是关心一个复杂产品的构建过程(再次封装和转移)。指挥者(Director)主要是用于创建一些复杂的对象,这些对象内部的构建顺序通常是稳定的,但对象内部的构建通常面临着复杂的变化。不过真的需要建造顺序更改时,我们也可以重新定义一个新的Director,只需要把原有的Director类替换掉就可以,上层的改动也相对较小,绝对的开闭原则是不存在的,相对的开闭才是我们应该掌握的。
【扩展说明】
或许有人会问,为什么不用接口代替对应的
AbstractBuilder
呢?
笔者是这样理解的,接口是一个独立的功能,不是一个系统。以手机为例:它含有上网、听音乐、看电影、储存、拍照、收发信息等功能,每一个功能都可以是一个接口。且这种接口具有通用性。问什么说是通用性?
例如上网功能接口电脑也可以继承,拍照这种功能接口专业摄像机也可以继承这就是通用性。这种微小独立功能的划分也符合接口隔离原则。但是抽象类呢,它更像一个特殊的缩小系统,就像手机是众多电子产品中的一种,但手机的品牌很多,基于这种缩小系统基本一致性的特点,不同的品牌,我们就可以根据手机抽象类实现不同的品牌实例。而且在抽象基类里面可以实现一些字段和函数,接口则不能。
接口是能够,抽象是含有。
【举例说明】
List这种就是集各种通用功能于一身的
这种文件操作的抽象类就很局限了
1.5.7 应用场景:
- 生成的对象具有复杂的内部结构。
- 生成的对象内部属性本身相互依赖。
1.5.8 小结:
-
笔者认为,如果构建过程相对简单,2-5行代码可以解决的问题用工厂模式就可以了,如果十分复杂的创建就可以考虑下建造者模式。
-
建造者模式的好处就是使得建造代码与表示代码分离,由于建造者隐藏了该产品的组装顺序和细节,所以若需要改变一个产品的内部表示,只需要再定义一个具体的建造者(示例中的BuilderHouse)就可以了。