Из лекции:
Раньше мы уже рассказывали, что если вы хотите разрешить вызывать методы своего класса из других классов, то их нужно пометить ключевым словом public. Если же хотите, чтобы какие-то методы можно было вызывать только из этого же класса, их нужно помечать ключевым словом private. Другими словами, мы делим методы класса на две категории: «для всех» и «только для своих».
С помощью интерфейсов это деление можно усилить еще больше. Мы сделаем специальный «класс для всех», и второй «класс для своих», который унаследуем от первого.
Мы разбили наш класс на два: интерфейс и класс, унаследованный от интерфейса. И в чем тут преимущество?
Один и тот же интерфейс могут реализовывать (наследовать) различные классы. И у каждого может быть свое поведение. Так же, как ArrayList и LinkedList — это две различные реализации интерфейса List.
Таким образом, мы скрываем не только различные реализации, но и сам класс, который ее содержит (везде в коде может фигурировать только интерфейс). Это позволяет очень гибко, прямо в процессе исполнения программы, подменять одни объекты на другие, меняя поведение объекта скрытно от всех классов, которые его используют.
______________________________________________________________________________________________________
Не пойму, в чем сокрытие? Сокрытие, как я понимаю, это когда мы сделали сеттер приватным, а тут публичный геттер в интерфейсе переопределен в публичный геттер в классе. Затем в примере с интерфейсом, была создана переменная интерфеса с объектом типа Студентимпл и вызван переопределенный метод. Ну, также можно создать переменную Студентимпл с объектом типа Студентимпл и вызвать этот же метод. Что тогда значит строчка "Таким образом, мы скрываем не только различные реализации, но и сам класс, который ее содержит (везде в коде может фигурировать только интерфейс)" Имеется ввиду, что используя метод геттера, мы на самом деле будем использовать переопределенный метод интерфейса? Ну, окей, и что?
И последнее предложение про подмену объектов в рантайме и изменение поведения объектов скрытно, вообще не пойму. Просто какая-то каша в голове...
Help 😀
fFamous
51 уровень
Не пойму пример с интерфейсом
Комментарии (11)
- популярные
- новые
- старые
Для того, чтобы оставить комментарий Вы должны авторизоваться
fog
15 августа 2021, 11:47
Пока писал ответ, текста набралось на небольшую статью.
+1
fFamous
15 августа 2021, 11:56
Сейчас заценим. Спасибо 🤜
+1
Илья Senior Developer / Mentor
15 августа 2021, 10:39
"Таким образом, мы скрываем не только различные реализации, но и сам класс, который ее содержит"
Пример кода на скрине никак не объясняет эту фразу.
Например, мы разрабатываем приложение "адресная книга". Создаем класс:
максимально общий интерфейс Collection.
Однако теперь нам нужно выбрать реализацию этого интерфейса.
Новичок делает так:
Теперь классу AddressBook известно, какую реализацию коллекции адресов он использует - это ArrayList.
Однако при использовании приложения, в какой-то момент, адресов становится слишком много и приложение начинает тормозить. Решение - подобрать вместо ArrayList более эффективную структуру данных для большого количества элементов (для примера пусть будет TreeSet).
Новичок залезет в класс AddressBook и поменяет "new ArrayList<>()" на "new TreeSet();"
Но тут возникают проблемы:
А если этот класс будет лежать в отдельном jar-нике, который будет подключать другая команда для разработки основного приложения? Придется пересобирать jar по их просьбе.
А если этим классом будет пользоваться не одна команда, а несколько и у каждого будет потребность подставить удобную им реализацию Collection? Вполне возможно, что некоторые захотят даже использовать реализацию не из jdk. Пересобирать jar для каждого персонально у нас не будет возможности.
Для хранения адресов мы будем использовать +1
Илья Senior Developer / Mentor
15 августа 2021, 10:40
Наша задача добиться того, чтобы разработчик использующий класс AddressBook сам имел возможность подставлять новую реализацию.
Сейчас наш AddressBook не достаточно гибкий для этого. А все потому, что он знает о конкретной реализации интерфейса Collection. Он к ней привязан, она в нем захардкожена, он сильно связан с ней (high coupling).
Правильный способ - передавать ее через конструктор:
SOLID, а именно Open–closed principle.
Сам принцип гласит: "Программные сущности (классы, модули, функции) должны быть открыты для расширения, но не для модификации".
AddressBook закрыт для модификации т.к. он может лежать в стороннем jar. Но в тоже время он всегда будет открыт для расширения за счет того, что мы можем через конструктор подставлять разные структуры данных, не изменяя при этом кода класса.
Где-то в стороннем коде:
Теперь мы скрыли от AddressBook различные реализации коллекции адресов и благодаря этому повысили его гибкость.
Кстати, именно об этом говорит один из принципов +1
hidden #2460969
15 августа 2021, 10:48
Кстати, именно об этом говорит один из принципов SOLID, а именно Open–closed principle.
вообще из SOLID для ситуации работы с интерфейсами (о чем топикстартер спрашивал) подходит принцип D Dependency Inversion Principle
0
Илья Senior Developer / Mentor
15 августа 2021, 11:02
Верно, эти принципы часто связаны и сосуществуют вместе.
На самом деле, тут помимо OCP и DIP есть еще и SRP.
0
fFamous
15 августа 2021, 11:30
Спасибо большое за ответ. Про джарник пока непонятно(ушел читать), но остальное понял :) Опять же, если мы используем просто какой-то класс, а не интерфейс, то это тоже скрытая реализация класса, ведь в конструктор можно будет подставить наследников этого класса.
0
hidden #2460969
15 августа 2021, 08:16
Не пойму, в чем сокрытие?
в том, что ты можешь изменить логику работы твоего класса, который реализует интерфейс, и другая программа не будет об этом знать, так как она работает через интерфейс. всё будет работать как и раньше. то есть поведение объекта изменилось скрытно от всех.
как пример, у нас есть интерфейс работы с БД, и метод find(), так вот остальные классы работают через этот интерфейс и метод find(), они знают что вызвав метод, будет осуществляться поиск в БД.
но как он будет реализован, какими средствами, в какой БД, этого они не знают, и не надо им этого знать. Сегодня мы работаем с H2, на этапе тестирования и разработки, завтра переведем на MySQL, послезавтра на PostgreSQL.
Ну, также можно создать переменную Студентимпл с объектом типа Студентимпл и вызвать этот же метод
этого надо стараться избегать.
0
fFamous
15 августа 2021, 09:04
Спасибо. Вроде смысл понятен, но представить это в голове нормально как-то не выходит. В примере с БД, классы, которые работают с интерфейсом, ты имеешь ввиду, что они его реализуют и не переопределяют? Вот смотри, у нас есть например обычный класс предок, который работает с БД и у него есть метод find(), классы наследники также могут переопределить этот метод, но у них он есть и по умолчанию. Они знают, что вызвав метод, будет поиск по БД. Хорошо, через неделю изменили работу метода, сменились средства, сменилась БД, ну и что, нам же главное - поиск.
0
hidden #2460969
15 августа 2021, 09:21
причем тут предок и наследник?
классы, которые работают с интерфейсом, ты имеешь ввиду, что они его реализуют и не переопределяют?
я имею ввиду вот что:
таким образом мы сделаем вот так, и всё будет работать точно так же
а теперь представь что там может быть сложный класс, с внутренней архитектурой и логикой. а не привычные ArrayList/LinkedList
0
fFamous
15 августа 2021, 09:43
Хм, то есть вся суть в том, что у программы, которая работает с нашим кодом, прописана только логика работы именно с интерфейсом, а что там дальше ее мало волнует. То есть в принципе, она могла бы работать и отдельно с переменной LinkedList<>() или ArrayList<>(), но это больше кода под каждый класс надо писать и нафиг не нужно. Теперь в принципе стало приходить понимание, зачем вообще составлять такие конструкции. Спасибо большое 🤜
0