抽象工厂模式【C#】

C#

浏览数:180

2019-8-16

示例代码为了尽可能突显设计模式的特征,采用了极简代码。尽量避免其他代码对理解设计模式产生干扰

定义:

抽象工厂模式(Abstract Factory Pattern):提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们具体的类。
简而言之,就是抽象工厂模式,可以使具体工厂同时具备创建多种产品的能力。比如接下来的示例代码中,电脑工厂具备生产显示器能力,同时还具备生产主机的能力。

结构导图(引自刘伟老师的博客

代码:

产品代码

  • 产品-显示器

    /// <summary>
    /// 抽象产品
    /// 抽象显示器
    /// </summary>
    interface IDisplay
    {
        void Show();
    }
    /// <summary>
    /// 具体产品
    /// 苹果显示器
    /// </summary>
    class AppleDisplay : IDisplay
    {
        public void Show()
        {
            Console.WriteLine("苹果显示器正常工作。");
        }
    }
    /// <summary>
    /// 具体产品
    /// 戴尔显示器
    /// </summary>
    class DellDisplay : IDisplay
    {
        public void Show()
        {
            Console.WriteLine("戴尔显示器正常工作。");
        }
    }
  • 产品-主机

    /// <summary>
    /// 抽象产品
    /// 抽象主机
    /// </summary>
    interface IMainframe
    {
        void Open();
    }
    /// <summary>
    /// 具体产品
    /// 苹果主机
    /// </summary>
    class AppleMainframe : IMainframe
    {
        public void Open()
        {
            Console.WriteLine("苹果主机成功开机。");
        }
    }
    /// <summary>
    /// 具体产品
    /// 戴尔主机
    /// </summary>
    class DellMainframe : IMainframe
    {
        public void Open()
        {
            Console.WriteLine("戴尔主机成功开机。");
        }
    }

如果使用普通工厂模式,那么每种产品,都应该存在其对应的工厂类,这样就会导致该情况下,要同时提供“显示器工厂类”以及“主机工厂类”,后期添加了键盘,鼠标等产品后,还有再提供更多的对应的工厂类。这无疑会导致项目中的工厂数量过多,一定程度上会增加项目的复杂度。

此时,抽象工厂模式就派上用场了!

工厂代码

    /// <summary>
    /// 抽象工厂
    /// 电脑工厂
    /// </summary>
    interface IComputerFactory
    {
        /// <summary>
        /// 生产一个显示器
        /// </summary>
        IDisplay ProduceADisplay();
    
        /// <summary>
        /// 生产一个主机
        /// </summary>
        IMainframe ProduceAMainframe();
    }
    /// <summary>
    /// 具体工厂
    /// 苹果电脑工厂
    /// </summary>
    class AppleComputerFactory : IComputerFactory
    {
        public IDisplay ProduceADisplay()
        {
            return new AppleDisplay();
        }
    
        public IMainframe ProduceAMainframe()
        {
            return new AppleMainframe();
        }
    }
    /// <summary>
    /// 具体工厂
    /// 戴尔电脑工厂
    /// </summary>
    class DellComputerFactory : IComputerFactory
    {
        public IDisplay ProduceADisplay()
        {
            return new DellDisplay();
        }
    
        public IMainframe ProduceAMainframe()
        {
            return new DellMainframe();
        }
    }

此时,每个电脑厂家,都有其独立的工厂。客户想要一台电脑,只需要找指定厂家定制就足够了。当然,实际上,大家买电脑一般不会直接找厂家,此处只是大概比喻。:P

请看客户端代码

    class Program
    {
        static void Main(string[] args)
        {
            // 假设此时我想要一台苹果电脑
            // 创建苹果电脑工厂
            var factory = new AppleComputerFactory();
    
            // 生产出显示器并测试产品是否合格
            var display = factory.ProduceADisplay();
            display.Show();
    
            // 生产出主机并测试产品是否合格
            var mainframe = factory.ProduceAMainframe();
            mainframe.Open();
    
            // 完事
            Console.WriteLine("电脑生产完成,正在配送···");
        }
    }

以上。

运行后的输出如下:

补充讨论:

很多人说,后期如果拓展某个具体产品(比如现在只有苹果和戴尔两个品牌的电脑,后期可能添加华为显示器和华为主机),很简单,最多添加个具体工厂足矣。但是如果添加了某种产品(比如现在只有显示器和主机,后期可能添加键盘等外设),那么扩展起来将非常困难,因为想要扩展就需要改动现有抽象工厂。
而一旦对现有抽象工厂进行了改动(比如添加了生产键盘的方法),那么所有继承它的那些具体工厂,都需要同步改动(哪怕有些工厂从始至终也没打算卖键盘,但它一样需要跟着抽象工厂一起被改造),这样算起来,改动并不小。

但其实要我说的话,我不一定非要改动现有的抽象工厂。我可以添加新的抽象工厂。

代码如下:

    /// <summary>
    /// 抽象工厂
    /// 附赠键盘的电脑工厂
    /// </summary>
    abstract class IComputerFactory_Keyboard : IComputerFactory
    {
        /// <summary>
        /// 持有旧工厂的引用
        /// </summary>
        protected abstract IComputerFactory OldComputerFactory { get; }

        /// <summary>
        /// 通过旧工厂生产显示器
        /// </summary>
        public IDisplay ProduceADisplay()
        {
            return OldComputerFactory.ProduceADisplay();
        }

        /// <summary>
        /// 生产一个键盘
        /// </summary>
        public abstract IKeyboard ProduceAKeyboard();

        /// <summary>
        /// 通过旧工厂生产主机
        /// </summary>
        public IMainframe ProduceAMainframe()
        {
            return OldComputerFactory.ProduceAMainframe();
        }
    }

对应添加一个新的附赠键盘的工厂

    /// <summary>
    /// 具体工厂
    /// 戴尔新工厂-买电脑送键盘
    /// </summary>
    class DellComputerFactory_Keyboard : IComputerFactory_Keyboard
    {
        private readonly DellComputerFactory ComputerFactory = new DellComputerFactory();
        protected override IComputerFactory OldComputerFactory
        {
            get
            {
                return ComputerFactory;
            }
        }

        public override IKeyboard ProduceAKeyboard()
        {
            return new DellKeyboard();
        }
    }

如此这般,即可在不改动原工厂的情况下,使指定厂商具备生产键盘的能力了。
PS:C#语言,还可以通过扩展方法的方式,为指定工厂添加一些额外的生产能力。

作者:迷鹿