设计模式-工厂模式及其改进

工厂模式是一种创建型模式,在很多项目中有用到。

案例:我们需要一辆汽车,通过汽车工厂拿到汽车对象。简单工厂模式做法如下

1、先定义接口:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace AllDesignPattern.FactoryPattern
{
public interface ICar
{
string EnergyCatgory { get; set; }
string Drive();
}
}

2、定义实现接口的类(比如此处电动车类,燃油车类):

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace AllDesignPattern.FactoryPattern
{
    public class ElectroCar : ICar
    {
        public string EnergyCatgory { get ; set ; }

        public string Drive()
        {
            return " Drive ElectroCar";
        }
    }
}

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace AllDesignPattern.FactoryPattern
{
    public class OilCar : ICar
    {
        public string EnergyCatgory { get; set; }

        public string Drive()
        {
            return " Drive OilCar";
        }
    }
}

3、定义生成对象的工厂

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace AllDesignPattern.FactoryPattern
{
    public class CarFactory
    {
        public ICar GetCar(string carCatgory)
        {
            switch (carCatgory)
            {
                case "ElectroCar":
                    return new ElectroCar() { EnergyCatgory = carCatgory };
                case "OilCar":
                    return new OilCar() { EnergyCatgory = carCatgory };
                default:
                    return null;
            }
        }
    }
}

如此这般一个简单工厂模式即实现了。

使用时如下:

CarFactory carFactory = new CarFactory();
            ICar car1 = carFactory.GetCar("OilCar");
            ICar car2 = carFactory.GetCar("ElectroCar");
            rctTestInfo.Clear();
            rctTestInfo.AppendText("\r\n" + DateTime.Now.ToString() + " :" + car1.Drive());
            rctTestInfo.AppendText("\r\n" + DateTime.Now.ToString() + " :" + car2.Drive());

看代码能发现简单工厂模式还是存在扩展性缺陷的,接口每增加一个实现类就需要修改工厂类增加一个case,实际项目中一般不会这样直接用。

可以通过应用反射改进该问题

应用反射后的工厂类如下:

