2、表驱动法
借鉴表驱动法【注:参见Steve McConnell著作《代码大全》第18章】的思想,我们可以利用一个Dictionary集合来维护目标对象与键值之间的映射关系。当我们需要获得对象时,可以利用键值对表进行查询,这样就可以有效地消除if语句。例如,可以在Strategy模式中使用表驱动法,将其作为模式的上下文对象,而不必执行对策略对象类型的逻辑判断。利用表驱动法,我们也可以解除对象之间的具体依赖。
仍然以订单的管理为例。我为订单的管理专门定义了一个OrderManager类,它负责初始化并维持对象表。
{
private static IDictionary<string,IOrderStrategy> m_strategyTable;
static OrderManager()
{
Init();
}
private static void Init()
{
m_strategyTable = new Dictionary<string,IOrderStrategy>();
m_strategyTable.Add("sync",new OrderSynchronous());
m_strategyTable.Add("async",new OrderAsynchronous());
}
public static IOrderStrategy GetOrderStrategy(string strategyKey)
{
IOrderStrategy strategy;
if (m_strategyTable.TryGetValue(strategyKey, out strategy))
{
return strategy;
}
else
{
throw new Exception("无法找到正确的订单策略对象");
}
}
}
在调用OrderManager的GetOrderStrategy()方法时,为提供更好的灵活性,寻找策略对象的键值应该放在配置文件中,以避免修改源代码。
IOrderStrategy strategy = OrderManager.GetOrderStrategy(strategyKey);
我们甚至可以提供一个注册方法RegisterStrategy(),用以应对未来可能的扩展。
{
//其余实现略
public static void RegisterStrategy(
string strategyKey,
IOrderStrategy strategy)
{
if (String.IsNullOrEmpty(strategyKey))
{
throw new ArgumentNullException(strategyKey);
}
if (strategy == null)
{
throw new ArgumentNullException("策略对象不能为null");
}
if (m_strategyTable.ContainsKey(strategyKey))
{
throw new ArgumentException("已经存在键值" + strategyKey);
}
m_strategyTable.Add(strategyKey,strategy);
}
}
3、依赖注入
依赖注入(Dependency Injection)是一个漂亮的隐喻。依赖关系就像是被注入的液体,我们可以在任何时候将依赖关系注入到模块中,而不只限于在编译时绑定。既然这种依赖关系是通过注入的方式完成,就意味着我们可以随时更新,因为注入的液体与模块并无直接关联。实现依赖注入的前提是面向接口编程,而辅助的技术则是利用反射技术。
依赖注入是目前大多数轻量级IoC(控制反转,Inversion of Control)容器用于解除外部服务与容器服务之间依赖关系的一把利刃。首先,容器服务包含了外部服务接口的定义。然后,依赖注入通过使用一个专门的装配器对象,提供外部服务的具体实现,并其赋值给对应的容器服务对象。Martin Fowler将依赖注入的形式分为三种:构造函数注入(Constructor Injection)、设置方法注入(Setter Injection)和接口注入(Interface Injection)。其中,接口注入通过定义接口约束的方式实现依赖注入,会给容器带来设计的限制。而构造函数注入与设置方式注入则表现了产生依赖的两个连接点:构造函数与属性。如果构造函数参数或属性对象的类型为抽象的接口类型,则产生具体依赖的源头在于具体对象的创建。将创建具体对象的职责转移到IoC容器,就可以在运行时为构造函数参数或属性对象传递依赖。
目前,实现了依赖注入的轻量级容器已经应用在许多框架产品中,如Java平台下的Spring、PicoContainer等。在.NET平台下,常见的依赖注入框架包括AutoFac,Ninject,Spring.NET,StructureMap和Windsor等。
以Ninject框架为例,我们可以定义这样的Order类:
{
private IOrderStrategy m_strategy;
public Order(IOrderStrategy strategy)
{
m_strategy = strategy;
}
public void Insert(OrderInfo order)
{
m_strategy.Insert(order);
}
}
然后,我们需要自定义一个OrderModule类,它派生自Ninject.Core.StandardModule类。这是Ninject实现依赖注入的一个特色,它抛弃了传统的xml映射文件,而是利用类型绑定的方式,并根据要创建的具体对象分组建立对应的Module类。注意,它不同于之前的解耦方法,因为它对业务逻辑代码没有造成任何侵略与污染。如上定义的Order类,保留了领域对象的本来面貌。使得领域层的开发人员可以专心致志着力于业务逻辑的实现。OrderModule类的定义如下所示:
public class OrderModule:StandardModule
{
public override void Load()
{
Bind<IOrderStrategy>().To<OrderSynchronous>();
}
}
客户端调用的代码可以通过Ninject提供的IKernel对象获得Order对象:
IKernel kernal = new StandardKernel(module);
Order order = kernal.Get<Order>();
order.Insert(new OrderInfo());