Хороший чехол для интерфейсов - PullRequest
11 голосов
/ 26 августа 2009

Я работаю в компании, где некоторым требуется обоснование использования интерфейса в нашем коде (Visual Studio C # 3.5).

Я хотел бы попросить рассуждения Iron Clad, для которых необходимы интерфейсы. (Моя цель - ДОКАЗАТЬ, что интерфейсы являются нормальной частью программирования.)

Мне не нужно убеждать, мне просто нужен хороший аргумент, чтобы убедить других.

Тип аргумента, который я ищу, основан на фактах, а не на сравнении (т. Е. «Потому что .NET-библиотека их использует» основана на сравнении).

Аргумент против них таков: если класс правильно настроен (с его открытыми и закрытыми членами), то интерфейс - это просто дополнительные издержки, потому что те, которые используют класс, ограничены открытыми членами. Если вам нужен интерфейс, реализованный более чем одним классом, просто настройте наследование / полиморфизм.

Ответы [ 17 ]

1 голос
/ 26 августа 2009

Извинения, поскольку это не отвечает на ваш вопрос относительно случая для Интерфейсов.

Тем не менее, я предлагаю попросить этого человека прочитать ..

Шаблоны дизайна Head First

- Ли

1 голос
/ 26 августа 2009

Пожалуйста, прости меня за псевдокод заранее!

Читайте о принципах SOLID. В принципах SOLID для использования интерфейсов есть несколько причин. Интерфейсы позволяют вам отделить ваши зависимости от реализации. Вы можете сделать еще один шаг вперед, используя такой инструмент, как StructureMap, чтобы действительно растопить муфту.

Где вы могли бы привыкнуть к

Widget widget1 = new Widget;

Это конкретно говорит о том, что вы хотите создать новый экземпляр Widget. Однако, если вы делаете это внутри метода другого объекта, вы теперь говорите, что другой объект напрямую зависит от использования Widget. Таким образом, мы могли бы тогда сказать что-то вроде

public class AnotherObject
{
    public void SomeMethod(Widget widget1)
    {
        //..do something with widget1
    }
}

Мы все еще привязаны к использованию Widget здесь. Но, по крайней мере, это более тестируемо, поскольку мы можем внедрить реализацию Widget в SomeMethod. Теперь, если бы мы вместо этого использовали Интерфейс, мы могли бы еще больше отделить вещи.

public class AnotherObject
{
    public void SomeMethod(IWidget widget1)
    {
        //..do something with widget1
    }
}

Обратите внимание, что теперь нам не требуется конкретная реализация Widget, а вместо этого мы запрашиваем все, что соответствует интерфейсу IWidget. Это означает, что все может быть внедрено, что означает, что при повседневном использовании кода мы можем внедрить фактическую реализацию Widget. Но это также означает, что когда мы хотим протестировать этот код, мы можем внедрить поддельную / mock / stub (в зависимости от вашего понимания этих терминов) и протестировать наш код.

Но как мы можем пойти дальше? С помощью StructureMap мы можем еще больше отделить этот код. В последнем примере кода наш код вызова может выглядеть примерно так

public class AnotherObject
{
    public void SomeMethod(IWidget widget1)
    {
        //..do something with widget1
    }
}

public class CallingObject
{
    public void AnotherMethod()
    {
        IWidget widget1 = new Widget();
        new AnotherObject().SomeMethod(widget1);
    }
}

Как вы можете видеть в приведенном выше коде, мы удалили зависимость в SomeMethod, передав объект, который соответствует IWidget. Но в CallingObject (). AnotherMethod у нас все еще есть зависимость. Мы также можем использовать StructureMap для удаления этой зависимости!

[PluginFamily("Default")]
public interface IAnotherObject
{
    ...
}

[PluginFamily("Default")]
public interface ICallingObject
{
    ...
}

[Pluggable("Default")]
public class AnotherObject : IAnotherObject
{
    private IWidget _widget;
    public AnotherObject(IWidget widget)
    {
        _widget = widget;
    }

    public void SomeMethod()
    {
        //..do something with _widget
    }
}

[Pluggable("Default")]
public class CallingObject : ICallingObject
{
    public void AnotherMethod()
    {
        ObjectFactory.GetInstance<IAnotherObject>().SomeMethod();
    }
}

Обратите внимание, что нигде в приведенном выше коде мы не реализуем фактическую реализацию AnotherObject. Поскольку все связано с StructurMap, мы можем позволить StructureMap передавать соответствующие реализации в зависимости от того, когда и где выполняется код. Теперь код по-настоящему гибок в том, что мы можем указать через конфигурацию или программно в тесте, какую реализацию мы хотим использовать. Эта конфигурация может быть выполнена на лету или как часть процесса сборки и т. Д. Но ее не нужно нигде жестко подключать.

1 голос
/ 26 августа 2009

Ну, моя первая реакция в том, что если вам нужно объяснить, почему вам нужны интерфейсы, это непростая битва:)

это, как говорится, кроме всех упомянутых выше причин, интерфейсы являются единственным способом для слабосвязанного программирования, n-уровневой архитектуры, где вам нужно обновлять / заменять компоненты на лету и т. Д. - в личном опыте, однако это было слишком эзотерическая концепция для руководителя архитектурной команды, в результате чего мы жили в аду dll - в мире .net не меньше!

1 голос
/ 26 августа 2009

Интерфейс объявляет контракт, который будет придерживаться любой объект, реализующий его. Это делает обеспечение качества в коде намного проще, чем применение принудительной письменной (не кодовой) или словесной структуры, в тот момент, когда класс украшен ссылкой на интерфейс, требования / контракт ясны, и код не будет компилироваться, пока вы не реализуете этот интерфейс полностью и типобезопасен.

Есть много других веских причин для использования интерфейсов (перечисленных здесь), но, вероятно, они не очень хорошо сочетаются с управлением, а также с хорошим старомодным заявлением о качестве;)

1 голос
/ 26 августа 2009

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

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

На ум приходит реализация IDisposable. Ваша команда поместит метод Dispose в класс Object и позволит ему распространяться по системе независимо от того, имеет ли он смысл для объекта или нет.

1 голос
/ 26 августа 2009

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

0 голосов
/ 26 августа 2009

Я не понимаю, как это лишние накладные расходы.

Интерфейсы обеспечивают гибкость, управляемость кода и возможность повторного использования. При кодировании интерфейса вам не нужно беспокоиться о конкретном коде реализации или логике определенного класса, который вы используете. Вы просто ожидаете результата. Многие классы имеют разные реализации для одной и той же функции (StreamWriter, StringWriter, XmlWriter). Вам не нужно беспокоиться о том, как они реализуют запись, вам просто нужно вызвать ее.

...