微软objectBuilder解读:构造一个轻量级的Dependency Injection容器(2) 工厂和容器
我们利用简单工厂继续对示例进行修改为
public class CreatorFactory
{
public static IMessageCreator GetCreator()
{
return new ConsoleMessageCreator();
}
}
public class ProcessorFactory
{
public static IMessageProcessor GetProcessor(string message)
{
if (message == "order") return new PrinterMessageProcessor();
return new ConsoleMessageProcessor();
}
}
Main里面的代码,
static void
{
Console.WriteLine("Demo DIP ");
IMessageCreator messageCreator = null;
IMessageProcessor messageProcessor = null;
messageCreator = CreatorFactory.GetCreator();
string s = messageCreator.Create();
messageProcessor = ProcessorFactory.GetProcessor(s);
messageProcessor.Process(s);
Console.Read();
}
现在看起来,代码有一定的扩展性,各个组件各司其职,符合高内聚低耦合的特征,如果新增加一种消息处理器类型或者消息生成器,只需要改动一个类的方法,改动不会遍及其他模块或者组件,上层应用系统得到稳定。再来看组件图。
那么当前代码还有么有其他问题或者改进之处呢?
1) 每增加一类接口,就要增加一个Factory类,例如要增加对消息的日志处理,就要增加一个接口IMessageLog和工厂组件LogFactory(可以用抽象工厂改进)
2) Main虽然对具体消息处理生成组件没有依赖,但是对具体工厂类增加了依赖。
解决问题的核心就是抛弃工厂和接口一一对应的建立关系,将工厂泛化,实现改进的工厂。我们给它一个新的定义---容器/Container
我们设想有这样一种万能容器(我心中的God),我们需要某种实现了特定功能(接口)的组件,就从它那里去拿。类似于main等的客户端就无需关心组件从哪里来的,也无需去了解接口和工厂的对应关系了(这也不应该由上层组件所关注),就像潘多拉的魔盒(比喻有点不恰当),你要什么有什么。
理解了Container的概念,我们很快就能思考到Container组件的主要接口应该为:
void Add(Type key, object value); //注册接口/组件
bool Remove(Type key); //注销接口/组件
object Get(Type key); //获取组件实例
bool Contains(Type key); //判断某个接口类型的组件是否存在
......
这里的Type参数很自然就是我们所需要的接口类的类型,Container的接口设计符合需求:需要实现某种接口的组件就从Container里面去拿。如果考虑泛型,那么可以再增加一些方法:
TItem Get<TItem>(); //object Get(Type key)的泛型方法
TItem Get<TItem>(Type key); //object Get(Type key)的泛型方法
细心的读者就会想到Container的数据结构一定是键/值对,接口类型为键,object类型为值了(因为有各种接口组件的实例,所以只能采用object类型来存储),在.net framework里面的Dictionary天然支持键/值对,直观的想法就可以利用Dictionary来实现我们需要的Container.
public interface IContainer
{
void Add(Type key, object value);
bool Remove(Type key);
object Get(Type key);
bool Contains(Type key);
TItem Get<TItem>();
TItem Get<TItem>(Type key);
}
public class DefaultContainer:IContainer
{
private Dictionary<Type, object> inner = new Dictionary<Type, object>();
//注册某个接口类型的组件实例
public void Add(Type key, object value)
{
if (key == null) throw new ArgumentNullException("key");
if (value == null) throw new ArgumentNullException("value");
inner.Add(key, value);
}
//清除某个接口类型的组件实例
public bool Remove(Type key)
{
if (key == null) throw new ArgumentNullException("key");
return inner.Remove(key);
}
//提取某个接口类型的组件实例
public object Get(Type key)
{
if (key == null) throw new ArgumentNullException("key");
if (inner.ContainsKey(key)) return inner[key];
return null;
}
//容器是否注册了包含某个接口类型的组件
public bool Contains(Type key)
{
if (key == null) throw new ArgumentNullException("key");
if (inner.ContainsKey(key)) return true;
return false;
}
//泛型方法: object Get(Type key)
public TItem Get<TItem>()
{
var value=Get(typeof(TItem));
if(value!=null) return (TItem)value;
return default(TItem);
}
//泛型方法: object Get(Type key)
public TItem Get<TItem>(Type key)
{
if (inner.ContainsKey(key)) return (TItem)Get(key);
return default(TItem);
}
}
Main里面的代码改为:
static void
{
Console.WriteLine("Demo DIP");
IContainer container = new DefaultContainer();
container.Add(typeof(IMessageCreator), new ConsoleMessageCreator());
container.Add(typeof(IMessageProcessor), new ConsoleMessageProcessor());
IMessageCreator messageCreator = container.Get<IMessageCreator>();
IMessageProcessor messageProcessor = container.Get<IMessageProcessor>();
string message=messageCreator.Create();
messageProcessor.Process(message);
Console.Read();
}
运行结果为:
下一章节我们要研究现在的Container的问题和微软对此的解决方案Locator.由此我们正式开始研究微软ObjectBuilder的源代码了。