Является ли наследование интерфейса плохой практикой? - PullRequest
8 голосов
/ 09 августа 2011

Мне было интересно, если следующий код показывает плохую практику (о наследовании интерфейса):

public interface IFoo : IDisposable
{
    void Test();
}

public class TestImpl : IFoo
{
    public void Test()
    {
        // do something
    }

    public void Dispose()
    {
        // disposing my dependencies
    }
}

public class FooFactory
{
    public IFoo CreateFoo()
    {
        return new TestImpl();
    }
}

public class Client
{
    public void Main()
    {
        FooFactory factory = new FooFactory();
        using(IFoo foo = factory.CreateFoo())
        {
            // do stuff then auto-dispose
            foo.Test();
        }
    }
}

Здесь я хотел две вещи: 1. Использовать оператор использования C #, чтобы я мог избавиться от своего конкретного объектаэлегантные зависимости (не нужно ничего приводить к IDisposable).2. Используйте фабричный метод для создания конкретного объекта, чтобы я мог работать только с интерфейсами.

Разделенные, оба являются хорошими практиками, но вместе?Я не нашел ничего, что говорило бы, что это неправильно http://msdn.microsoft.com/en-us/library/ms229022.aspx (MSDN - Дизайн интерфейса), но я думаю, что это не звучит хорошо ...

Буду признателен за любые комментарии по этому поводу.Если это плохая практика, с какими проблемами я столкнусь при использовании этого подхода?

Спасибо!

Ответы [ 4 ]

17 голосов
/ 09 августа 2011

В дикой природе нет такой вещи, как определенно хорошая или плохая практика, у каждого есть свои плюсы и минусы.
Практика становится хорошей или плохой, только когда она применяется к конкретной проблеме (читай: не IFoo).

public bool IsGood<TContext>(IPractice practice)
    where TContext : RealWorldApplication, new();

Итак, вместо того, чтобы пытаться найти руководство по MSDN, задайте себе следующий вопрос:

Делает ли эторешить проблему и сделать код более простым для чтения?

Если ответ да , воспользуйтесь им.

Лично мне нравится подход.Если контракт на IFoo требует надлежащей утилизации (подумайте: IGraphicalObject, IResource, IConnection), это очень хорошее дизайнерское решение, чтобы сделать это явным.

ДонНе попадайтесь в ловушку: программирование с интерфейсами делает вас менее зависимыми от конкретных реализаций, но программирование только с интерфейсами может быть кошмаром.Будьте разумны, вот и все.

Обновление

Вы можете использовать Google, чтобы найти все интерфейсы, которые наследуются от IDisposable в самом .NET Framework:

intitle:interface "inherits idisposable" site:msdn.microsoft.com

Как я уже говорил выше, это обычно делается для объектов, которые известны для использования ресурсов, таких как соединения с базой данных, обертки неуправляемых объектов, графическиеобъекты и так далее.

Наложение реализации Dispose() на класс, в котором нечего утилизировать , было бы плохой идеей.
В этом случае лучше оставить его необязательным ипроверьте поддержку IDisposable, как предложено Питером 1056 *.

10 голосов
/ 09 августа 2011

Это совершенно верно. Это именно то, для чего предназначены интерфейсы. Они определяют контракты. Контракт может быть построен с использованием нескольких субподрядов.

3 голосов
/ 09 августа 2011

Я думаю, что это правильно. Ваша фабрика скрывает, какой класс на самом деле создан, поэтому IFoo должен наследовать IDisposable, чтобы клиент смог вызвать Dispose(). С этим классом клиент может - и должен - быть полностью не осведомленным о действующем экземпляре класса. Вся соответствующая информация должна переноситься интерфейсом, включая необходимость утилизации.

2 голосов
/ 09 августа 2011

Технически все в порядке с этим решением.

С семантической точки зрения вы можете утверждать, что необходимость IDisposable - это деталь реализации и не имеет ничего общего с фактическим контрактом вашего класса.

Что вы можете сделать, это привести объект к IDisposable и вызвать Dispose(), только если он реализует IDisposable:

var disposable = foo as IDisposable;

if (disposable != null)
    disposable.Dispose();

Но в этом случае я бы пошел с интерфейсомнаследство, как вы предложили.Это не самый чистый подход, но он отлично справляется с задачей и требует меньше кода.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...