10

考虑这段代码:

public class MyClass()
{
  public MyClass()
  {    
  }

  public DoSomething()
  {
    using (var service = new CustomerCreditServiceClient())
    {
       var creditLimit = service.GetCreditLimit(
         customer.Firstname, customer.Surname, customer.DateOfBirth);       
    }
  }
}

我们现在想要重构它以松散耦合它。我们最终得到了这个:

public class MyClass()
{
  private readonly ICustomerCreditService service;

  public MyClass(ICustomerCreditService service)
  {
     this.service= service;
  }

  public DoSomething()
  {
     var creditLimit = service.GetCreditLimit(
       customer.Firstname, customer.Surname, customer.DateOfBirth);       
  }
}

看起来不错吧?现在任何实现都可以使用该接口,一切都很好。

如果我现在说实现是一个 WCF 类,并且重构完成之前的 using 语句是有原因的,那该怎么办。即/关闭 WCF 连接。

所以现在我们的接口必须实现一个Dispose方法调用,或者我们使用工厂接口来获取实现并在其周围放置一个 using 语句。

对我来说(虽然对这个主题很陌生),这似乎是一个有漏洞的抽象。我们不得不将方法调用放在我们的代码中,只是为了实现处理东西的方式。

有人可以帮助我理解这一点并确认我是对还是错。

谢谢

4

5 回答 5

6

ICustomerCreditService是的,当您让implement时,这是一个泄漏的抽象IDisposable,因为您现在已经ICustomerCreditService考虑到了特定的实现。此外,这会向该接口的使用者传达它可以处置该服务,这可能是不正确的,特别是因为一般来说,资源应该由创建它的人(拥有所有权)处置。当您将资源注入到一个类中(例如使用构造函数注入)时,不清楚消费者是否被赋予了所有权。

所以一般来说,负责创建该资源的人应该处理它。

但是,在您的情况下,您可以通过实现一个非一次性实现来简单地防止这种情况发生,该实现ICustomerCreditServiceClient只是在同一方法调用中创建和处置 WCF 客户端。这使一切变得更加容易:

public class WcfCustomerCreditServiceClient
    : ICustomerCreditServiceClient
{
    public CreditLimit GetCreditLimit(Customer customer)
    {
        using (var service = new CustomerCreditServiceClient())
        {
            return service.GetCreditLimit(customer.Firstname,
                customer.Surname, customer.DateOfBirth);       
        }
    }
}
于 2012-09-04T08:46:16.200 回答
1

您应该处理customerCreditService调用代码中的生命周期。应该如何MyClass知道调用者是否仍需要该服务?如果调用者负责清理其资源,则MyClass不需要是一次性的。

// calling method

using (var service = new CustomerCreditServiceClient()) {
    var myClass = new MyClass(service);
    myClass.DoSomething();
}

更新:在评论中,OP 提到了使用像 Ninject 这样的 IoC。然后代码可能如下所示:

IKernel kernel = ...;

using (var block = kernel.BeginBlock())
{
    var service = block.Get<ICustomerCreditService>();
    var myClass = new MyClass(service);
    myClass.DoSomething();
}

kernel.BeginBlock()创建一个激活块。它确保在块结束时处理已解析的实例。

于 2012-09-04T08:30:00.360 回答
1

ICustomerCreditService您应该调用已实例化的 Dispose,因为MyClass现在不知道ICustomerCreditService.

于 2012-09-04T08:30:27.270 回答
1

再次从第一个实现开始,我会尝试向类添加一个 getInterface-Request,以便实现可以或多或少保持不变。然后它可以安全地调用Dispose(实际上它只是推迟了接口实现的创建,但仍然控制着它的生命周期):(c#-code not verify...)

public class MyClass()
{
  public delegate ICustomerCreditService InterfaceGetter;
  private InterfceGetter getInterface;
  public MyClass(InterfaceGetter iget)
  {
    getInterface = iget;
  }
  public DoSomething()
  {
    using (var customerCreditService = getInterface())
    {
       var creditLimit = customerCreditService.GetCreditLimit(customer.Firstname, customer.Surname, customer.DateOfBirth);       
    }
  }
}
于 2012-09-04T08:53:48.007 回答
0

是的。但这是必要的邪恶。接口的存在本身就是IDisposable一个泄漏的抽象。泄漏抽象只是编程的日常事实。尽可能避免它们,但当你不能时不要担心——它们无处不在。

于 2012-09-04T08:54:43.583 回答