factory pattern demo in C#
Switch branches/tags
Nothing to show
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Failed to load latest commit information.
factorypatterndemo
README.md
factorypatterndemo.sln
factorypatterndemo.userprefs

README.md

昨天写代码的时候发现大多代码都一样,只有类型不一样,当时脑海里就冒出“工厂模式”的概念,但又说不清到底什么是工厂模式,我所遇到的情况又到底适不适合使用工厂模式,于是花时间好好把工厂模式看了一下,想通过这篇文章来输出我所看到和get到的内容。

我比较喜欢旅游,就拿旅游产品来举例子吧。

简单工厂

首先抽象一个旅游产品接口,该接口有一个获取线路列表的行为:

    /// <summary>
    /// 旅游产品接口
    /// </summary>
    public interface ITravelProduct
    {
        /// <summary>
        /// 获取旅游产品线路列表
        /// </summary>
        /// <returns>The line list.</returns>
        string GetLineList();
    }

旅游产品有很多种,比如国内游(具体的产品类):

    /// <summary>
    /// 国内游
    /// </summary>
    public class DomesticTravel:ITravelProduct
    {
        public string GetLineList() => "您获取到国内游的线路列表啦!";
    }

再比如出境游(具体的产品类):

    /// <summary>
    /// 出境游
    /// </summary>
    public class OutboundTravel:ITravelProduct
    {
        public string GetLineList() => "您获取到出境游的线路列表啦!";
    }

再比如邮轮游(具体的产品类):

    /// <summary>
    /// 邮轮游
    /// </summary>
    public class CruiseTravel:ITravelProduct
    {
        public string GetLineList() => "您获取到邮轮游的线路列表啦!";
    }

然后我们来到一家OTA,看到网站上提供了如下几种旅游产品的线路列表(简单工厂类):

    /// <summary>
    /// 简单工厂模式的工厂类
    /// </summary>
    public class SimpleTravelProductFactory
    {
        public const int Domestic = 1;//国内游
        public const int Outbond = 2;//出境游
        public const int Cruise = 3;//邮轮

        public static ITravelProduct createTravelProduct(int type){
            switch(type){
                case Domestic:
                    return new DomesticTravel();
                case Outbond:
                    return new OutboundTravel();
                case Cruise:
                    return new CruiseTravel();
                default:
                    return new DomesticTravel();

            }
        }
    }

下面就看你想选择哪种旅游产品啦,比如你想选个国内游的线路列表看看,那么,我们就让简单工厂给我们返回一下国内游的线路列表:

        public static void Main(string[] args)
        {
            Console.WriteLine("=========简单工厂模式==========");
            string domesticLineList = SimpleTravelProductFactory.createTravelProduct(SimpleTravelProductFactory.Domestic).GetLineList();
            Console.WriteLine(domesticLineList);
        }

输出结果:

=========简单工厂模式==========
您获取到国内游的线路列表啦!

如果你想获取出境游的线路列表活着邮轮游的线路列表,只需要在创建旅游产品时传入对应的产品类型即可。 显而易见,简单工厂的缺点就是,如果有很多很多种产品类型,那么你工厂类中的switch,case语句会很长,而且在做产品的扩展时,除了要增加具体的产品类外,还需要修改工厂类(往工厂类中增加case语句呀),这种情况下,你的产品和你的工厂还是有较强的耦合的,要解决这个问题,有两个方法: 一是利用反射实现简单工厂模式;二是使用工厂模式。 在这之前,我们先来看看简单工厂模式中工厂类的另一种写法:

    /// <summary>
    /// 简单工厂模式工厂类的另一种写法
    /// </summary>
    public class MulWayTravelProductFactory
    {
        /// <summary>
        /// 直接指定创建的产品
        /// </summary>
        /// <returns>The domestic travel product.</returns>
        public static ITravelProduct createDomesticTravelProduct()
        {
            return new DomesticTravel();
        }

        public static ITravelProduct createOutboundTravelProduct()
        {
            return new OutboundTravel();
        }

        public static ITravelProduct createCruiseTravelProduct()
        {
            return new CruiseTravel();
        }
    }

此时,查看国内游线路列表的写法如下:

