技术开发 频道

Visual Studio中解除具体依赖的技术

  2、表驱动法

  借鉴表驱动法【注:参见Steve McConnell著作《代码大全》第18章】的思想,我们可以利用一个Dictionary集合来维护目标对象与键值之间的映射关系。当我们需要获得对象时,可以利用键值对表进行查询,这样就可以有效地消除if语句。例如,可以在Strategy模式中使用表驱动法,将其作为模式的上下文对象,而不必执行对策略对象类型的逻辑判断。利用表驱动法,我们也可以解除对象之间的具体依赖。

  仍然以订单的管理为例。我为订单的管理专门定义了一个OrderManager类,它负责初始化并维持对象表。

public static class 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()方法时,为提供更好的灵活性,寻找策略对象的键值应该放在配置文件中,以避免修改源代码。

string strategyKey = ConfigurationManager.AppSettings["StrategyKey"];
IOrderStrategy strategy
= OrderManager.GetOrderStrategy(strategyKey);

 

  我们甚至可以提供一个注册方法RegisterStrategy(),用以应对未来可能的扩展。

public static class OrderManager
{
    
//其余实现略

    
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类:

public class 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类的定义如下所示:

using Ninject.Core;

public class OrderModule:StandardModule
{
    
public override void Load()
    {
        Bind
<IOrderStrategy>().To<OrderSynchronous>();
    }
}

 

  客户端调用的代码可以通过Ninject提供的IKernel对象获得Order对象:

OrderModule module = new OrderModule();
IKernel kernal
= new StandardKernel(module);

Order order
= kernal.Get<Order>();
order.Insert(
new OrderInfo());
0
相关文章