Страницы

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

вторник, 26 ноября 2019 г.

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


Ранее никогда не писал собственные интерфейсы для "упрощения жизни". Почему? Чт
в них такого интересного? 

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

Спасибо за замечательные ответы!
    


Ответы

Ответ 1



Лично для меня самым наглядным для понимания сущности интерфейсов являются коллекци и все, что с ними связанно. Скорее всего это так, потому что с ними приходилось чаще всего работать. Одна из главных особенностей ООП - избавление от дублирования логики, за счет правильно архитектурной составляющей. Для данного случая (в общем понимании) и были придуманы интерфейсы и абстрактные классы. Допустим у меня есть сферический в вакууме метод, делающий какую-то работу с коллекцией: /*Здесь может быть любой тип коллекции, который реализует соотв. интерфейс. Причем я использую интерфейс самого "низкого" уровня, т.к. он полностью покрывает данный функционал (т.е. проход по элементам в цикле) Также я могу написать любую свою реализацию для этого интерфейса */ 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-объекты вместо настоящих". Поверьте, в перспективе это очень облегчает жизнь.

Ответ 2



Интерфейс предоставляет контракт, который должны выполнять все классы, реализующи этот интерфейс, и является абстракцией, показывающей что объект может делать, но как он этот делает - не важно. На практике это приводит к следующему: При использовании интерфейсов появляется возможность заменять один класс, реализующи интерфейс, на другой класс, реализующий этот же интерфейс, без переписывания всего кода. Например, если методу передаётся Map: public void handle(Map map) { ... } то не придётся менять описание метода, если потребуется использовать TreeMap вместо HashMap. Аналогично если метод возвращает Map: он может начать возвращать TreeMap вместо HashMap и трагедии библейских масштабов при этом не случится, так как код, работающий с возвращенным этим методом значением, имеет дело с Map. Это увеличивает гибкость кода: проще переключаться с одного источника данных на другой и с одной реализации на другую. Также это полезно при тестировании, так как позволяет использовать Mock-объекты вместо настоящих. Если нужно одинаково обрабатывать коллекции элементов (например, ArrayList и Set, возвращаемый методом keySet() у HashMap), то достаточно описать метод так: public void handle(Collection elements) { ... } Использование generics-типа здесь для большей реалистичности. Без использования интерфейса пришлось создавать два метода: public void handle(ArrayList elements) { ... } public void handle(Set elements) { ... } И либо дублировать код, либо, например, во втором методе создавать ArrayList, добавлять в него все элементы из Set и вызывать первый метод. Также использование интерфейсов позволяет объединить разные объекты, реализующи один и тот же интерфейс, в один список и одинаково их обрабатывать: public interface Animal { public void feed(); } public class Dog implements Animal { public void feed() { ... } } public class Cat implements Animal { public void feed() { ... } } List animals = new ArrayList<>(); animals.add(new Cat()); animals.add(new Dog()); for (Animal animal : animals) { animal.feed(); } Без использования интерфейса пришлось бы городить "if-else" (или "switch-case") с отдельной реализацией логики для каждого типа животных.

Ответ 3



Потому что интерфейс - это абстракция, которая говорит о том, что делать, а не ка делать. "упрощение жизни" заключается в том, что интерфейс задает единообразное ожидаемое поведение, и где бы вы его не встретили (если конечно программист не наплевал на контракт интерфейса). Вообще, чтобы понять, что я тут написал, вам бы почитать книги по ООП.

Ответ 4



Интерфейсы и в правду упрощают жизнь .Допустим в android для навигации gps Googl api использую интерфейс, для слушателя используют интерфейс (Интерфейсы используютс почти везде даже в потоках )А начиная с Java-8 интерфейсы реализуют и тело, так чт можно уже писать не только условие но и тело интерфейса .Но дело каждого использовать их или нет .Не всегда программист обязан реализовывать свои интерфейсы.Правила "написание хорошего кода" гласит:сперва составь интерфейсы абстракцию потом реализуй))

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

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