学习设计模式--建造者模式(Builder)

最近又看了下建造者模式Builder,在网上找了很多文章,但是总感觉有一些问题。 有些示例明明很像一个工厂模式,还有些示例会导致具体建造者类无限膨胀,好像都不大适合使用这个模式。
后来又对照GOF重新看了下,同时参考了http://www.cnblogs.com/happyhippy/archive/2010/09/01/1814287.html等文章,写下自己的理解。

1、定义
将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
在以下情况使用建造者模式
• 当创建复杂对象的算法应该独立于该对象的组成部分以及它们的装配方式时。
• 当构造过程必须允许被构造的对象有不同的表示时。

2、示例

本人很喜欢单机武侠RPG游戏,可惜现在作品太少,新出的也是一个比一个坑。不过现在这方面的网游还不少,就使用这个来做个简单的示例。
很多游戏都会充值买礼包,比如一个礼包有名称、银两、装备(可多个)三个属性,有几种常用的礼包,也可以让玩家自由组合。而且,游戏的土豪VIP客户购买礼包还会有额外惊喜!
首先设计一个礼包类。

namespace BuilderPattern.Example
{
    /// <summary>
    /// 礼包
    /// </summary>
    public class Gift
    {
        /// <summary>
        /// 名称
        /// </summary>
        public string Name { get; set; }

        /// <summary>
        /// 银两
        /// </summary>
        public int Money { get; set; }

        /// <summary>
        /// 装备
        /// </summary>
        public IList<string> Equipments { get; set; }

        public Gift()
        {
            Equipments = new List<string>();
        }

        public void Show()
        {
            Console.WriteLine(string.Concat("购买", Name, ""));
            Console.WriteLine("包含纹银{0}两", Money);
            if (Equipments.Count > 0)
            {
                Console.Write("包含装备:");
                foreach (var item in Equipments)
                {
                    Console.Write(string.Concat(item, " "));
                }
            }
            Console.WriteLine();
        }
    }
}

礼包有了,怎么来创建呢?首先,普通玩家和VIP玩家购买礼包的时候,礼包会有不同的表现,VIP会有额外惊喜(金钱翻倍,把所有的武器都做成土豪金的),那么尝试在创建的时候分开,使得同样的构建过程可以创建不同的表示,普通玩家和VIP玩家拿到的东西是不一样的。
我们先创建一个抽象的建造者类GiftBuilder,然后2个类GenrealGiftBuilder和BiggerGiftBuilder实现GiftBuilder,分别用来创建普通玩家的礼包和VIP玩家的礼包。

下面是相关的代码。

    /// <summary>
    /// 礼包建造者抽象类
    /// </summary>
    public abstract class GiftBuilder
    {
        /// <summary>
        /// 设置名称
        /// </summary>
        /// <param name="name"></param>
        public abstract void SetName(string name);

        /// <summary>
        /// 设置银两
        /// </summary>
        /// <param name="number"></param>
        public abstract void AddMoney(int number);

        /// <summary>
        /// 添加装备
        /// </summary>
        /// <param name="name"></param>
        public abstract void AddEquipment(string name);

        /// <summary>
        /// 返回礼包
        /// </summary>
        /// <returns></returns>
        public abstract Gift GetGift();
    }


    /// <summary>
    /// 普通礼包建造者
    /// </summary>
    public class GenrealGiftBuilder : GiftBuilder
    {
        private readonly Gift _gift = new Gift();

        public override void SetName(string name)
        {
            _gift.Name = name;
        }

        public override void AddMoney(int number)
        {
            _gift.Money += number;
        }

        public override void AddEquipment(string name)
        {
            _gift.Equipments.Add(name);
        }

        public override Gift GetGift()
        {
            return _gift;
        }
    }


    /// <summary>
    /// 土豪客户专用礼包建造者
    /// </summary>
    public class BiggerGiftBuilder : GiftBuilder
    {
        private readonly Gift _gift = new Gift();

        public override void SetName(string name)
        {
            _gift.Name = name;
        }

        public override void AddMoney(int number)
        {
            //土豪客户,金钱翻倍
            _gift.Money += number*2;
        }

        public override void AddEquipment(string name)
        {
            //土豪的装备必须是镶钻的
            _gift.Equipments.Add(string.Concat("土豪金", name));
        }

        public override Gift GetGift()
        {
            return _gift;
        }

        /// <summary>
        /// 买礼包要show出来
        /// </summary>
        /// <param name="name"></param>
        public void Show(string name)
        {
            Console.WriteLine("VIP客户{0}买大礼包啦!礼包所有金额翻倍,所有装备是土豪金的,VIP就是这么任性!", name);
        }
    }