Console.WriteLine("=========简单工厂模式工厂类的另一种写法==========");
string domesticLineList = MulWayTravelProductFactory.createDomesticTravelProduct().GetLineList();
Console.WriteLine(domesticLineList);

输出结果:

=========简单工厂模式工厂类的另一种写法==========
您获取到国内游的线路列表啦!

这种方法虽然摆脱了switch,case语句,但依然摆脱不了具体的产品类和工厂类的耦合。所以,下面,我们就来看看利用反射实现的简单工厂模式吧!

利用反射实现的简单工厂模式

我们只需把工厂类生产产品的方式改成反射的方式即可。这里主要是用到了反射中,根据类名获取类实例的知识点,直接看代码吧:

    /// <summary>
    /// 利用反射实现的简单工厂模式
    /// </summary>
    public class ReflectTravelProductFactory
    {
        /// <summary>
        /// Creates the travel product.
        /// </summary>
        /// <returns>The travel product.</returns>
        /// <param name="fullname">类名(包括命名空间的类名)</param>
        /// <typeparam name="T">The 1st type parameter.</typeparam>
        public static T createTravelProduct<T>(string fullname)
        {
            Assembly assenbly = Assembly.GetExecutingAssembly();
            return (T)assenbly.CreateInstance(fullname);
        }
    }

需要注意两点:

  • 参数fullname就是类名,但是完全限定名,也就是说要包括命名空间的
  • CreateInstance创建的类实例其实是object类型的,需要强制转换一下

此时,查看国内游线路列表的写法为:

Console.WriteLine("=========利用反射实现的简单工厂模式==========");
string domesticLineList = ReflectTravelProductFactory.createTravelProduct<DomesticTravel>("factorypatterndemo.TravelProduct.DomesticTravel").GetLineList();
Console.WriteLine(domesticLineList);

输出结果:

=========利用反射实现的简单工厂模式==========
您获取到国内游的线路列表啦!

此时,具体的产品类跟工厂类已经完全解耦了。也就是说,如果需要做产品的扩展,只需要增加具体的产品类,无需修改工厂类了。 但这个方式唯一的缺点(准确点讲是问题吧)就是反射的性能有待商榷。

工厂模式

上面所有方式的做法就是在一个工厂里生产了所有的产品,工厂模式的做法是抽象出一个工厂接口,然后不同的产品在不同的工厂中进行生产。 (1)抽象出一个工厂接口:

    /// <summary>
    /// 抽象工厂接口
    /// </summary>
    public interface ITravelProductFactory
    {
        ITravelProduct getTravelProductType();//该接口有一个获取旅游产品类型的行为
    }

(2)不同的产品建立不同的工厂 国内游产品的工厂:

    /// <summary>
    /// 生产国内游产品的工厂
    /// </summary>
    public class DomesticTravelFactory:ITravelProductFactory
    {
        public ITravelProduct getTravelProductType()
        {
            return new DomesticTravel();
        }
    }

出境游产品的工厂:

    /// <summary>
    /// 生产出境游产品的工厂
    /// </summary>
    public class OutboundTravelFactory:ITravelProductFactory
    {
        public ITravelProduct getTravelProductType()
        {
            return new OutboundTravel();
        }
    }

邮轮游产品的工厂:

    /// <summary>
    /// 生产邮轮游产品的工厂
    /// </summary>
    public class CruiseTravelFactory:ITravelProductFactory
    {
        public ITravelProduct getTravelProductType()
        {
            return new CruiseTravel();
        }
    }

此时,查看国内游线路列表的写法如下:

Console.WriteLine("=========工厂模式==========");
ITravelProductFactory factory = new DomesticTravelFactory();
ITravelProduct domesticTravel= factory.getTravelProductType();
string domesticLineList = domesticTravel.GetLineList();
Console.WriteLine(domesticLineList);

输出结果:

=========工厂模式==========
您获取到国内游的线路列表啦!

如果要�扩展�产品,只需要增加对应的产品类和产品工厂即可。

大家知道,OTA不止一家,每家都有国内游、出境游、邮轮游,此时抽象工厂模式即将登场。

抽象工厂

