Почему не делает каждого класса в платформе .NET, имеют соответствующий интерфейс?

Так как я начал разрабатывать в тесте/поведении управляемый стиль, я ценил способность дразнить каждую зависимость.

Начиная с насмешки платформ как Moq работают лучше всего при сообщении дразнить интерфейс я теперь реализую интерфейс почти для каждого класса, я создаю b/c, скорее всего, я должен буду дразнить его в тесте в конечном счете. Ну, и программирование к интерфейсу является хорошей практикой, так или иначе.

Время от времени мои классы берут зависимости от классов .NET (например, FileSystemWatcher, DispatcherTimer). Было бы замечательно в этом случае иметь интерфейс, таким образом, я мог зависеть от IDispatcherTimer вместо этого, чтобы смочь передать его насмешка и моделировать ее поведение, чтобы видеть, реагирует ли моя система под тестом правильно.

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

Вот такой адаптер для DispatcherTimer и соответствующего интерфейса:

 using System;
using System.Windows.Threading;

public interface IDispatcherTimer
{
    #region Events

    event EventHandler Tick;

    #endregion

    #region Properties

    Dispatcher Dispatcher { get; }

    TimeSpan Interval { get; set; }

    bool IsEnabled { get; set; }

    object Tag { get; set; }

    #endregion

    #region Public Methods

    void Start();

    void Stop();

    #endregion
}

/// 
/// Adapts the DispatcherTimer class to implement the  interface.
///  
public class DispatcherTimerAdapter : DispatcherTimer, IDispatcherTimer
{
}

Хотя это не конец света, интересно, почему разработчики .NET не заняли минуту, чтобы заставить их классы реализовать эти интерфейсы с самого начала. Это озадачивает меня тем более, что теперь существует большое стремление к хорошим методам из Microsoft.

У кого-либо есть кто-либо (возможно, внутри) информацией, почему это противоречие существует?

13
задан Jon Seigel 1 April 2010 в 01:58
поделиться

5 ответов

Интерфейсы могут быть очень полезны, и, конечно, жаль, что в библиотеках классов .NET есть упущения, когда интерфейс (или два) мог бы сделать вещи чище или проще.

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

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

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

Наконец, отвечая непосредственно на ваш вопрос, Microsoft должна принимать решения о том, сколько времени и усилий вкладывать в .NET при каждом выпуске. Каждая функция и каждый тип должны быть разработаны, реализованы, документированы, протестированы, поддержаны и так далее. Поскольку функции не бесплатны, должна быть убедительная причина для их внедрения, чтобы преодолеть большой барьер стоимости. Задержка выпуска .NET для добавления интерфейсов, которые, возможно, никогда не будут широко полезны (а возможно, даже вредны), - это не то, что предпочло бы большинство разработчиков.

11
ответ дан 1 December 2019 в 23:14
поделиться

Это старая дискуссия, и TDDеры считают, что слишком мало швов в BCL, в то время как Microsoft, как правило, очень консервативна в отношении предоставляемых швов. Многие реализации BCL в значительной степени полагаются на внутренние и закрытые классы, что действительно ухудшает тестируемость .

Я искренне верю, что тестируемость первых версий .NET никогда не обсуждалась, но позже Microsoft узнала об этой проблеме. Однако осознание проблемы - это еще не то, что делать с ней достаточно.

Несмотря на то, что он стал лучше, главный аргумент Microsoft против предоставления более открытого BCL заключается в том, что он возлагает определенное бремя на их усилия по разработке:

  • Они должны убедиться, что каждый потребитель интерфейса не нарушает замену Лискова. Принцип. Их аргумент состоит в том, что это требует дополнительных усилий по тестированию.
  • Они хотят убедиться, что не загоняют себя в угол, выпуская непродуманный API. Аргумент здесь в том, что, как только тип доступен в BCL, его очень сложно удалить из-за причин обратной совместимости.

Я не говорю, что полностью согласен с этими аргументами, но Microsoft чаще всего приводит их.

6
ответ дан 1 December 2019 в 23:14
поделиться

У интерфейсов есть слабое место - таблицы виртуальных функций. Для каждого метода / свойства интерфейса система резервирует указатель в специальном блоке памяти - виртуальной таблице класса. Это делает вызовы функций медленнее и потребляет память. Таким образом, вы должны использовать только интерфейсы, в которых могут существовать разные реализации. В противном случае вы снижаете производительность.

-1
ответ дан 1 December 2019 в 23:14
поделиться

Интерфейсы представляют собой контракты, о которых договаривается каждый реализующий класс. Они являются способом управления абстракцией и частью инструментария языка, используемого для построения объектно-ориентированных систем. Если единственной причиной введения интерфейса является возможность легко высмеять класс, то интерфейс не должен быть там. Насмешка - это концепция, имеющая смысл только для разработчиков системы. Тот факт, что класс не может быть высмеян, не является признаком плохого дизайна. Если следовать той же логике, то зачем нам нужно ключевое слово sealed? Зачем вам нужно запрещать кому-то наследовать, зачем делать метод невиртуальным, чтобы предотвратить переопределение? Потому что это часть объектно-ориентированного дизайна. И вы не создаете дизайн, основываясь на возможностях фреймворков mocking. Короче говоря, иметь интерфейс для каждого класса было бы нелепо.

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

P.S. Если вы используете TypeMock, то вы можете высмеивать даже статические и запечатанные классы.

2
ответ дан 1 December 2019 в 23:14
поделиться

Одной из основных причин отказа от использования интерфейсов по умолчанию является будущая проверка вашего API; невозможно расширить интерфейс. Вы можете создавать только новые интерфейсы. Это означает, что вы получите IFileWatcher1, IFileWatcher2, IFileWatcher3 (просто посмотрите на COM, чтобы увидеть это в действии). Это сильно загрязняет API. Классы (абстрактные и не абстрактные) могут быть расширены.

Кроме того, если вам не нужна зависимость, например, от FileSystemWatcher, вероятно, в любом случае создавать такую ​​зависимость в первую очередь (будь то через FileSystemWatcher или IFileSystemWatcher4) - не лучшая идея. Предоставьте для этой цели свои собственные адаптеры, для которых вы можете предоставить интерфейсы.

1
ответ дан 1 December 2019 в 23:14
поделиться
Другие вопросы по тегам:

Похожие вопросы: