Почему не каждый класс в .Net Framework имеет соответствующий интерфейс? - PullRequest
12 голосов
/ 03 марта 2010

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

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

Иногда мои классы зависят от классов .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
}

/// <summary>
/// Adapts the DispatcherTimer class to implement the <see cref="IDispatcherTimer"/> interface.
/// </summary> 
public class DispatcherTimerAdapter : DispatcherTimer, IDispatcherTimer
{
}

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

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

Ответы [ 5 ]

11 голосов
/ 03 марта 2010

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

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

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

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

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

6 голосов
/ 03 марта 2010

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

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

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

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

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

2 голосов
/ 03 марта 2010

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

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

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

1 голос
/ 03 марта 2010

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

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

0 голосов
/ 03 марта 2010

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

...