前言
没有人买车会只买一个轮胎或一个方向盘,大家买的都是一辆包含轮胎、方向盘和发动机等多个部件的完整汽车。如何将这些部件组装成一辆完整的汽车并返回给用户,这是建造者模式需要解决的问题。
建造者模式又称生成器模式,它是一种较为复杂、使用频率也相对较低的创建型模式。建造者模式向客户端返回的不是一个简单的产品,而是一个由多个部件组成的复杂产品。
一、游戏角色设计
A公司游戏开发小组决定开发一款名为《xx群侠传》的网络游戏,该游戏采用主流的RPG模式。玩家可以在游戏中扮演虚拟世界中的一个特定角色,角色根据不同的游戏情节和统计数据(如力量、魔法、技能等)具有不同的能力,角色也会随着不断升级而拥有更加强大的能力。
作为 RPG 游戏的一个重要组成部分,需要对游戏角色进行设计,而且随着该游戏的升级将不断增加新的角色。不同类型的游戏角色,其性别、脸型、服装、发型等外部特性都有所差异,例如“天使”拥有美丽的面容和披肩长发,并且穿一身白裙;而“恶魔”极其丑陋,留着光头并穿一件刺眼的黑衣。
A 公司决定开发一个小工具来创建游戏角色,可以创建不同类型的角色并可以灵活的增加新的角色。
开发人员通过分析发现,游戏角色是一个复杂对象,它包含性别、脸型等多个组成部分,不同的游戏角色其组成部分有所差异。
但是,无论何种造型的游戏角色,他们的创建步骤都大同小异,都需要逐步创建其组成部分,再将各部分装配成一个完整的游戏角。如何一步一步的创建一个包含多个组成部分的复杂对象,建造者模式为解决此类问题而诞生。
二、建造者模式概述
建造者模式是较为复杂的创建型模式,它将客户端与包含多个组成部分(或部件)的复杂对象的创建过程分离。客户端无需知道复杂对象的内部组成部分与装配方式,只需要知道所需要的建造者类型即可。建造者模式关注如何一步一步的创建一个复杂对象,不同的具体建造者定义了不同的创建过程,而且具体建造者相互独立,增加新的建造者非常方便,无需修改已有代码,系统具有较好的扩展性。建造者定义如下:
建造者模式(Builder Pattern):将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。建造者模式是一种对象创建型模式。
建造者模式结构如图:
在建造者模式结构图中包含以下4个角色:
-
Builder(抽象建造者):它为创建一个产品 Product 对象的各个部件指定抽象接口,在该接口中一般声明两类方法:一类方法是 BuildPartX(),用于创建复杂对象的各个部件;另一类方法是 GetResult() ,用于返回复杂对象。Builder 既可以是抽象类,也可以是接口。 -
ConcreteBuilder(具体建造者):它实现了 Builder 接口,实现各个部件的具体构造和装配方法,定义并明确其所创建的复杂对象,也可以提供一个方法返回创建好的复杂产品对象。 -
Product(产品角色):它是被构建的复杂对象,包含多个组成部件。具体建造者创建该产品的内部表示并定义其装配过程。 -
Director(指挥者):它负责安排复杂对象的建造次序。指挥者与抽象建造者之间存在关联关系,可以在其 Construct() 方法中调用建造者对象的部件构造与装配方法,完成复杂对象的建造。客户端一般只需要与指挥者进行交互,在客户端确定具体建造者的类型,并实例化具体建造者对象(也可以通过配置文件或反射机制),然后通过指挥者的构造函数或Setter方法将该对象传入指挥者类中。
什么是复杂对象?简单的说,复杂对象是指那些包含多个成员变量的对象,这些成员变量也称为部件或零件。典型复杂对象代码如下:
public class Product {
public string PartA { get; set; }
private string PartB { get; set; }
private string PartC { get; set; }
}
抽象建造者类中定义了产品的创建方法和返回方法,典型代码如下:
public abstract Builder {
//创建产品对象
protected Product product = new Product();
public abstract void BuildPartA();
public abstract void BuildPartB();
public abstract void BuildPartC();
//返回产品对象
public Product GetResult() {
return product;
}
}
指挥者类 Director ,该类主要有两个作用:一方面它隔离了客户与创建过程,另一方面它控制产品的创建过程。指挥者针对抽象建造者编程,客户端只需要知道具体建造者的类型,即可通过指挥者类调用建造者的相关方法,返回一个完整的产品对象。指挥者类的代码如下:
public class Director {
private Builder _builder;
public Director (Builder builder){
_builder = builder;
}
public void SetBuilder(Builder builder){
_builder = builder;
}
//产品构建和组装方法
public Product Construct() {
_builder.BuildPartA();
_builder.BuildPartB();
_builder.BuildPartC();
return _builder.GetResult();
}
}
对于客户端来说,只需关心具体的建造者即可。代码如下:
...
Builder builder = new ConcreteBuilder();
Director director = new Director(builder);
Product product = director.Construct();
...
可以通过配置文件来存储具体建造者类的类名,使得更换新的建造者时,无需修改源代码,系统扩展更方便。
三、完整解决方案
开发人员决定使用建造者模式来实现游戏角色的创建,基本结构如下:
-
ActorController :充当指挥者 -
ActorBuilder :充当抽象建造者 -
HeroBuilder、AngelBuilder、DevilBuilder充当具体建造者 -
Actor:充当复杂产品
完整代码如下:
/// <summary>
/// 角色类:复杂产品
/// </summary>
public 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; }
}
/// <summary>
/// 角色建造器:抽象建造者
/// </summary>
public 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();
/// <summary>
/// 工厂方法,返回一个完整的游戏角色对象
/// </summary>
/// <returns></returns>
public Actor CreateActor()
{
return actor;
}
}
/// <summary>
/// 英雄角色建造器:具体建造者
/// </summary>
public 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 = "飘逸";
}
}
/// <summary>
/// 天使角色建造器:具体建造者
/// </summary>
public class AngelBuilder : 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 = "披肩长发";
}
}
/// <summary>
/// 恶魔角色建造器:具体建造者
/// </summary>
public class DevilBuilder : 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 = "光头";
}
}
/// <summary>
/// 游戏角色创建控制器:指挥者
/// </summary>
public class ActorController
{
/// <summary>
/// 逐步构建复杂产品对象
/// </summary>
/// <param name="ab"></param>
/// <returns></returns>
public Actor Construct(ActorBuilder ab)
{
ab.BuildType();
ab.BuildSex();
ab.BuildFace();
ab.BuildCostume();
ab.BuildHairstyle();
var actor = ab.CreateActor();
return actor;
}
}
将具体的建造者写在配置文件中,通过反射来创建:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<appSettings>
<add key="ConcreteBuilder" value="LXP.DesignPattern.Builder.AngelBuilder"/>
</appSettings>
</configuration>
/// <summary>
/// 配置文件帮助类
/// </summary>
public static class AppConfigHelper
{
public static object GetBuilder()
{
try
{
var concreteBuilder = ConfigurationManager.AppSettings["ConcreteBuilder"];
var type = Type.GetType(concreteBuilder);
return type == null ? null : Activator.CreateInstance(type);
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
return null;
}
}
客户端代码:
class Program
{
static void Main(string[] args)
{
var ab = AppConfigHelper.GetBuilder() as ActorBuilder;
var controller=new ActorController();
var actor = controller.Construct(ab);
Console.WriteLine($"{actor.Type}的外观");
Console.WriteLine($"性别:{actor.Sex}");
Console.WriteLine($"面容:{actor.Face}");
Console.WriteLine($"服装:{actor.Costume}");
Console.WriteLine($"发行:{actor.Hairstyle}");
}
}
编译并输出结果:
建造者模式中,客户端只需要实例化指挥者类,指挥者类针对抽象建造者编程。客户端根据实际需要传入具体的建造者类型,指挥者将指导具体建造者一步一步的构造一个完整的产品,相同的构建过程可以创建完全不同的产品。如果需要更换角色,只需要修改配置文件,更换具体的角色建造者类即可,如果要新增角色,可以新增一个具体的角色建造者类作为抽象角色建造者的子类,再修改配置文件即可,原有代码无须修改,符合开闭原则。
四、建造者模式总结
建造者模式的核心在于如何一步一步的构建一个包含多个组成部件的完整对象,使用相同的构建过程构建不同的产品。在软件开发中,如果需要创建复杂对象,并希望系统具备很好的灵活性和可扩展性,可以考虑使用建造者模式。
1.主要优点
-
在建造者模式中,客户端不必知道产品内部组成的细节,将产品本身与产品的创建过程解耦,使得相同的创建过程可以创建出不同的产品对象。 -
每个具体的建造者都相对独立,而与其它具体建造者无关。因此,可以很方便的替换具体建造者或增加新的具体建造者。由于指挥者针对抽象建造者编程,增加新的具体建造者无须修改原有类库的代码,系统扩展更加方便,符合开闭原则。 -
可以更加精细的控制产品的创建过程。将复杂产品的创建步骤分解在不同的方法中,使得创建过程更加清晰,也更加方便使用程序来控制创建过程。
2.主要缺点
-
建造者模式所创建的产品一般都具有较多的共同点,其组成部分相似。如果产品之间的差异性很大,例如很多组成部分都不相同,就不适合使用建造者模式,因此其使用范围受到一定的限制。 -
如果产品的内部结构复杂且多变,可能会需要定义很多具体建造者来实现这种变化,这就导致系统变的很庞大,增加系统的理解难度和运维技术。
3.使用场景
-
需要生成的产品对象有复杂的内部结构,这些产品对象通常包含多个成员变量。 -
需要生成的产品对象的属性相互依赖,需要指定其生成顺序。 -
对象的创建过程独立于创建该对象的类。在建造者模式中通过引入指挥者类,将创建过程封装在指挥者类中,而不在建造者类和客户类中。 -
隔离复杂对象的创建和使用,并使得相同的创建过程可以创建不同的产品。
示例代码:
https://github.com/crazyliuxp/DesignPattern.Simples.CSharp
参考资料:
原文始发于微信公众号(十一学技术):一起学习设计模式–06.建造者模式
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/20363.html