【IT168技术文档】在这篇文章中,引入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的实现。
public enum GameDiskType
{
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 sealed class PS3DiskFactory
{
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了。
public enum GameDevice
{
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,例如
namespace PS3
{
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的实现。
public enum GameDevice
{
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去掉,代码如下:
public class Player
{
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的配置文件就可以了。