Страницы

Поиск по вопросам

вторник, 2 октября 2018 г.

Почему интерфейсы упрощают жизнь?

Ранее никогда не писал собственные интерфейсы для "упрощения жизни". Почему? Что в них такого интересного?
В комментариях попросили добавить в вопрос мои знания и область работы. Пишу android приложения и... Все. Об интерфейсах не знаю ничего, только то, что можно класс от интерфейса наследовать и переопределить методы. Но зачем? Смысл интерфейсов?
Спасибо за замечательные ответы!


Ответ

Лично для меня самым наглядным для понимания сущности интерфейсов являются коллекции и все, что с ними связанно. Скорее всего это так, потому что с ними приходилось чаще всего работать.
Одна из главных особенностей ООП - избавление от дублирования логики, за счет правильной архитектурной составляющей. Для данного случая (в общем понимании) и были придуманы интерфейсы и абстрактные классы.
Допустим у меня есть сферический в вакууме метод, делающий какую-то работу с коллекцией:
/*Здесь может быть любой тип коллекции, который реализует соотв. интерфейс. Причем я использую интерфейс самого "низкого" уровня, т.к. он полностью покрывает данный функционал (т.е. проход по элементам в цикле) Также я могу написать любую свою реализацию для этого интерфейса */ public void showCollectionAtConsole(Iterable col){ col.forEach(object -> System.out.println(object)); }
Удобство здесь в том, что только в самой Java довольно много реализаций данного интерфейса, (подробнее) и я могу использовать данную логику для каждой из них.
Теперь у меня есть другой метод, реализующий какой-то функционал:
public boolean removeOrAddMyObject (Collection col, MyObject object){ //Если удалил объект - true, иначе - false boolean isRemove = true;
if (col!=null){ if (col.isEmpty() || !col.remove(object)) { col.add(object); isRemove = false; } }else throw new IllegalStateException("Коллекция = null");
return isRemove; }
Здесь мне приходится использовать три метода коллекции: isEmpty(), add(), remove(). Однако все эти методы описаны в интерфейсе Collection и нет нужды в качестве аргумента передавать более "высокий" интерфейс или класс с реализацией. Как я уже писал выше, данное решение позволит мне с чистой совестью переиспользовать неоднократно данный метод для других реализаций.
Также, как мне кажется, отличным примером использования интерфейсов являются Callback'и:
public final class MyClass {
static class MyObject { int id; String name;
public MyObject(int id, String name) { this.id = id; this.name = name; }
public void setId(int id) { this.id = id; } public void setName(String name){ this.name = name; }
@Override public String toString(){ return "My Object: id = "+id+", name = "+name; } }
//Интерфейс коллбэка interface MyCallback { void doSome(T myObject); }
static void doSomeAwesome(MyObject object, MyCallback callback) { System.out.println(object); callback.doSome(object); System.out.println(object); }
public static void main(String[] args) {
MyObject myObject = new MyObject(1,"someName");
MyCallback callback = new MyCallback() { @Override public void doSome(MyObject myObject) { myObject.setId(2); myObject.setName("someOtherName"); } };
doSomeAwesome(myObject,callback); } }
Суть сводится к тому, что в интерфейсе коллбэка вы явным образом определяете дженерик (если хотите передавать в метод параметры и/или получать из метода объекты) и описываете контракт. В моем случае это один метод, принимающий на вход аргумент. Теперь, я могу реализовать метод коллбэка на свое усмотрение, и таким образом расширить использование метода doSomeAwesome() дополнительной логикой.
Если выполнить код на выходе будет:
My Object: id = 1, name = someName My Object: id = 2, name = someOtherName
UPD: Также следует понимать, что есть и другие контексты для использования интерфейсов:
контекст множественного наследования (в Java нельзя наследоваться от более 1 класса, но можно имплементировать более 1 интерфейса) интерфейс как прослойка между взаимодействующими сущностями. Т.е. есть два модуля в программе которые выполняют разные функции и при этом взаимодействуют друг с другом. В идеальной ситуации оба модуля должны представлять друг для друга "черный ящик" с необходимым минимумом методов, описываемых интерфейсами. Таким образом куда проще избежать сложностей при последующей отладке кода. контекст проектирования архитектуры приложения. Как правило, на начальном этапе проектирования архитектуры будущего приложения, очень удобно "прикидывать" взаимодействие между сущностями с помощью интерфейсов. Это как будто черновой вариант, который не требует "заморачиваться" с реализацией, и позволяет относительно безболезненно модифицировать архитектуру на ранних этапах. контекст тестирования. Как уже упоминали в одном из ответов, использование интерфейсов позволяет: "использовать Mock-объекты вместо настоящих". Поверьте, в перспективе это очень облегчает жизнь.

Комментариев нет:

Отправить комментарий