using AllDesignPattern.FactoryPattern;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace AllDesignPattern.ReflectionFactoryPattern
{
    public class ReflectCarFactory
    {
        /// <summary>
        /// 
        /// </summary>
        /// <param name="carCatgory">参数需要传入类名加程序集完整名称</param>
        /// <param name="assemblyPath"></param>
        /// <returns></returns>
        public ICar GetCar(string carCatgory, string assemblyPath = null)
        {
            try
            {
                Assembly assembly;
                if (!string.IsNullOrEmpty(assemblyPath))
                {
                    assembly = Assembly.LoadFile(assemblyPath); // 加载程序集(EXE 或 DLL,LoadFile(string path)的参数path为程序集路径,不能是相对路径                   
                }
                else
                {
                    assembly = Assembly.GetExecutingAssembly(); // 获取当前程序集,如果类型不在当前程序集需要传入程序集的路径                   
                }
                if (assembly.GetTypes().FirstOrDefault(p => p.FullName == carCatgory) == null)
                {
                    throw new Exception($"类型{carCatgory}在程序集{assemblyPath}中不存在");
                }
                ICar theCar = (ICar)assembly.CreateInstance(carCatgory);// 创建类的实例,CreateInstance的参数typeName是类的完全限定名(即包括命名空间)
                return theCar;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

    }
}

这样改进之后,工厂需要加同接口的产品可以不用修改工厂类了,比如此时我们再添加一个太阳能汽车类,就只需要加完实现类就可以直接调用反射工厂创建了。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace AllDesignPattern.FactoryPattern
{
    public class SolarEnergyCar : ICar
    {
        public string EnergyCatgory { get; set; }

        public string Drive()
        {
            return " Drive SolarEnergyCar";
        }
    }
}

用反射工厂创建对象方式如下:

            ReflectCarFactory reflectCarFactory = new ReflectCarFactory();
            ICar car5 = reflectCarFactory.GetCar("AllDesignPattern.FactoryPattern.ElectroCar");
            ICar car6 = reflectCarFactory.GetCar("AllDesignPattern.FactoryPattern.SolarEnergyCar");
            rctTestInfo.AppendText("\r\n" + DateTime.Now.ToString() + " 反射+工厂模式 :" + car5.Drive());
            rctTestInfo.AppendText("\r\n" + DateTime.Now.ToString() + " 反射+工厂模式 :" + car6.Drive());

 

反射+工厂模式解决了,创建同一个接口实现类的对象时可以只需要写接口实现类就可以了。如果要创建另外一个接口的实现类还得重新定义一个工厂,基于此可进一步改进,应用泛型把该工厂变成泛型工厂即可用一个工厂创建多种接口实现类的对象。

如下由定义了一个颜色接口

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace AllDesignPattern.AbstractFactoryPattern
{
    public interface IColor
    {
        string Show();
    }
}

并定义了两种颜色

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace AllDesignPattern.FactoryPattern
{
    public class Green : IColor
    {
        public string Show()
        {
            return "this is Green";
        }
    }

    public class Red : IColor
    {
        public string Show()
        {
            return "this is red";
        }
    }
        
}

如果使用简单工厂模式这时又需要定义一个颜色工厂,当然也可以使用抽象工厂模式,但是抽象工厂模式每新增加一个接口也是需要通过继承抽象工厂定义一个新工厂的。

在此我们也可以对原来的反射汽车工厂类进行改进加入泛型即可用来创建颜色,改进后如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Reflection;

namespace AllDesignPattern.ReflectionFactoryPattern
{
    public class GenerosityFactory
    {

        /// <summary>
        /// 
        /// </summary>
        /// <param name="Catgory">参数需要传入类名加程序集完整名称</param>
        /// <param name="assemblyPath"></param>
        /// <returns></returns>
        public T GetInstance<T>(string catgory, string assemblyPath = null)
        {
            try
            {
                Assembly assembly;
                if (!string.IsNullOrEmpty(assemblyPath))
                {
                    assembly = Assembly.LoadFile(assemblyPath); // 加载程序集(EXE 或 DLL,LoadFile(string path)的参数path为程序集路径,不能是相对路径                   
                }
                else
                {
                    assembly = Assembly.GetExecutingAssembly(); // 获取当前程序集,如果类型不在当前程序集需要传入类型所在的程序集路径                   
                }
                if (assembly.GetTypes().FirstOrDefault(p => p.FullName == catgory) == null)
                {
                    throw new Exception($"类型{Catgory}在程序集{assemblyPath}中不存在");
                }
                T theCar = (T)assembly.CreateInstance(catgory);// 创建类的实例,CreateInstance的参数typeName是类的完全限定名(即包括命名空间)
                return theCar;
            }
            catch (Exception ex)
            {
                throw ex;
            }

        }


    }
}

改进后可以使用这一个泛型反射工厂创建多种接口实现类的对象。

使用如下:

GenerosityFactory generosityFactory = new GenerosityFactory();
string genMsg = "\r\n" + DateTime.Now.ToString() + " 反射 + 工厂模式 + 泛型 : ";
ICar car7 = generosityFactory.GetInstance<ICar>("AllDesignPattern.FactoryPattern.ElectroCar");
rctTestInfo.AppendText(genMsg + car7.Drive());
ICar car8 = generosityFactory.GetInstance<ICar>("AllDesignPattern.FactoryPattern.OilCar");
rctTestInfo.AppendText(genMsg+ car8.Drive());
IColor color3 = generosityFactory.GetInstance<IColor>("AllDesignPattern.AbstractFactoryPattern.Green");
rctTestInfo.AppendText(genMsg + color3.Show());
IColor color4 = generosityFactory.GetInstance<IColor>("AllDesignPattern.AbstractFactoryPattern.Red");
rctTestInfo.AppendText(genMsg + color4.Show());

 

小结:

案例中的工厂创建对象时只是调用默认构造函数简单的创建,实际项目中不同的对象初始化时并不一定都是调用默认构造函数,因而还需要结合实际情况对工厂类进行改造,进一步改进工厂在GetInstance方法中加一个XML或者JSON文件路径字符串,方法中读取此XML或者JSON文件,依据读取到的配置信息来初始化对象可实现依赖注入。

 

posted on 2020-12-11 13:16  CodeTreker  阅读(187)  评论(0编辑  收藏  举报

导航