多态工厂(工厂方法模式)

上一篇文章我们实现了简单工厂模式,简单工厂模式虽然简单,但存在一个很严重的问题:当系统中需要引入新产品时,由于静态工厂方法通过传入参数的不同来创建不同的产品,这必定要修改工厂类的源代码,将违背开闭原则。如何实现增加新产品而不影响已有代码?工厂方法模式为此应运而生。本篇将介绍一下第2种工厂模式---工厂方法模式。

在工厂方法模式中,不再提供一个统一的工厂类来创建所有的产品对象,而是针对不同的产品提供不同的工厂,系统提供一个与产品等级结构对应的工厂等级结构。

首先我们看一结构图:

从图中可以看出,在工厂方法模式结构图中包含4个角色。

1、Product (抽象产品)

2、ConcreteProduct(具体产品)

3、Factory(抽象工厂)

4、ConcreteFactory(具体工厂)

代码实现:

定义抽象工厂:

    //抽象工厂
    public interface ILoggerFactory
    {
        public ILogger CreateLogger();
    }

实现file logger:

     public class FileLoggerFactory : ILoggerFactory
     {
        //实现FileLogger
        public ILogger CreateLogger()
        {
            ILogger logger = new FileLogger();
            return logger;
        }
     }

实现database logger:

    public class DatabaseLoggerFactory : ILoggerFactory
    {
        //实现DatabaseLogger
        public ILogger CreateLogger()
        {
            ILogger logger = new DatabaseLogger();
            return logger;
        }
    }

定义抽象产品:

    public interface ILogger
    {
        public void WriteLog();
    }

实现具体产品的file log:

    public class FileLogger : ILogger
    {
        public void WriteLog()
        {
            Console.WriteLine("file log");
        }
    }

实现具体产品的db log:

    public class DatabaseLogger : ILogger
    {
        public void WriteLog()
        {
            Console.WriteLine("db log");
        }
    }

最后我们用console app 来打印一下结果:

    class Program
    {
        static void Main(string[] args)
        {
            //选择使用哪个工厂
            ILoggerFactory loggerFactory = new FileLoggerFactory();
            //工厂不同产品也就不同,这里不关心产品的具体实现
            ILogger logger = loggerFactory.CreateLogger();
            //输出产品
            logger.WriteLog();
        }
    }

结果如下:

 大多数时候我们习惯用config去决定代码的功能,这样才够灵活。

所以我们配置一下config:

<?xml version="1.0" encoding="utf-8" ?>

<config>
	<chartType>DatabaseLoggerFactory</chartType>
</config>

写一个静态方法读取xml config:

        public static string GetConfig()
        {
            XmlDocument doc = new XmlDocument();
            doc.Load(@"Config.xml");
            XmlNode root = doc.SelectSingleNode("config");
            return root.FirstChild.InnerText;
        }

我们拿到的sring,接下来要用这个string 来确定去实例化哪个类:(这里用到了反射)

        /// <summary>
        /// 创建对象实例
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="fullName">命名空间.类型名</param>
        /// <param name="assemblyName">程序集</param>
        /// <returns></returns>
        public static T CreateInstance<T>(string fullName, string assemblyName)
        {
            string path = fullName + "," + assemblyName;//命名空间.类型名,程序集
            Type o = Type.GetType(path);//加载类型
            object obj = Activator.CreateInstance(o, true);//根据类型创建实例
            return (T)obj;//类型转换并返回
        }

然后在app 里就可以写成这样了:

    class Program
    {
        static void Main(string[] args)
        {

            //通过反射来拿到实例
            ILoggerFactory loggerFactory = CreateInstance<ILoggerFactory>            
            ("PolymorphicFactoryPattern." + GetConfig(), "PolymorphicFactoryPattern");

            ILogger logger = loggerFactory.CreateLogger();
            logger.WriteLog();
        }
    }

结果如下:我们拿到了db log 的输出,说明code work。

  这里调用抽象工厂的代码可以隐藏起来,封装到抽象工厂里面:

    public interface ILoggerFactory
    {
        public ILogger CreateLogger();
        
        //这样抽象工厂调用抽象产品的代码就被隐藏了
        public void WriteLog()
        {
            ILogger logger = this.CreateLogger();
            logger.WriteLog();
        }
        
    }

 而app 运行代码也变得更简单了:

        static void Main(string[] args)
        {

            //接口
            ILoggerFactory loggerFactory = CreateInstance<ILoggerFactory>("PolymorphicFactoryPattern." + GetConfig(), "PolymorphicFactoryPattern");
             
            loggerFactory.WriteLog();
        }

总结:

工厂方法模式是简单工厂模式的延伸,它继承了简单工厂模式的优点,同时还弥补了简单工厂不足。工厂方法模式是使用频率最高的设计模式之一,是很多开源框架和API类库的核心模式。

优点:

1、工厂方法用来创建客户所需要的产品,同时还向客户隐藏了哪种具体产品类将被实例化这一细节,用户只需要关心所需产品对应的工厂,无须关心创建细节,甚至无须知道具体产品类的类名。

2、它能让工厂可以自主确定创建何种产品对象,而如果创建这个对角的细节完全封装在具体工厂内部。工厂方法模式之所以又被称为多态工厂模式,正因为所有具体工厂都具有同一抽象父类。

3、在系统中加入新产品时,无须修改抽象工厂和抽象产品提供的接口,无须修改客户端,也无须修改其它具体产品和具体工厂。而只要添加一个具体工厂和具体厂品即可。这样符合开闭原则。

缺点:

1、扩展时需要成对增加具体工厂和具体产品,类比较多增加系统的额外开销。

2、系统要用到多态,反射、dom 等技术增加了实现难度。

适用场景:

1、客户端不知道其所需要的对象的类。客户端不需要知道具体产品类的类名,只需要知道对应的工厂即可,具体的产品对象由具体工厂类创建,可将具体工厂类的类名存储配置文件或db中。

2、抽象工厂类通其子类来指定创建哪个对象。一个抽象工厂类只需要提供一个创建产品的接口,而由其子类来确定具体要创建的对象,对用面向对象的多态性和里氏代换原则,在程序运行时,子类对象将覆盖父类对象,从而使得系统更容易扩展。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值