(1)分别建立国内游、出境游和邮轮游的抽象接口,并继承一开始定义的ITravelProduct接口,这样就可以共用ITravelProduct接口中定义的行为了。代码如下:

    public interface IDomesticTravel:ITravelProduct
    {
    }
    public interface IOutboundTravel:ITravelProduct
    {
    }
    public interface ICruiseTravel:ITravelProduct
    {
    }

(2)定义一个OTA抽象工厂,该工厂可以生产国内游、出境游和邮轮游三种产品:

    public interface ITravelFactory
    {
        IDomesticTravel createDomesticTravel();
        IOutboundTravel createOutboundTravel();
        ICruiseTravel createCruiseTravel();
    }

(3)定义一家名叫XC的OTA工厂,该工厂继承ITravelFactory

    public class XC_Factory:ITravelFactory
    {
        public IDomesticTravel createDomesticTravel()
        {
            return new XC_DomesticTravel();
        }

        public IOutboundTravel createOutboundTravel()
        {
            return new XC_OutboundTravel();
        }

        public ICruiseTravel createCruiseTravel()
        {
            return new XC_CruiseTravel();
        }
    }

再定义一家名叫TC的OTA工厂:

    public class TC_Factory : ITravelFactory
    {
        public ICruiseTravel createCruiseTravel()
        {
            return new TC_CruiseTravel();
        }

        public IDomesticTravel createDomesticTravel()
        {
            return new TC_DomesticTravel();
        }

        public IOutboundTravel createOutboundTravel()
        {
            return new TC_OutboundTravel();
        }
    }

现在,我们想要获取两家OTA的所有国内游线路列表:

Console.WriteLine("=========抽象工厂模式==========");
ITravelFactory tc_factory = new TC_Factory();
ITravelFactory xc_factory = new XC_Factory();
IDomesticTravel tc_domesticTravel = tc_factory.createDomesticTravel();
IDomesticTravel xc_domesticTravel = xc_factory.createDomesticTravel();
string tc_domesticLineList = tc_domesticTravel.GetLineList();
string xc_domesticLineList = xc_domesticTravel.GetLineList();
Console.WriteLine(tc_domesticLineList);
Console.WriteLine(xc_domesticLineList);

输出结果:

=========抽象工厂模式==========
您获取到TC的国内游线路列表啦!
您获取到XC的国内游线路列表啦!

好啦,到这里,工厂模式基本讲结束了。总结一下:

  • 工厂模式有三种:简单工厂、工厂和抽象工厂
  • 简单工厂模式中,产品类和工厂类之间存在耦合
  • 工厂模式只能解决单一商家多个产品的问题
  • 抽象工厂模式可以解决多个商家多个产品的问题

Q&A环节 Q:上面的代码中都是使用接口来表示抽象产品或者抽象工厂,可以改成用抽象类吗? (这个问题翻译一下,其实就是抽象类和接口的区别) A:就本实例中的接口而言,完全可以使用抽象类替换。但不是说抽象类和接口就是等价的,不然为什么会同时存在呢?存在即合理嘛,一般对象的抽象偏向使用抽象类,行为的抽象偏向使用接口。抽象类中除了有无需实现的抽象方法,也可以有具体实现的方法;而接口中只能有无实现的方法。一个类只能继承一个父类,但可以继承多个接口,所以我们在使用接口的时候有更好的扩展性,这大概就是编程原则中有一条是针对接口编程,而不是针对类编程吧。

源码地址:

差不多就这些了。

说些题外话,最近看到身边的同事小伙伴们在自己坚持不懈地努力下,慢慢成长和蜕变成更加优秀的样子,有时候我常常想,如果我一开始认识此刻的TA,大概都不敢和TA交朋友吧。没有谁天生就是优秀的,每个优秀的人背后都付出了旁人难以想象和无法看到的努力和坚持。也许我还是对自己要求太低了,由于种种原因放松了对自我成长的要求,看着自己与伙伴越来越大的差距,既羞愧又紧张,也有不少压力,也许是我还没放弃自己吧,如果要自己立刻和如此优秀的他们比,也许我连重新上路的勇气都没有,所以就和昨天的自己比吧。其实这篇文章昨天就能完成的,但我还是拖延到今天才完成,不过好在今天完成了,说明比昨天的我优秀了一点点,哈哈,只能这么安慰自己了,加油吧!