更新1:使用as转换数据类型。
在上篇文章 我的实用设计模式 - Simple Factory,Factory Method和Abstract Factory 讲述了Simple Factory,Factory Method和Abstract Factory的关系以及演变过程。在这篇文章中,引入Reflection来实现Simple Factory,增强系统的可扩展性。
下面是Reflection在msdn的定义
Reflection provides objects (of type Type) that describe assemblies, modules and types. You can use reflection to dynamically create an instance of a type, bind the type to an existing object, or get the type from an existing object and invoke its methods or access its fields and properties.
我的理解是Reflection在运行时提供从assemblies(.NET平台下编译生成的DLL或者EXE文件)取出Metadata信息的功能,然后可以对Metadata的信息进行读取,修改,以及动态的根据这些Metadata生成对象。
由于.NET平台提供Reflection,意味着.NET开发的assemblies很容易就可以进行disassemble(反编译),同时也可以通过Reflection调用一下非public的Methods,但是这些不是这篇文章讨论的范围。
现在先看回上篇文章中Simple Factory的实现。
{
Racing,
Shooting,
Fighting
}
public sealed class PS3DiskFactory
{
public static AbstractGameDisk CreateGameDisk(GameDiskType type)
{
switch (type)
{
case GameDiskType.Racing:
return new PS3RacingGameDisk();
case GameDiskType.Shooting:
return new PS3ShootingGameDisk();
case GameDiskType.Fighting:
return new PS3FightingGameDisk();
default:
return null;
}
}
}
为了方便演示Reflection,我把GameDiskType的定义修改了一下,区分大小写。从原先没有使用模式,直接在Client new对象的方式到使用Simple Factory,实现了对对象实例化过程的封装,所有的对象实例化变化被封装到PS3DiskFactory的工厂类里面了。这一步符合设计原则“封装变化”。可是有人会问,在PS3DiskFactory里面,如果新增GameDisk还是需要修改条件从句(switch...case),和原先的设计没有太大的改变,从Refactoring的角度看,代码还是有臭味道。 Well,Well,Well,你的鼻子真灵呀。这里涉及到设计上的边界(Boundary)问题。从Client类看,PS3DiskFactory确实把对象初始化的需求封装了,变化不会在Client类里面发生。从整个系统来看,对象初始化的变化,还是需要修改源代码(例如修改PS3DiskFactory),那怎么解决这个问题了,在.NET里面提供了Reflection可以解决这个问题。
{
public AbstractGameDisk CreateGameDisk(GameDiskType type)
{
// Get fully qualified factory name
string name = this.GetType().Namespace + ".PS3" + type.ToString() + "GameDisk";
// Dynamic factory creation
return System.Activator.CreateInstance(Type.GetType(name)) as AbstractGameDisk;
}
}
在CreateGameDisk里面,先组成需要实例化的类的全名称(fully qualified name),然后使用System.Activator.CreateInstance来生成。上面是PS3DiskFactory的代码,用于实例化PS3的GameDisk,可以看到生成全名称的时候hard code了”PS3“。如果把设备名字作为参数传递到工厂中,那么一个工厂就可以同时生成PS3和Wii的GameDisk了。
{
PS3,
Wii
}
public sealed class DiskFactory
{
public AbstractGameDisk CreateGameDisk(GameDevice device, GameDiskType type)
{
// Get fully qualified factory name
string name = this.GetType().Namespace + "." + device.ToString() + type.ToString() + "GameDisk";
// Dynamic factory creation
return System.Activator.CreateInstance(Type.GetType(name)) as AbstractGameDisk;
}
}
在上述代码中,要实例化的对象的namespace是和当前工厂类是同一个namespace,同样要实例化的类和工厂类在同一个Assembly里面。当然他们可以分布于不同的assemblies。拿上篇文章的Asbtract Factory的工厂类作为例子,如果不同的游戏机类型,其Concrete Factories和Concrete Products分别独立存在于一个namespace和assembly,例如
{
public class PS3Console : AbstractGameConsole
{
}
public class PS3Joystick : AbstractJoystick
{
}
public class PS3Player : AbstractPlayer
{
public override AbstractGameDisk CreateGameDisk(GameDiskType type)
{
return new PS3RacingGameDisk();
}
public override AbstractGameConsole CreateGameConsole()
{
return new PS3Console();
}
public override AbstractJoystick CreateJoystick()
{
return new PS3Joystick();
}
}
}
名字空间(namespace)为PS3的程序编译到PS3Factory.dll中,而Wii的实现在WiiFactory.dll中,以下是原有没有使用Reflection的实现。
{
PS3,
Wii
}
public class Player
{
private AbstractPlayer playerFactory;
public Player(GameDevice device)
{
switch (device)
{
case GameDevice.PS3:
playerFactory = new PS3Player();
break;
case GameDevice.Wii:
playerFactory = new WiiPlayer();
break;
}
}
}
使用Reflection后,可以把switch去掉,代码如下:
{
private AbstractPlayer playerFactory;
public Player(GameDevice device)
{
//Load Assembly
Assembly asm = Assembly.LoadFrom(device.ToString() + "Factory.dll");
// Get fully qualified factory name
string name = device.ToString() + "." + device.ToString() + "Player";
// Dynamic factory creation
playerFactory = asm.CreateInstance(name) as AbstractPlayer;
}
}
GameDevice可以来自于配置文件。使用Reflection后,系统动态plug-in新的模块,例如需要增加Xbox模块,把Xbox的Concrete Factories和Concrete Products实现封装到Xbox的namespace,同时编译到XboxFactory.dll,修改GameDevice的配置文件就可以了。
参考文献:
Reflection (C# Programming Guide)
本文转自Jake Lin博客园博客,原文链接:http://www.cnblogs.com/procoder/archive/2009/04/30/1447024.html,如需转载请自行联系原作者