Это утечка абстракции, если реализация интерфейса вызывает Dispose
Рассмотрим этот код:
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
вызов метода или мы используем фабричный интерфейс, чтобы получить реализацию и поместить в нее оператор использования.
Для меня (хотя и новичок в этой теме) это выглядит как дырявая абстракция. Нам нужно помещать вызовы методов в наш код только ради того, как реализация обрабатывает вещи.
Может ли кто-нибудь помочь мне понять это и подтвердить, прав я или нет.
Спасибо
5 ответов
Да, это утечка абстракции, когда вы позволяете ICustomerCreditService
воплощать в жизнь 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);
}
}
}
Вы должны позвонить утилизировать ICustomerCreditService
где он был создан как MyClass
теперь не имеет представления о жизненном цикле ICustomerCreditService
,
Вы должны справиться с жизненным циклом customerCreditService
в коде вызова. Как должен MyClass
знать, если услуга все еще нужна вызывающей стороне? Если вызывающий отвечает за очистку своих ресурсов, то MyClass
не должен быть одноразовым.
// calling method
using (var service = new CustomerCreditServiceClient()) {
var myClass = new MyClass(service);
myClass.DoSomething();
}
Обновление: в комментариях OP упоминается использование IoC, подобного Ninject. Код может выглядеть так:
IKernel kernel = ...;
using (var block = kernel.BeginBlock())
{
var service = block.Get<ICustomerCreditService>();
var myClass = new MyClass(service);
myClass.DoSomething();
}
kernel.BeginBlock()
создает блок активации. Он гарантирует, что разрешенные экземпляры удаляются по окончании блока.
Начиная снова с первой реализации, я попытался бы добавить getInterface-Request к классу, чтобы реализация могла остаться более или менее такой же. Тогда можно смело звонить Dispose
(фактически он только откладывает создание реализации интерфейса, но контролирует его жизненный цикл):
(C#-код не проверен...)
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);
}
}
}
Да, это. Но это неизбежное зло. Само существование IDisposable
Интерфейс является дырявой абстракцией. Утечка абстракций - это просто повседневный факт программирования. Избегайте их, где это возможно, но не волнуйтесь, когда не можете - они в любом случае повсеместны.