使用反射在NET中实现动态工厂(第一部分)

转载 2007年10月01日 14:05:00
在软件开发进行了若干年后,设计模式逐渐被更多的程序员理解和采用。对常见的特定问题总是有其解决办法,这些解决办法逐渐得到了大家的公认。许多这样的解决办法被汇总整理成设计模式用来解决很多编程中的问题。基于这一点,微软公司提供了一个专栏来讨论各种实用的模式来帮助大家更快的解决开发过程中的问题。
(http://msdn.microsoft.com/msdnmag/issues/03/03/designpatterns/default.aspx)
在设计模式中经常用的一种叫做:工厂模式。工厂模式的好处是使用动态创建对象(甚至只靠对象的类型来创建对象)来降低对象间的耦合。可是当工厂方法和其他模式一起使用来创建高内聚低耦合的代码的时候,工厂方法并不是值得推荐的。使用.Net的反射机制使用C#语言来创建一个工厂类(没有写最常用的实现的部分)。
关于软件设计中的两大原则:高内聚低耦合的好处我就不多讲了。
工厂设计模式:
举个例子来说明工厂模式的好处:比如A对象需要向B对象发送消息,A必须持有一个B对象的引用来完成这个功能,也就是说:B对象必须经过初始化并且A对象可以访问他。我们大家最长使用的方法就是在A对象中直接初始化B对象,直接持有一个对B对象的引用,这样A对象就必须知道怎样初始化B对象,比如:需要那些初始化的参数等等,有经验的程序员都知道这样编程思路写下的程序的弊端。
怎样解决上面的问题呢。
答案是把创建B对象的工作交给另一个类:C
也就是说:所有需要使用B类的地方统一从C那里后的一个B的实例。
    这就是工厂方法的功能。
    当然,我们不可能完全消除耦和,这种方法是降低了程序的耦合程度,有利于程序以后的修改和扩充。上面采用类C来创建B的方法,还有更好的改进,就是使用接口,也就是我们常说的 :纯虚工厂,那样耦和的程度就更低了。大家可以参考具体的参考资料。
工厂模式中有三个主体需要我们分清: 1、工厂 2、客户  3、 产品
客户是:任何需要使用工厂提供对象的对象。
产品是:工厂生成的一个返回到客户的对象。
 
使用工厂模式的一般方法
   工厂模式有很多个版本,不同的做法各有优劣。我们下面比较最常用的两种工厂模式后介绍使用反射机制的工厂模式。
据个例子:
假如你有一个管理电脑零件的程序,其中:
InventoryMgr     , 是客户,仓库管理员。
PartsFactory      , 是工厂;
MonitorInvFactory , 是显示器工厂;
KeyboardInvFactory, 是键盘工厂;
IpartsInventory    , 是上面的工厂返回的产品。
UML图如下:
 
下面我把上面的图给大家解释一下:
MainClass 是用来向IventoryMgr来发送一个需要仓库对象请求的类。比如现在我们需要补充仓库的显示器的数量,就可以向IventoryMgr发送这个请求。代码如下:
class MainClass {
    static void Main(string[] args) {
        PartsFactory myfactory = null;
        InventoryMgr myinvmgr = new InventoryMgr();
        foreach(string marg in args){
            switch(marg) {
                case "Monitors":                
                    myfactory = new MonitorInvFactory();
                    break;
                case "Keyboards":
                    myfactory = new KeyboardInvFactory();
                    break;
                default:
                    break;
            }
            if(myfactory != null)
                myinvmgr.ReplenishInventory(myfactory);            
            myfactory = null;
        }
    }
}
    class InventoryMgr {
    public void ReplenishInventory(PartsFactory vfactory) {
        IPartsInventory PartInv = vfactory.ReturnPartInventory();
        PartInv.Restock();
    }
}
 
interface IPartsInventory {
    void Restock();
}
 
class MonitorInventory : IPartsInventory {
    public void Restock() {
        Console.WriteLine("The monitor inventory has been restocked");
    }
 
class KeyboardInventory : IPartsInventory {
    public void Restock() {
        Console.WriteLine("The keyboard inventory has been restocked");
    }
}
   
abstract class PartsFactory {
    public abstract IPartsInventory ReturnPartInventory();
}
 
class MonitorInvFactory : PartsFactory {
    public override IPartsInventory ReturnPartInventory() {
        return (IPartsInventory) new MonitorInventory();
    }     
}
class KeyboardInvFactory : PartsFactory {
    public override IPartsInventory ReturnPartInventory() {
        return (IPartsInventory) new KeyboardInventory();
    }     
}
上面的代码是可以说是一种工厂模式的合理的实现,但是有些不足的地方。
大家可以从耦和的角度看看上面工厂模式的代码实现。一个一个来:
InventoryMgr 没的说,耦合程度很低,他使用接口屏蔽了创建具体的类的信息。即使你增加新的零件(新工厂)也不会影响他。这就是:对扩展开发,对修改关闭的典型例子。同时InventoryMgr 也对实现不同的IpartsInventory 具有低的耦和。
那问题到底在哪里呢?
问题在MainClass中,他违背了“不要和陌生人说话”的公理。
因为MainClass中使用了创建具体类的工厂。从上面的代码可以看出MainClass实际上在参数未传达到之前并不知道具体的工厂,而且他只是简单的创建好类后有给了InventoryMgr 。 上面的做法实际上把:MainClass 、MonitorInvFactory、KeyboardInvFactory 等本应该不必要的耦和放到了一起。
一般来讲:一个对象中创建另一个对象的原则是:这个对象需要向其创建的对象发送消息,除非这个对象的任务就是创建和返回其他对象。工厂模式就是这样的一个例子。
MainClass的另一个弊端是他不是高内聚的。
MainClass 不应该考虑到底使用那个具体类来处理请求,他只是派发消息的代理。MainClass的作用只是接受客户消息请求,然后传达给InventoryMgr 做进一步的处理。使用反射机制可以更好的解决这个问题。
我们现在看看工厂方法的另一个常见的做法:
不使用抽象工厂创建对象,而以具体工厂创建对象代之。
这样做代码可以简化不少。
MainClass
InventoryMgr
PartsFactory
有一些变化
IpartsInventory  没有改变
MonitorInvFactory   不再需要
KeyboardInvFactory  不再需要 因为一个具体的工厂代替他们使用了。
最大的变化是使用了一个迭代器:enmInvParts。
代码如下:
class MainClass {
    static void Main(string[] args) {       
        InventoryMgr InvMgr = new InventoryMgr();
        foreach(string marg in args){
            switch(marg) {
                case "Monitors":
                    InvMgr.ReplenishInventory(enmInvParts.Monitors);
                    break;
                case "Keyboards":
                    InvMgr.ReplenishInventory(enmInvParts.Keyboards);
                    break;
                default:
                    break;
            }
        }
    }
}
 
public enum enmInvParts : int {Monitors = 1, Keyboards=2}
 
class InventoryMgr {
    public void ReplenishInventory(enmInvParts InventoryPart) {
        PartsFactory factory = new PartsFactory();
        IPartsInventory IP = factory.ReturnPartInventory(InventoryPart);
        IP.Restock();
    }
}
 
class PartsFactory {
    public IPartsInventory ReturnPartInventory(enmInvParts InvPart) {
        IPartsInventory InvType;
 
        switch(InvPart) {
            case enmInvParts.Monitors:
                InvType = new MonitorInventory();       
                break;
            case enmInvParts.Keyboards:
                InvType = new KeyboardInventory();
                break;
            default:
                InvType = null;
                break;
        }
        return InvType;
    }
}
我们现在分析一下上面的代码:
上面提到的MainClass的弊端已经得到了改进。
MainClass不会在和“陌生人说活”了,但是他不得不和工厂耦和。使用enmInvParts 来包装客户端的请求。对客户端请求的包装是合理的,他也简单的把消息给了InventoryMgr 。
明眼人都能看出来:MainClass的耦和问题转嫁给了PartsFactory。并且PartsFactory 和IpartsInventory也出现了耦和。
让我们来看看反射机制如何实现低耦和这个崇高的目标吧。
 

相关文章推荐

ASP.NET MVC3书店--第八节 使用Ajax的购物车(第一部分)

在本网站中,我们允许用户不用注册而直接将书籍放入购物车,直到下订单时才需要在网站中注册。因此购物逻辑与下订单逻辑分为两个控制器来进行处理:一个购物控制器允许匿名用户将书籍放入购物车中,一个下订单控制器...

设计模式学习(十四)————抽象工厂模式(使用Qt框架的反射技术——根据字符串动态创建类来实现)

抽象工厂模式:提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类!这个例子也可以用简单工厂模式+反射+读取配置文件来完成,这样更加简洁!!!普通的抽象工厂模式下面通过一个模拟访问数据库...

一步一步的使用C++和OPENGL实现COLLADA骨骼动画 第一部分

一步一步的使用C++和OPENGL实现COLLADA骨骼动画 第一部分   英文原作者:waZim 原文标题:Step by Step Skeletal Animation i...

一步一步的使用C++和OPENGL实现COLLADA骨骼动画 第一部分

一步一步的使用C++和OPENGL实现COLLADA骨骼动画 第一部分   英文原作者:waZim 原文标题:Step by Step Skeletal Animation in C++ an...

ASP.NET MVC3书店--第五节 表单编辑(第一部分)

在上一节里,我们从数据库中读取了数据并将其显示在页面上。在本章中,我们将实现对于数据的编辑操作。 5.1    创建的带有Create,Update,Delete与Details方法的控制器   ...

ASP.NET MVC3书店--第九节 注册与下订单(第一部分)

在本节中,我们将要创建一个下订单控制器,该控制器将获取购买者的收货地址与付款信息。在下订单之前,用户首先需要在网站中进行注册,因此在访问这个控制器的时候需要进行用户身份认证处理。  点击图9-1中所...

vb.net2008 Direct编程(三)音乐播放器——第一部分

vb2008 Direct编程之音乐播放器 又是新的一篇教程,来看看题目,很吸引人是吧,那就让我们来show一场,编写一个自己的音乐播放器。先来看一下我们的音乐播放器的功能——调节声道,调节音量,播...

Clay:易塑的c#动态对象——第一部分:为什么我们需要它

当我正尝试建立一种合适的数据结构用于在Orchard中承载视图模型(view model)——由许多不同的实体毫无约束地构建而成的时候,很快就变得非常明显必须要用一种动态结构。 我们需要的是一分...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)