原型模式与各种工厂模式,还有反射

本文链接:原型模式与各种工厂模式,还有反射

 

各种工厂模式与原型模式相对简单就不再具体介绍了,只有个小例子,见下。

 

原型模式与工厂模式实现上的区别主要在于:原型模式的工厂类维护一个产品的原型对象,并在工厂方法中返回原型对象的克隆;而工厂模式则直接返回新的产品对象。

 

只给出两种工厂的示例代码(C#),先给出产品示例:

 

原型模式工厂:

 

普通的简单工厂:

 

(Java中相等性比较请使用:boolean Object.equals(Object)。Java没有重载“==”运算符,所以不保证字符串用“==”比较的结果都正确,虽然多数情况下会是正确的。)

 

通过简单的对比,可以看出原型模式的优点在于,原型工厂类与具体产品耦合度低。一旦工厂类代码编写完毕,以后就不需要在进行更改。而普通的工厂看起来就很不舒服了,只要具体产品的体系有任何改动,工厂就需要进行相关的调整,这样就需要同时维护两个地方的代码。

 

那么有什么改进的办法么?有,至少在.Net与Java下有,那就是——反射。

 

大家应该都知道.Net与Java的类与C++是有些不同的,C++的类只有一些基本的类型信息,没有元数据。而.Net与Java的类,有大量的类型相关的信息,就是所谓的元数据(Metadata)。元数据都有什么东西呢?比如说,一个类,有名称,命名空间,访问权限,所有的域信息,所有的方法信息,所有的属性信息,所有的事件信息,所有的嵌套类信息,等等;一个方法,有名称,有所在类,访问权限,返回值类型信息,参数类表的类型信息,甚至可以对其进行方法调用!

 

那么具体的做法如下:

一个使用反射实现的工厂:

 

可以看出,使用反射实现的普通工厂,有了和原型模式工厂那样的低耦合度,而且更低,因为在对工厂初始化的时候,原型工厂还需要对其原型对象进行初始化,而使用反射的工厂就什么都不用管了,因为其只依靠类型信息就可以工作。但其有两个个缺点,一是产品系列的类的构造器至少要有一个是完全相同的,否则无法通过反射统一调用;二是速度肯定要比硬编码的普通的工厂模式低不少。所以当产品类型不需要经常改动的时候,就用基本的工厂模式就可以了。

这个类中对通过反射得到的构造器进行了缓存,这是因为反射的速度相对于赋值与方法调用来说,是比较慢的,通过缓存,可以减少反射调用的次数。我做了一个简单的测试,Release设置下,对反射工厂的缓存版本和非缓存版本分别进行1000000次的循环调用,缓存版本约使用4600+毫秒,非缓存版本约使用16000+毫秒,相差还比较明显的。所以反射不要滥用,能不用反射实现的就不要使用。

还有一种使用反射的类似方案,是使用了一个不同的方法:Activator.CreateInstance(Type, object[]),缓存的是产品的Type,效率和上面的示例代码差不多,不再给出具体代码。

通过对比发现,上面用到的各种反射方法中,最慢的是:Type.GetType(string)方法。虽然不知道方法是怎么实现的,但我猜测可能是这样的(仅仅是猜测):根据参数中完整类名的命名空间,逐级向下寻找(不知道类型在运行库中是不是有某种结构的索引之类的);或者是遍历程序集,寻找匹配的类。(唉,以后有机会看看.Net实现机制方面的书籍与.Net类库源码,应该能了解更多)

 

反射的优点在于非常的灵活,灵活到甚至超过了动态语言。但使用反射的负面效果,除了刚才提到的效率问题(当然肯定比解释型语言快得多),还有一个很实际的问题,使用反射就不能得到集成开发环境的各种检查的帮助。

 

在绝大多数情况下,是不需要使用反射的(反射多数在工具类中使用),一般我们使用虚方法以及方法指针就足够实现动态调用了。那么什么情况下必须用反射呢?我就把我所遇到的情况列举出来吧:

  1. 动态的生成对象:众所周知,new时必须给定具体的对象类型,那么想要动态获得具体对象,只有使用反射了。限制条件是,相关的所有类都必须有相同签名的构造器。
  2. 动态调用静态方法:静态方法是没有虚特性的,如果有一套有继承关系的类,每个类都有一个获得某个东西默认值的静态方法,方法签名相同。因为静态方法没法使用虚特性进行动态调用,所以可以使用反射来做,当然,限制条件是方法签名必须完全相同。
  3. 我用的比较少,目前就想到这些了,欢迎大家补充~

最后,希望C++能原生支持元数据与反射……

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值