C#回顾 - 7.如何使用反射实现工厂模式?
2016-11-28 15:44
696 查看
工厂模式是一种比较常用的设计模式,其基本思想在于使用不同的工厂类型来打造不同产品的部件。例如,我们在打造一间屋子时,可能需要窗户、屋顶、门、房梁、柱子等零部件。有的屋子需要很多根柱子,而有的屋子又不需要窗户。在这样的需求下,就可以使用工厂模式。
上图的设计思路是:
①使用者告诉工厂管理者需要哪个产品部件;
②工厂管理者分析使用者传入的信息,生成合适的实现工厂接口的类型对象;
③通过工厂生产出相应的产品,返回给使用者一个实现了该产品接口的类型对象;
通过上述思路,实现代码如下:
①首先是定义工厂接口,产品接口与产品类型的枚举
②其次是具体实现产品接口的产品类:窗户、屋顶和柱子
③然后是具体实现工厂接口的工厂类:实现接口返回一个具体的产品对象
④最后是工厂管理类:组织起众多的产品与工厂
按照国际惯例,我们实现一个入口方法来测试一下:
当一个新的产品—地板需要被添加时,我们需要改的地方是:添加零件枚举记录、添加针对地板的工厂类、添加新地板产品类,修改工厂管理类(在switch中添加一条case语句),这样设计的优点在于无论添加何种零件,产品使用者都不需要关心内部的变动,可以一如既往地使用工厂管理类来得到希望的零件,而缺点也有以下几点: ①工厂管理类和工厂类族耦合;
②每次添加新的零件都需要添加一对工厂类和产品类,类型会越来越多;
①产品、枚举和以上一致,这里的改变主要在于添加了两个自定义的特性,这两个特性会被分别附加在产品类型和产品接口上:
②下面是产品接口和产品类族的定义,其中产品接口使用了 ProductListAttribute 特性,而每个产品都使用了 ProductAttribute 特性:
③下面是修改后的工厂类,由于使用了反射特性,这里一个工厂类型就可以生产所有的产品:
④最后时修改后的工厂管理类,核心只有三行代码:
上述代码中最主要的变化在于两点:其一是工厂管理类不再需要根据不同的零件寻找不同的工厂,因为只有一个工厂负责处理所有的产品零件;其二是产品类型和产品接口应用了两个自定义特性,来方便工厂进行反射。 ProductAttribute 附加在产品类上,标注了当前类型代表了哪个产品零件。而 ProductListAttribute 则附加在产品接口之上,方便反射得知一共有多少产品零件。这时需要添加一个新的地板产品零件类型时,我们需要做的是:1.添加零件枚举记录/// <summary>
/// 屋子产品的零件
/// </summary>
public enum RoomParts
{
Roof,
Window,
Pillar,
Floor //地板
}
2.添加代表地板的类型(Product.cs)/// <summary>
/// 地板
/// </summary>
[Product(RoomParts.Floor)]
public class Floor : IProduct
{
// 实现接口,返回产品名字
public string GetName()
{
return "地板";
}
}
3.修改添加在IProduct上的属性初始化参数(增加地板类型), /// <summary>
/// 产品接口
/// </summary>
[ProductList(new Type[] { typeof(Roof), typeof(Window), typeof(Pillar), typeof(Floor) })]
public interface IProduct
{
string GetName();
}
可以看到这时调用者、工厂管理类和工厂都不再需要对新添加的零件进行改动,程序只需要添加必要的类型和枚举记录即可。当然,这样的设计也存在一定缺陷:反射的运行效率相对较低,在产品零件相对较多时,每生产一个产品就需要反射遍历这是一件相当耗时的工作。
作者:周旭龙
出处:http://www.cnblogs.com/edisonchou/p/4827578.html
来自为知笔记(Wiz)
(1)工厂模式的传统实现和其弊端
下图展示了针对屋子设计的传统工厂模式架构图:上图的设计思路是:
①使用者告诉工厂管理者需要哪个产品部件;
②工厂管理者分析使用者传入的信息,生成合适的实现工厂接口的类型对象;
③通过工厂生产出相应的产品,返回给使用者一个实现了该产品接口的类型对象;
通过上述思路,实现代码如下:
①首先是定义工厂接口,产品接口与产品类型的枚举
IFactory.cs(工厂接口,产品接口一起 ) /// <summary> /// 屋子产品的零件 /// </summary> public enum RoomParts { Roof, Window, Pillar } /// <summary> /// 工厂接口 /// </summary> public interface IFactory { IProduct Produce(); } /// <summary> /// 产品接口 /// </summary> public interface IProduct { string GetName(); } |
Product.cs /// <summary> /// 屋顶 /// </summary> public class Roof : IProduct { // 实现接口,返回产品名字 public string GetName() { return "屋顶"; } } /// <summary> /// 窗户 /// </summary> public class Window : IProduct { // 实现接口,返回产品名字 public string GetName() { return "窗户"; } } /// <summary> /// 柱子 /// </summary> public class Pillar : IProduct { // 实现接口,返回产品名字 public string GetName() { return "柱子"; } } |
Factory.cs /// <summary> /// 屋顶工厂 /// </summary> public class RoofFactory : IFactory { // 实现接口,返回一个产品对象 public IProduct Produce() { return new Roof(); } } /// <summary> /// 窗户工厂 /// </summary> public class WindowFactory : IFactory { // 实现接口,返回一个产品对象 public IProduct Produce() { return new Window(); } } /// <summary> /// 柱子工厂 /// </summary> public class PillarFactory : IFactory { // 实现接口,返回一个产品对象 public IProduct Produce() { return new Pillar(); } } |
FactoryManager.cs class FactoryManager { public static IProduct GetProduct(RoomParts part) { IFactory factory = null; //传统工厂模式的弊端,工厂管理类和工厂类的紧耦合 switch (part) { case RoomParts.Roof: factory = new RoofFactory(); break; case RoomParts.Window: factory = new WindowFactory(); break; case RoomParts.Pillar: factory = new PillarFactory(); break; default: return null; } IProduct product = factory.Produce(); Console.WriteLine("生产了一个产品:{0}", product.GetName()); return product; } } |
Client.cs class Client { static void Main(string[] args) { IProduct window = FactoryManager.GetProduct(RoomParts.Window); Console.WriteLine("我获取到了{0}", window.GetName()); IProduct roof = FactoryManager.GetProduct(RoomParts.Roof); Console.WriteLine("我获取到了{0}", roof.GetName()); IProduct pillar = FactoryManager.GetProduct(RoomParts.Pillar); Console.WriteLine("我获取到了{0}", pillar.GetName()); Console.ReadKey(); } } |
当一个新的产品—地板需要被添加时,我们需要改的地方是:添加零件枚举记录、添加针对地板的工厂类、添加新地板产品类,修改工厂管理类(在switch中添加一条case语句),这样设计的优点在于无论添加何种零件,产品使用者都不需要关心内部的变动,可以一如既往地使用工厂管理类来得到希望的零件,而缺点也有以下几点: ①工厂管理类和工厂类族耦合;
②每次添加新的零件都需要添加一对工厂类和产品类,类型会越来越多;
(2)基于反射的工厂模式的实现
利用反射机制可以实现更加灵活的工厂模式,这一点体现在利用反射可以动态地获知一个产品由哪些零部件组成,而不再需要用一个switch语句来逐一地寻找合适的工厂。①产品、枚举和以上一致,这里的改变主要在于添加了两个自定义的特性,这两个特性会被分别附加在产品类型和产品接口上:
ProductAttribute.cs /// <summary> /// 该特性用于附加在产品类型之上 /// </summary> [AttributeUsage(AttributeTargets.Class)] public class ProductAttribute : Attribute { // 标注零件的成员 private RoomParts myRoomPart; public ProductAttribute(RoomParts part) { myRoomPart = part; } public RoomParts RoomPart { get { return myRoomPart; } } } /// <summary> /// 该特性用于附加在产品接口类型之上 /// </summary> [AttributeUsage(AttributeTargets.Interface)] public class ProductListAttribute : Attribute { // 产品类型集合 private Type[] myList; public ProductListAttribute(Type[] products) { myList = products; } public Type[] ProductList { get { return myList; } } } |
/// <summary> /// 屋顶 /// </summary> [Product(RoomParts.Roof)] public class Roof : IProduct { // 实现接口,返回产品名字 public string GetName() { return "小天鹅屋顶"; } } /// <summary> /// 窗户 /// </summary> [Product(RoomParts.Window)] public class Window : IProduct { // 实现接口,返回产品名字 public string GetName() { return "双汇窗户"; } } /// <summary> /// 柱子 /// </summary> [Product(RoomParts.Pillar)] public class Pillar : IProduct { // 实现接口,返回产品名字 public string GetName() { return "小米柱子"; } } |
Factory.cs public class Factory { public IProduct Product(RoomParts parts) { ProductListAttribute attr = (ProductListAttribute)System.Attribute.GetCustomAttribute(typeof(IProduct), typeof(ProductListAttribute)); foreach (var type in attr.ProductList) { ProductAttribute pa = (ProductAttribute)System.Attribute.GetCustomAttribute(type, typeof(ProductAttribute)); if (pa.RoomPart == parts) { object product = Assembly.GetExecutingAssembly().CreateInstance(type.FullName); return product as IProduct; } } return null; } } ///// <summary> ///// 屋顶工厂 ///// </summary> //public class RoofFactory : IFactory //{ // // 实现接口,返回一个产品对象 // public IProduct Produce() // { // return new Roof(); // } //} ... ... |
FactoryManager.cs class FactoryManager { public static IProduct GetProduct(RoomParts part) { //IFactory factory = null; ////传统工厂模式的弊端,工厂管理类和工厂类的紧耦合 //switch (part) //{ // case RoomParts.Roof: // factory = new RoofFactory(); // break; // case RoomParts.Window: // factory = new WindowFactory(); // break; // case RoomParts.Pillar: // factory = new PillarFactory(); // break; // default: // return null; //} //IProduct product = factory.Produce(); Factory factory = new Factory(); IProduct product = factory.Product(part); Console.WriteLine("生产了一个产品:{0}", product.GetName()); return product; } } |
/// 屋子产品的零件
/// </summary>
public enum RoomParts
{
Roof,
Window,
Pillar,
Floor //地板
}
2.添加代表地板的类型(Product.cs)/// <summary>
/// 地板
/// </summary>
[Product(RoomParts.Floor)]
public class Floor : IProduct
{
// 实现接口,返回产品名字
public string GetName()
{
return "地板";
}
}
3.修改添加在IProduct上的属性初始化参数(增加地板类型), /// <summary>
/// 产品接口
/// </summary>
[ProductList(new Type[] { typeof(Roof), typeof(Window), typeof(Pillar), typeof(Floor) })]
public interface IProduct
{
string GetName();
}
可以看到这时调用者、工厂管理类和工厂都不再需要对新添加的零件进行改动,程序只需要添加必要的类型和枚举记录即可。当然,这样的设计也存在一定缺陷:反射的运行效率相对较低,在产品零件相对较多时,每生产一个产品就需要反射遍历这是一件相当耗时的工作。
作者:周旭龙
出处:http://www.cnblogs.com/edisonchou/p/4827578.html
来自为知笔记(Wiz)
相关文章推荐
- 工厂模式实现方式、使用场景(包括基于反射的简单工厂模式)
- C# 丢弃工厂模式,反射方式实现计算器
- 使用反射来实现简单工厂模式
- Java反射机制-使用反射实现工厂模式
- 使用了继承、多态还有工厂模式和反射,但是还是没有OO的感觉。[已经增加了实现的代码]
- C# 反射工厂模式的实现
- 使用反射在NET中实现动态工厂(第一部分)
- 请问在.net framework精简版,使用C#语言如何实现MD5和SHA1算法
- 使用反射在NET中实现动态工厂(第二部分)
- 如何在ActionScript 3.0里使用工厂模式和模板方法模式(Factory and Template Method Patterns)
- 使用Register/Notify模式在C#中实现非托管资源的统一处置
- 如何使用 C# .NET 在 ASP.NET 应用程序中实现基于窗体的身份验证
- 如何使用 C# .NET 在 ASP.NET 应用程序中实现基于窗体的身份验证
- 使用特性(attributes)和激活机制来实现工厂模式【翻译】
- 如何使用反射技术实现ASP.NET国际化站点
- 使用反射在NET中实现动态工厂(第一部分)
- CodeProject - 使用特性(attributes)和激活机制来实现工厂模式
- 如何在C# 中使用WMI 实现远程查询和共享
- 如何在C#中实现全屏模式
- 在C#中如何实现枚举类型的特性扩展和反射获取