之前我们接介绍了简单工厂,这次我们介绍一种更为常用的模式——工厂模式。
工厂方法模式Factory Method,又称多态性工厂模式。在工厂方法模式中,核心的工厂类不再负责所有的产品的创建,而是将具体创建的工作交给子类去做。该核心类成为一个抽象工厂角色,仅负责给出具体工厂子类必须实现的接口,而不接触哪一个产品类应当被实例化这种细节。
工厂方法模式是简单工厂模式的衍生,解决了许多简单工厂模式的问题。首先完全实现‘开-闭 原则’,实现了可扩展。其次更复杂的层次结构,可以应用于产品结果复杂的场合。
开闭原则(OCP):)是面向对象设计中“可复用设计”的基石,是面向对象设计中最重要的原则之一,其它很多的设计原则都是实现开闭原则的一种手段。对于扩展是开放的,对于修改是关闭的,这意味着模块的行为是可以扩展的。当应用的需求改变时,我们可以对模块进行扩展,使其具有满足那些改变的新行为。也就是说,我们可以改变模块的功能。对模块行为进行扩展时,不必改动模块的源代码或者二进制代码。模块的二进制可执行版本,无论是可链接的库、DLL或者.EXE文件,都无需改动。实现开闭原则的关键就在于“抽象”。把系统的所有可能的行为抽象成一个抽象底层,这个抽象底层规定出所有的具体实现必须提供的方法的特征。作为系统设计的抽象层,要预见所有可能的扩展,从而使得在任何扩展情况下,系统的抽象底层不需修改;同时,由于可以从抽象底层导出一个或多个新的具体实现,可以改变系统的行为,因此系统设计对扩展是开放的。
之前简单工厂的核心工厂就是根据需求不断制造类的实例,现在我们将这个行为抽象化,就会达到多种效果:单一化,模块化,拓展性强,便于修改。主程序的代码可以不用动,只需要增加类就行了(实现某种功能的类,往往继承于一个接口)。下面看代码:
public interface IFactory { IPerson GetSay(); } public interface IPerson { void Say(); } public class Student : IPerson { public void Say() { Console.WriteLine("我是学生...."); } } public class Teacher : IPerson { public void Say() { Console.WriteLine("我是老师...."); } } public class StudentFactory : IFactory { public IPerson GetSay() { return new Student(); } } public class TeacherFactory : IFactory { public IPerson GetSay() { return new Teacher(); } }
上面我们把最重要的两个东西都抽象画了——工厂和产品。产品IPerson我们就不说了还是遵循简单工厂。最大变化是这里的工厂IFactory不再直接参与生产,而是把这项具体工作将给了子类StudentFactory、TeachFactory,让他们去生产类的实例。将这个生产动作抽象化,完美的实现了开闭原则。下面是主程序调用:
<appSettings> <add key="AssemblyPath" value="ConsoleApp2"/> <add key="NameSpace" value="ConsoleApp2"/> <add key="StudentFactory" value="StudentFactory"/> </appSettings>
private static readonly string assemblyName = ConfigurationManager.AppSettings["AssemblyPath"];//程序集 private static readonly string nameSpace = ConfigurationManager.AppSettings["NameSpace"];//命名空间 private static readonly string className = ConfigurationManager.AppSettings["StudentFactory"];//类名 public static object CreateInstance(string assemblyName, string nameSpace, string className) { try { string fullName = nameSpace + "." + className;//命名空间.类型名 object ect = Assembly.Load(assemblyName).CreateInstance(fullName);//加载程序集,创建程序集里面的 命名空间.类型名 实例 return ect;//类型转换并返回 } catch (Exception ex) { Console.WriteLine(ex.Message + "===" + ex.StackTrace); return false; } } static void Main(string[] args) { IFactory ifc = CreateInstance(assemblyName, nameSpace, className) as IFactory; IPerson ip = ifc.GetSay(); ip.Say(); Console.ReadKey(); }
这里有个技巧就是利用之前介绍过的反射,修改配置文件就可以创建类的实例。这里我们通过反射创建了StudentFactory的实例,然后利用类的多态特性,调用GetSay方法。得到继承于接口IPerson的子类实例,然后又利用类的多态特性,调用Say方法,最终完成。主程序中两次抽象,两次创建了类的实例,最开始还用到了反射,从头至尾,目的就是为了解耦,达到开闭的原则,便于以后的功能拓展。