知道怎么创建礼包了。但是有一个问题,上面的礼包里面的内容都是一个个添加进来了,这个不可能让玩家去做吧。所以游戏公司都会预设几个常用的礼包,这时候我们需要一个类来控制礼包的生成过程。
下面的Order类就是就是这个作用。

    public class Order
    {
        /// <summary>
        /// 礼包A
        /// </summary>
        /// <param name="builder"></param>
        /// <returns></returns>
        public Gift GetGiftA(GiftBuilder builder)
        {
            builder.SetName("礼包A");
            builder.AddMoney(50);
            builder.AddEquipment("白虹剑");
            builder.AddEquipment("鳄鱼甲");
            builder.AddEquipment("松风剑法");
            return builder.GetGift();
        }

        /// <summary>
        /// 超级大礼包
        /// </summary>
        /// <param name="builder"></param>
        /// <returns></returns>
        public Gift GetBiggerThenBiggerGift(GiftBuilder builder)
        {
            builder.SetName("土豪大礼包");
            builder.AddMoney(10000000);
            builder.AddEquipment("倚天剑");
            builder.AddEquipment("屠龙刀");
            builder.AddEquipment("打狗棒");
            builder.AddEquipment("软猬甲");
            builder.AddEquipment("降龙十八掌");
            builder.AddEquipment("九阳真经");
            builder.AddEquipment("易筋经");
            return builder.GetGift();
        }
    }

我这里只写了2个礼包,还可以加入更多的。当然,如果玩家想自定义礼包的每一项内容,也可以单独加入一个方法,根据传入的参数来定义礼包内容。
还有,如果Order的内容越来越多,而且很多东西也类似,可以继续使用其他的设计方法来重构

到这里,整个系统的构建就完成了,看下客户端如何调用。

    class Program
    {
        static void Main(string[] args)
        {
            var order = new Order();
            var builder = new GenrealGiftBuilder();
            var gift = order.GetGiftA(builder);
            gift.Show();

            Console.WriteLine();

            var biggerBuilder = new BiggerGiftBuilder();
            var biggerGift = order.GetBiggerThenBiggerGift(biggerBuilder);
            biggerBuilder.Show("张三");
            biggerGift.Show();

            Console.ReadLine();
        }
    }


程序的输出如下,VIP客户的待遇就是好呀!

 

3、总结

看看GOF对建造者模式的定义:

• Builder 抽象建造者,示例中的GiftBuilder
为创建一个Product对象的各个部件指定抽象接口
• ConcreteBuilder 具体建造者,示例中的GenrealGiftBuilder和BiggerGiftBuilder
实现Builder的接口以构造和装配该产品的各个部件
定义并明确它所创建的表示
提供一个检索产品的接口
• Director 导演类,示例中的Order
构造一个使用Builder接口的对象
• Product 产品类,示例中的Gift
表示被构造的复杂对象。ConcreteBuilder创建该产品的内部表示并定义它的装配过程
包含定义组成部件的类,包括将这些部件装配成最终产品的接口

在上面的示例中,Order类可以定义各种不同的礼包,根据是否是VIP玩家,礼包也有不同的表示。而且,同样的礼包实体,可以让玩家自己选择礼包内容,只要传递金钱、装备等参数就可以。
对照着这个示例,再回顾下Builder模式的定义及使用场景:
将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
在以下情况使用建造者模式
• 当创建复杂对象的算法应该独立于该对象的组成部分以及它们的装配方式时。
• 当构造过程必须允许被构造的对象有不同的表示时。

网上也有很多类似的示例,但是有些是这样设计的:每种礼包都设计成一个具体建造者类(同BiggerGiftBuilder),但是这样的话会造成ConcreteBuilder无限膨胀。也许礼包1--5内容都差不多,就是装备有一点差别,为这个弄太多类得不偿失,况且这些类的实现其实都是一样的。

有什么问题请大家不吝指正,谢谢!

posted @ 2014-12-10 19:17  星月无痕  阅读(238)  评论(0编辑  收藏  举报