Если вы всегда код для интерфейсов в Java
Я понимаю принципы Coding to Interfaces - чтобы отделить реализацию от интерфейса и позволить менять и заменять реализации интерфейса.
Должен ли я кодировать интерфейсы для каждого класса, который я пишу, или это перебор? Я не хочу удваивать количество исходных файлов в проекте, если оно того не стоит.
Какие факторы я могу использовать, чтобы решить, следует ли кодировать по интерфейсу или нет?
6 ответов
В общем, я согласен с другими ответами: используйте интерфейс, когда вы знаете или ожидаете изменения и / или другую реализацию, или идите на тестируемость.
Но не всегда легко узнать заранее, что может измениться в будущем, особенно если вы не так опытны. Я думаю, что решением этой проблемы является рефакторинг: держите его простым (= без интерфейса), пока он не нужен. Когда возникает необходимость, просто сделайте рефакторинг "Представьте / Извлеките интерфейс" (поддерживается почти всеми приличными IDE).
Когда вы делаете это, извлекайте только те методы, которые действительно нужны вызывающим. Не бойтесь извлекать более одного отдельного интерфейса (если не все извлеченные методы действительно последовательны).
Одним из драйверов рефакторинга может быть тестирование: если вы не можете легко что-то тестировать с помощью классов, подумайте только о представлении одного / нескольких интерфейсов. Это также позволит вам использовать макеты, которые могут значительно упростить тестирование во многих случаях.
Редактировать: основываясь на комментарии Тарского, я реализовал еще один важный сценарий / корректировку предыдущих заявлений:
Если вы предоставляете внешний API (для других [под] проектов, или действительно выпускаете его "на волю"), то использование интерфейсов в API (за исключением простых классов значений) почти всегда является хорошей идеей.
Это позволит вам изменить impl, если хотите, не нарушая клиентский код. У вас будут проблемы только в том случае, если вам также придется сменить интерфейс. Не нарушать совместимость будет очень сложно (если не невозможно).
Я использую его, когда хотя бы одно из следующего верно:
1) Я хочу отделить несвязанные модули / классы друг от друга, и я хочу, чтобы это отражалось в зависимостях пакета java.
2) Когда развязка важна с точки зрения зависимостей сборки
3) Когда реализация может изменяться через конфигурацию или динамически во время выполнения (обычно из-за некоторого шаблона проектирования)
4) Когда я хочу, чтобы класс был тестируемым, я делаю его "точки привязки" к интерфейсам внешних ресурсов, чтобы я мог использовать фиктивные реализации.
5) Менее часто: когда я хочу иметь возможность ограничить доступ к некоторому классу. В этом случае мой метод возвращает интерфейс вместо фактического класса, поэтому вызывающий объект знает, что я не ожидаю, что он выполнит другие операции с возвращаемым значением.
Нет, у каждого класса не должно быть интерфейса. Это перебор в квадрате.
Вы используете интерфейс, когда вам нужно абстрагироваться от того, как это делается, и вы уверены, что реализация может измениться.
Посмотрите на API коллекций java.util для хорошего примера. Интерфейс List - это хорошая абстракция для общей идеи List, но вы можете видеть, что существует множество способов его реализации: ArrayList, LinkedList и т. Д.
ОБНОВЛЕНИЕ: А как насчет того случая, когда вы разрабатываете конкретный класс, решаете после того, как у вас есть клиенты, что интерфейс необходим, а затем вы ломаете своих клиентов, добавляя интерфейс? Да, вот что происходит. Как ты можешь знать, чего не знаешь? Никакое программное обеспечение или методология не могут это исправить.
Хорошая новость для вас заключается в том, что извлечение интерфейса из конкретного класса - это простой рефакторинг для вас и ваших клиентов. IDE регулярно занимаются такими вещами. Вы и ваши клиенты должны ими воспользоваться.
Я бы сказал, что слои, такие как сервисы и постоянство, всегда должны иметь интерфейсы. Все, что может быть прокси, должно иметь интерфейс. Если вы делаете Spring AOP, все, что вы хотите украсить аспектом, должно иметь интерфейс.
Объекты модели или значения, такие как Person, Address или Phone, не должны иметь интерфейса. Неизменяемый объект значения не должен иметь интерфейса.
Остальные попадают в серые зоны. Используйте свое лучшее суждение.
Короче говоря, нет. Вы являетесь создателем приложения, поэтому у вас есть хорошее представление о том, что может измениться. На мой взгляд, некоторые из книг по шаблонам дизайна немного сумасшедшие, так как они, кажется, всегда подталкивают разработку к интерфейсам, а не реализацию, несмотря ни на что. Иногда это просто излишне. Для моего последнего приложения (небольшого размера) у меня есть интерфейс базы данных, потому что я думаю, что есть шанс, что позже я смогу перенести его в Интернет и переключиться на MySQL. Конечно, в действительности, было бы легко создать интерфейс базы данных в будущем от моего существующего класса и просто добавить "реализацию myDbInterface" позже с несколькими небольшими изменениями. Это работает, потому что я единственный разработчик, хотя. Для большой команды или другого типа продукта это может быть другой ответ. Просто используйте свое лучшее суждение (после прочтения всех хороших ответов SO, конечно).
Да, используйте интерфейс, когда вы ожидаете, что поведение изменится., В будущем.
Или ваш класс, который может обрабатывать изменения в понятной форме.
например.
public static void main(String s[])
{
Employee e1=new Employee(new Salesman());
Employee e2=new Employee(new Manager());
system.out.println(e1.performaction());
system.out.println(e1.performaction());
}
Class Employee
{
//which changes frequently
Authority a;
public Employee(Authority a)
{
this.a=a;
}
public String performAction()
{
a.action();
}
}
// ------------------------------------------------ ------------------
interface Authority
{
public String action();
}
class Manager implements Authority
{
public String action()
{
returns "I manages People";
}
class SalesMan implements Authority
{
public String action()
{
returns "I Sell things Company makes ";
}
}
Мне нравится думать, что каждый использует интерфейсы, когда важна возможность подключения и потенциал для другого поведения. Услуги идеально подходят. Кто-то может захотеть реализацию по умолчанию, но подделка (насмешка) и т. Д. - это другое. Это, очевидно, означает, что должен быть intf, так как всегда будет как минимум 2 формы.
Типы значений с другой стороны должны быть конечными классами. Они никогда не должны быть расширены. Типы значений включают что-то вроде Suburb, MimeType. Если есть вариации этих, используйте состав. В качестве цели я стараюсь, чтобы в моих типах значений было мало поведения. Проверка простого типа, который он переносит и т. Д., Вероятно, самая сложная вещь, которую он делает. Например, класс Color, который принимает r/g/b, должен проверять, что каждый компонент находится между 0 и 255, и это так.