Ответ 1
Если бы я знал, что будет только одна реализация, я бы не создал интерфейс. Это относится к YAGNI, IMO.
(Конечно, редко я знаю что-то о будущем для факта...)
Зачем вам когда-либо использовать интерфейс, у вас будет только одна его реализация?
Если бы я знал, что будет только одна реализация, я бы не создал интерфейс. Это относится к YAGNI, IMO.
(Конечно, редко я знаю что-то о будущем для факта...)
Отделить API от реализации, что часто является хорошей практикой программирования. Это поможет с удобочитаемостью, если ничего другого. Это также позволит кому-то использовать ваш код в будущем, чтобы обеспечить альтернативную реализацию интерфейса, если они этого желают.
Очень хорошая практика - установить interface
как спецификацию для кода class
на.
Если вы определяете общедоступные методы/функциональные возможности, которые ваш interface
будет иметь, вы можете заблокировать это на месте. Тогда становится легче кодировать class
, когда у вас есть четкая функциональность.
Я считаю, что более важно сделать хороший код для написания кода проще, чем содержать базу кода.
Потому что они пропускают файлы заголовков С++?
Меня это тоже беспокоит, когда люди создают интерфейс, абстрактный текст и реальную реализацию для каждого класса, даже если их не будет больше одного, а все три файла почти пусты.
Однако большое использование будет:
Заместитель будущего расширения/улучшений
Легко реализовать инверсию инъекции управления/зависимостей
Легко реализовать mocks для модульного тестирования.
* Редактирование:
Я заметил, что у вас есть Spring в ваших тегах. Если использовать Spring, то # 2 выше, вероятно, является biggie. Его проще разрабатывать классы, которые ожидают интерфейсы, и вы можете поменять фактические реализации интерфейса в конфигурации Spring (Injection Dependency).
Если действительно существует одна реализация и только когда-либо будет одной реализацией, не кодируйте интерфейс. (YAGNI).
Тем не менее, в 99% случаев существует как минимум две реализации класса, один реальный, один, используемый в тестировании.
Легкость разделения и издевательств частей системы во время тестирования более чем стоит дополнительных усилий по созданию интерфейса для одного класса.
Как побочная заметка, и, возможно, это связано только с тем, что мне не хватает самоконтроля, кодирование против интерфейсов меня намного больше фокусирует, когда я работаю над определенным классом. Я нахожу, что больше думаю о себе, и этот интерфейс, который я вызываю, вернет/сделает это ", а не" и этот класс, я работаю так, он вызывает х, преобразует y, общается с z, ставит кофе, пушит подушки, интегрирует n по отношению к y, а затем возвращает экземпляр обезьяны... подождите, что я делал снова? "
Согласен, я вижу это все время, и он делает мой орех. Если пока их только одна реализация и вряд ли будет больше, я оставлю ее до следующего разработчика, чтобы определить и реализовать интерфейс, когда они сочтут это подходящим.
Некоторые технологии требуют использования интерфейсов. COM для одного. Их у вас часто есть только один класс, реализующий ваш интерфейс.
Если у вас будет только одна реализация, я бы этого не сделал. Приведенные здесь аргументы о том, что у вас могут быть несколько реализаций, действительно не выдерживают тщательного изучения. Если вы закончите с несколькими реализациями, для извлечения интерфейса требуется около 5 секунд, используя либо встроенные инструменты Visual Studio, либо Resharper.
Итак, да, YAGNI - не усложняйте свою жизнь, пока вам не придется.
Одна из причин может быть развязана: если ваши классы используются другим разработчиком, вы можете дать ему исходный код для интерфейсов и сохранить информацию о реализации для себя.
Если реализация изменится, а "контракт", определенный в интерфейсе, не будет, вы будете счастливы: ваш интерфейс все еще описывает, что делает класс, и никто не должен знать, как он это делает.
Интерфейсы с одним методом обычно можно избежать, когда вы работаете на языках, которые позволяют передавать функции в качестве значений первого порядка. Чтобы назвать несколько примеров:
Интерфейсы с одним методом для передачи фрагментов логики реализации:
public interface IComparable<T>
{
int CompareTo(T first, T second);
}
public static class Array<T>
{
public void Sort(T[] input)
{
if (T is IComparable) { /* sorting implementation */ }
else { Throw new Exception("Doesn't implement IComparable"); }
}
}
Можно заменить на:
public static class Array<T>
{
public void Sort(T[] input, Func<T, T, int> compare)
{
/* sorting implementation */
}
}
Я считаю функциональный стиль более читаемым и многоразовым.
Интерфейсы с одним методом для зависимостей-инъекций/издевательств:
public interface IFailureNotifier
{
void HandleFailure(Exception ex);
}
public class TransactionProcessor
{
public IFailureNotifier FailureNotifier { get; set; }
public TransactionProcessor(IFailureNotifier failureNotifier)
{
this.FailureNotifier = failureNotifier;
}
public void ProcessItems(object[] items)
{
try
{
for(object item in items) { /* do stuff */ }
}
catch(Exception ex)
{
FailureNotifier.HandleFailure(ex);
}
}
}
Можно переписать как:
public class TransactionProcessor
{
public Action<Exception> FailureNotifier { get; set; }
public TransactionProcessor(Action<Exception> failureNotifier)
{
this.FailureNotifier = failureNotifier;
}
public void ProcessItems(object[] items)
{
try
{
for(object item in items) { /* do stuff */ }
}
catch(Exception ex)
{
FailureNotifier(ex);
}
}
}
Преимущество этого подхода - более простая библиотека классов: мне не нужен суп из крошечных объектов для реализации единственного метода IFailureNotifier, я просто передаю реализацию напрямую.
Чтобы не сказать, что интерфейсы с одним методом плохи, вы все равно хотите обернуть функцию в интерфейсе, если функция зависит от основного изменяемого состояния. Однако я лично считаю, что большинство преимуществ интерфейсов с одним методом уже обеспечиваются первоклассными функциями.
В некоторых случаях для видимости: вы можете захотеть, чтобы определенные части кода увидели конкретный класс и получили доступ к сеттерам, а некоторые другие - только для геттеров: предоставить им доступ только к интерфейсу.
Это не всегда может быть достигнуто с помощью public/protected/private.
Когда я работаю с кем-то еще в проекте, например, если я делаю внешний интерфейс (например, веб-приложение), а другой человек выполняет всю работу с базой данных, мы начинаем с написания API. Сторона, которая стоит перед мной, касается проблемной области: классы для пользователя, администратора, сотрудника, SKU или что-то еще. Тогда мы можем работать независимо; она реализует все интерфейсы, и я пишу код, который их использует.
Если вы работаете с системой, которая позволяет рефакторинг, вы должны добавлять только интерфейсы, если они необходимы для спецификации (скажем, внешний API) или если вам нужны несколько реализаций. Я считаю тестовые объекты допустимыми интерпретациями, поэтому, если вам нужен интерфейс, чтобы получить его под контролем, это тонкое использование интерфейсов.
Объекты Value должны редко становиться интерфейсами, объекты обслуживания должны часто становиться интерфейсами.
Если у вас есть ссылка на интерфейс для объекта, вы автоматически получаете подсчет ссылок. Таким образом, на самом деле существует общий интерфейс с одной реализацией, когда вы хотите, чтобы объект был очищен автоматически. Счетчик ссылок лучше, чем сбор мусора, поскольку деструктор объекта вызывается, как только последняя ссылка либо выходит за пределы области видимости, либо больше не ссылается на нее.
потому что у вас может быть два или более в будущем
Сложение После прочтения некоторых комментариев: Объект mocking: это означало бы, что у вас будет более одной реализации вашего интерфейса, yoru mock - это еще одна реализация.
Возможность дальнейшего расширения в будущем.
Я предпочитаю не определять интерфейс, если мне это не нужно. Интерфейс IMO является одним из самых злоупотребляемых шаблонов проектирования. Я думаю, что дело, о котором вы говорите, связано с вопросом: "Что, если... понадобится в будущем?". Поскольку разработчик еще не гадалка... Я бы придерживался интерфейса "недоиспользования".
Чтобы способствовать ослаблению связи, считается, что лучший дизайн основан на абстрактной зависимости, чем привязка себя к конкретному классу.
Одним из примеров того, где эта практика особенно полезна, является Spring подход на основе прокси-сервера, где ваша единственная реализация находится в разные JVM.
В ряде других ответов упоминается "работа с другими", и это, безусловно, вызывает озабоченность. Проект с 10 программистами должен будет приближаться к вещам иначе, чем проект с одним, и, к сожалению, излагать архитектуру программного обеспечения таким образом, чтобы несколько людей могли вносить свой вклад в это, что-то, что люди, кажется, всегда усваивают трудный путь.
Кто-нибудь может предложить хорошие книги или статьи?
Я часто делаю это и по нескольким причинам:
1) Он позволяет указать, какие функции класса фактически используются вне класса. Это говорит о том, какие функции вы всегда должны поддерживать, пока этот интерфейс остается неизменным. Это позволяет по каким-либо причинам иметь другие общедоступные методы в этом классе (для поддержки других интерфейсов или для других объектов, которые имеют экземпляр объекта и ссылаются на него по фактическому имени класса, а не через интерфейс).
2) Он также обрезает список, предоставляемый через intellisense, только функциям, поддерживаемым интерфейсом.
3) И, как указано выше, это полезно для модульного тестирования и издевательских фреймворков.
Одна ситуация для интерфейса только для одной реализации: RMI. У вас есть объект в одном приложении и будет использовать его из другого приложения через RMI; вам нужен интерфейс. Таким образом, вам даже не нужно включать класс реализации в вызывающее приложение, которое может спасти вызывающее приложение от включения пута J2EE-материала или внешних библиотек, которые могут использовать реализация.
Другая причина: сегодня у вас есть только одна реализация, но завтра может быть что-то в JDK или внешняя библиотека, которая может привести к изменению этой реализации (возможно, создание новой). Ваша система может измениться, и вам нужно добавить новую реализацию, сохранив старую версию; некоторое обслуживание и т.д.
Примером того, где может быть смысл создать интерфейс для одного класса, является (как указывали другие), когда мы можем ожидать, что он будет расширен.
Например, если у меня есть инструмент отслеживания, который я создаю для личного использования, это будет использовать SQL-сервер. Я создаю интерфейс для абстракции БД, хотя методы БД содержатся в одном классе. Причина в том, что это упростит работу над расширением приложения для использования других платформ БД.
Я понимаю, что аналогичные типы абстракций уже существуют в .NET framework, но я решил, что это был подходящий пример.
Некоторые люди всегда создают интерфейс, потому что они могут быть сгенерированы автоматически, поэтому они оправдывают создание рабочих интерфейсов, независимо от того, действительно ли они используются или нет.
Например, иногда разработчики оцениваются на каждую строку написанного кода, поэтому имеет смысл использовать интерфейс.
потому что интерфейс лучше всего подходит для окончательного указания типа