Ооп наследование инкапсуляция полиморфизм. Реализация полиморфизма. Полиморфные функции и методы. Реальный полиморфизм ООП, пример

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

Собственно, представим себе рядом стакан, кружку, чайник, кофемашину, велосипед и скейт. Что между ними всеми общего? Ну как минимум то, что они есть. То есть это - объекты, которые были созданы. Но как они были созданы? Скорее всего на заводе производителя по чертежам. Ок, чертежём назовём конструктор. Ну а класс? А что это такое? А его нет в нашей вселенной - эта сущность есть абстракция, что живёт лишь в наших мыслях. В реальном мире её нет и никогда не будет, такова уж физика - ей по барабану, что птицы и млекопитающие имеют дальних родственников - она лишь обеспечивает возможность естесственного отбора. А уж родственников друг другу находим мы, люди.

С объектами и классами разобрались, а что же там с нашими стаканами и велосипедами. Мы уже поняли, что всё это объект, то есть грубо можно все объекты наследовать от какого-нибудь суперпредка, суперкласса, что и реализовано в некоторых языках. Но что другого общего между скейтом и стаканом, например? Конечно, можно углубляться и считать, что они все из молекул, и они все из твёрдых веществ. Однако это всё бред и СПГС , так что ответ прост - да ничего. То есть это совершенно разные объекты с совершенно разным функционалом. Более того - естесственно компьютерные модели и иерархии будут сильно отличатся от физик и химий. И это нормально, вопрос об адекватностях моделей ставиться лишь когда модель неадекватна, а до тех пор пилить можно что угодно, лишь бы работало.

Вот. У нас есть супер-предок Object, от которого дефолтно наследуются все объекты. Допустим, то что объекты состоят из атомов и есть то, что наследуют все объекты. Но все дополнения и правки - полиморфизм. Так, из атомов мы слепили колёса и приделали на доску - ок, это скейт. На него можно встать и катиться, а сильно извернувшись и полетать в трёх метрах над землёй, прямо таки излучая своё яркое эго. В то время как стакан - это мы слепили из атомов плотную ёмкость, из которой вода не выливается под действием силы тяжести. И прямое применение стакана - налив воды опрокинуть его над ртом, чтобы вода вытекла прямо в желудок. Так делают настоящие пацаны, не заботясь об икоте или страхе утонуть, так что вот - полиморфизм.

Однако что с остальным? У нас ещё абстракция, инкапсуляция и наследование. Ок, начнём с наследования, так оно наиболее близко. Вот что у нас общего между стаканом и кружкой? Ну в оба можно налить воду, но у кружки есть ручка чтобы держаться. То есть можно придумать некий общий класс - ёмкость. Однако что это за класс? Можно например за этот класс взять стакан, тогда все ёмкости по дефолту стаканы, а всё остальное - видоизменённые стаканы. Но кому-то больше нравяться кувшины, например некоторые чики насят их на голове, считая что это удобно. Ну и пусть носят, но как-то же решить надо, что главнее и идеальнее. Так вот - недостяжимый идеал и есть главный - это называется абстрактный класс. То есть ёмкость, что невозможно создать, для которого нет полного чертежа. А все чертежи, что дополнили до полного - есть наследованные классы от класса ёмкость.

Тут мы подошли к абстракции. Вот такое иерархическое наследование приводит нас к, возможно главной, идее ООП. Вот мы взяли и выделили всё, куда можно налить воду в отдельный класс, нарисовали общий чертёж, но специально не доделали его, оставив зазор для будущих творцов, и назвали чертёж - ёмкость. Тысячи лет изобретатили всех миров создают свои ёмкости, одна лучше другой. Для разных людей - по разному, конечно. Но каждый раз группировать молекулы стекла определённым образом - непростая задача. Поэтому ремесленники пошли на хитрость, они создали тайный совет ремесленников мира и решили делиться друг с другом своими наработками. То есть создавать мелкие чертежи и объявлять классом, например, извлистой ручки в форме ленты Мёбиуса, например. Возможно такая ручка удобно только инопланетным существам, но чертёж создан и к нему можно ссылаться при создании своего чертежа. Таким образом мы абстрагируемся от низкоуровневой задачи "формирования ёмкостей посредством перемещения молекул" к "конструированию ёмкости посредством совмещения деталей, элементов". Это и есть абстракция.

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

Таким образом, абстракция невозможна без инкапсуляции и наследовании, как невозможен полиморфизм без, собственно, наследования. Ну а полиморфизм невозможен ещё и без инкапсуляции, которая банально бесполезна без наследования и полиморфизма. Вот такие тут треугольники с пирогами. Жаль только про пирог наврали. И про день рожденье.

классы определяется не наборами атрибутов, а семантикой. Так, например, объекты "конюшня" и "лошадь" могут иметь одинаковые атрибуты: цена и возраст. При этом они могут относиться к одному классу , если рассматриваются в задаче просто как товар , либо к разным классам , если в рамках поставленной задачи будут использоваться по -разному, т.е. над ними будут совершаться различные действия.

Объединение объектов в классы позволяет рассмотреть задачу в более общей постановке. Класс имеет имя (например, "лошадь"), которое относится ко всем объектам этого класса . Кроме того, в классе вводятся имена атрибутов, которые определены для объектов . В этом смысле описание класса аналогично описанию типа структуры или записи ( record ), широко применяющихся в процедурном программировании; при этом каждый объект имеет тот же смысл, что и экземпляр структуры ( переменная или константа соответствующего типа).

Формально класс - это шаблон поведения объектов определенного типа с заданными параметрами, определяющими состояние . Все экземпляры одного класса (объекты , порожденные от одного класса ) имеют один и тот же набор свойств и общее поведение , то есть одинаково реагируют на одинаковые сообщения.

В соответствии с UML ( Unified Modelling Language - унифицированный язык моделирования ), класс имеет следующее графическое представление .

Класс изображается в виде прямоугольника, состоящего из трех частей. В верхней части помещается название класса , в средней - свойства объектов класса , в нижней - действия, которые можно выполнять с объектами данного класса (методы).

Каждый класс также может иметь специальные методы, которые автоматически вызываются при создании и уничтожении объектов этого класса :

  • конструктор (constructor) - выполняется при создании объектов ;
  • деструктор ( destructor ) - выполняется при уничтожении объектов .

Обычно конструктор и деструктор имеют специальный синтаксис , который может отличаться от синтаксиса, используемого для написания обычных методов класса .

Инкапсуляция

Инкапсуляция (encapsulation) - это сокрытие реализации класса и отделение его внутреннего представления от внешнего (интерфейса). При использовании объектно-ориентированного подхода не принято применять прямой доступ к свойствам какого-либо класса из методов других классов . Для доступа к свойствам класса принято задействовать специальные методы этого класса для получения и изменения его свойств.

Внутри объекта данные и методы могут обладать различной степенью открытости (или доступности). Степени доступности, принятые в языке Java, подробно будут рассмотрены в лекции 6. Они позволяют более тонко управлять свойством инкапсуляции .

Открытые члены класса составляют внешний интерфейс объекта . Это та функциональность, которая доступна другим классам . Закрытыми обычно объявляются все свойства класса , а также вспомогательные методы, которые являются деталями реализации и от которых не должны зависеть другие части системы.

Благодаря сокрытию реализации за внешним интерфейсом класса можно менять внутреннюю логику отдельного класса , не меняя код остальных компонентов системы. Это свойство называется модульность .

Обеспечение доступа к свойствам класса только через его методы также дает ряд преимуществ. Во-первых, так гораздо проще контролировать корректные значения полей, ведь прямое обращение к свойствам отслеживать невозможно, а значит, им могут присвоить некорректные значения.

Во-вторых, не составит труда изменить способ хранения данных. Если информация станет храниться не в памяти, а в долговременном хранилище, таком как файловая система или база данных, потребуется изменить лишь ряд методов одного класса , а не вводить эту функциональность во все части системы.

Наконец, программный код, написанный с использованием данного принципа, легче отлаживать. Для того чтобы узнать, кто и когда изменил свойство интересующего нас объекта , достаточно добавить вывод отладочной информации в тот метод объекта , посредством которого осуществляется доступ к свойству этого объекта . При использовании прямого доступа к свойствам объектов программисту пришлось бы добавлять вывод отладочной информации во все участки кода, где используется интересующий нас объект .

Наследование

Наследование (inheritance) - это отношение между классами , при котором класс использует структуру или поведение другого класса (одиночное наследование ), или других (множественное наследование ) классов . Наследование вводит иерархию "общее/частное", в которой подкласс наследует от одного или нескольких более общих суперклассов . Подклассы обычно дополняют или переопределяют унаследованную структуру и поведение .

В качестве примера можно рассмотреть задачу, в которой необходимо реализовать классы "Легковой автомобиль" и "Грузовой автомобиль". Очевидно, эти два класса имеют общую функциональность. Так, оба они имеют 4 колеса, двигатель, могут перемещаться и т.д. Всеми этими свойствами обладает любой автомобиль, независимо от того, грузовой он или легковой, 5- или 12-местный. Разумно вынести эти общие свойства и функциональность в отдельный класс , например, "Автомобиль" и наследовать от него классы "Легковой автомобиль" и "Грузовой автомобиль", чтобы избежать повторного написания одного и того же кода в разных классах .


Отношение обобщения обозначается сплошной линией с треугольной стрелкой на конце. Стрелка указывает на более общий класс ( класс-предок или суперкласс ), а ее отсутствие - на более специальный класс ( класс-потомок или подкласс ).

Использование наследования способствует уменьшению количества кода, созданного для описания схожих сущностей, а также способствует написанию более эффективного и гибкого кода.

В рассмотренном примере применено одиночное наследование . Некоторый класс также может наследовать свойства и поведение сразу нескольких классов . Наиболее популярным примером применения множественного наследования является проектирование системы учета товаров в зоомагазине.

Все животные в зоомагазине являются наследниками класса "Животное", а также наследниками класса "Товар". Т.е. все они имеют возраст, нуждаются в пище и воде и в то же время имеют цену и могут быть проданы.

Множественное наследование на диаграмме изображается точно так же, как одиночное, за исключением того, что линии наследования соединяют класс-потомок сразу с несколькими суперклассами .

Не все объектно-ориентированные языки программирования содержат языковые конструкции для описания множественного наследования .

В языке Java множественное наследование имеет ограниченную поддержку через интерфейсы и будет рассмотрено в лекции 8.

Полиморфизм

Полиморфизм является одним из фундаментальных понятий в объектно-ориентированном программировании наряду с наследованием и инкапсуляцией . Слово " полиморфизм " греческого происхождения и означает "имеющий много форм". Чтобы понять, что оно означает применительно к объектно-ориентированному программированию , рассмотрим пример.

Предположим, мы хотим создать векторный графический редактор, в котором нам нужно описать в виде классов набор графических примитивов - Point , Line , Circle , Box и т.д. У каждого из этих классов определим метод draw для отображения соответствующего примитива на экране.

Очевидно, придется написать код, который при необходимости отобразить рисунок, будет последовательно перебирать все примитивы, на момент отрисовки находящиеся на экране, и вызывать метод draw у каждого из них. Человек, не знакомый с полиморфизмом , вероятнее всего, создаст несколько массивов (отдельный массив для каждого типа примитивов) и напишет код, который последовательно переберет элементы из каждого массива и вызовет у каждого элемента метод draw . В результате получится примерно следующий код:

... //создание пустого массива, который может // содержать объекты Point с максимальным // объемом 1000 Point p = new Point; Line l = new Line; Circle c = new Circle; Box b = new Box; ... // предположим, в этом месте происходит // заполнение всех массивов соответствующими // объектами... for(int i = 0; i < p.length;i++) { //цикл с перебором всех ячеек массива. //вызов метода draw() в случае, // если ячейка не пустая. if(p[i]!=null) p[i].draw(); } for(int i = 0; i < l.length;i++) { if(l[i]!=null) l[i].draw(); } for(int i = 0; i < c.length;i++) { if(c[i]!=null) c[i].draw(); } for(int i = 0; i < b.length;i++) { if(b[i]!=null) b[i].draw(); } ...

Недостатком написанного выше кода является дублирование практически идентичного кода для отображения каждого типа примитивов. Также неудобно то, что при дальнейшей модернизации нашего графического редактора и добавлении возможности рисовать новые типы графических примитивов, например Text , Star и т.д., при таком подходе придется менять существующий код и добавлять в него определения новых массивов, а также обработку содержащихся в них элементов.

Используя полиморфизм , мы можем значительно упростить реализацию подобной функциональности. Прежде всего, создадим общий родительский

JAVA основывается на концепциях объектно-ориентированного программирования, что позволяет перейти на более высокий уровень абстракции, чтобы разрешить любую проблему реалистичным путем. Объектно-ориентированный подход концептуализирует решение проблемы в плоскости объектов реального мира, которые легче повторно использовать в приложении. Например, Chair (стул), Fan (вентилятор), Dog (Собака), Computer (компьютер) и так далее. В JAVA класс представляет собой макет, шаблон или прототип, который определяет общее поведение объекта данного типа. Экземпляр - это отдельная реализация класса, и все экзепляры класса имеют одинаковые свойства, которые описаны в определении класса. Например, вы можете опрделить класс с именем House (дом) с количеством комнат в качестве атрибута и создать экземпляры класса, такие как дом с двумя комнатами, дом с тремя комнатами и так далее. Преимущества: Ниже перечислены некоторые плюсы объектно-ориентированной разработки программного обеспечения (ПО).

  • Снижение затрат на поддержку ПО, в основном за счет того, что она осуществляется модульно.
  • Усовершенствованное повторное использование кода благодаря таким качествам, как наследование, и, как результат, более быстрая разработка ПО.
  • Повышенные надежность и гибкость кода.
  • Легкость понимания вследствие моделирования реального мира.
  • Лучшая абстракция на уровне объекта.
  • Уменьшение сложности перехода от одной фазы разработки к другой.
Есть четыре основные характеристики ООП:
  • Инкапсуляция
  • Наследование
  • Полиморфизм
  • Абстракция

Инкапсуляция

Инкапсуляция выступает договором для объекта, что он должен скрыть, а что открыть для доступа другими объектами. В JAVA мы используем модификатор доступа private для того, чтобы скрыть метод и ограничить доступ к переменной из внешнего мира. JAVA также располагает различными модификаторами доступа: public , по умолчанию, protected , private , которые используются для ограничения видимости на разных уровнях. Но конечной целью является инкапсуляция тех вещей, которые не должны быть изменены. Лучше всего работает подход, при котором, у класса должна быть только одна причина для изменения, и инкапсулирование воплощает в реальность проектирование этой “одной причины”. Правильным в инкапсуляции считается сокрытие часто изменяющихся вещей во избежание повреждения других классов. Преимущества: Ниже представлены некоторые преимущества инкапсуляции:
  • Мы можем защитить внутреннее состояние объекта с помощью сокрытия его атрибутов.
  • Это улучшает модульное построение кода, так как предотвращает взаимодействие объектов неожиданными способами.
  • Повышается практичность кода.
  • Это поддерживает договорные отношения конкретного объекта.
  • Инкапсуляция облегчает поддержку ПО.
  • Изменения в коде могут производиться независимо друг от друга.

Полиморфизм

Полиморфизм в программировании - это способность предоставлять один и тот же интерфейс для различных базовых форм (типов данных). Это означает, что классы, имеющие различную функциональность, совместно используют один и тот же интерфейс и могут быть динамически вызваны передачей параметров по ссылке. Классический пример - это класс Shape (фигура) и все классы, наследуемые от него: square (квадрат), circle (круг), dodecahedron (додекаэдр), irregular polygon (неправильный многоугольник), splat (клякса) и так далее. В этом примере каждый класс будет иметь свой собственный метод Draw() и клиентский код может просто делать: Shape shape = new Shape () ; Shape.area() чтобы получить корректное поведение любой фигуры Красота полиморфизма заключается в том, что код, работая с различными классами, не должен знать, какой класс он использует, так как все они работают по одному принципу. Процесс, применяемый объектно-ориентированными языками программирования для реализации динамического полиморфизма, называется динамическим связыванием. Примечание: Полиморфизм - это способность выбирать более конкретные методы для исполнения в зависимости от объекта. Полиморфизм осуществляется тогда, когда не задействованы абстракные классы. Преимущества:
  • Создание повторно используемого кода. То есть, как только класс создан, реализован и протестирован, он может свободно использоваться без заботы о том, что конкретно в нем написано.
  • Это обеспечивает более универсальный и слабосвязанный код.
  • Понижается время компиляции, что ускоряет разработку.
  • Динамическое связывание.
  • Один и тот же интерфейс может быть использован для создания методов с разными реализациями.
  • Вся реализация может быть заменена с помощью использования одинаковых сигнатур метода.
Переопределение методов как часть полиморфизма. Переопределение взаимодействует с двумя методами: методом родительского класса и методом производного класса. Эти методы имеют одинкавые имя и сигнатуры. Переопределение позволяет вам производить одну и ту же оперецию различными путями для разных типов объектов. Например: while (it. hasNext () ) { Shape s = (Shape) it. next () ; totalArea += s. area (dim) ; //будет применен полиморфизм и вызван нужный метод для каждого объекта. } Перезагрузка методов или ad-hoc полиморфизм или статический полиморфизм Перезагрузка взаимодействует с несколькими методами одного класса, которые одинаково названы, но имеют разные сигнатуры методов. Перезагрузка позволяет вам описать одну и ту же операцию различными путями для разных данных. Иногда ее называют статическим полиморфизмом, но фактически полиморфизмом она не является. Это ничто иное, как просто наличие двух методов с одинаковыми именами, но разным списком аргументов. Перезагрузка не имеет ничего общего с наследованием и полиморфизмом. И перезагруженный метод совсем не то же самое, что переопределенный метод. Параметрический полиморфизм через обобщение в JAVA При объявлении класса поле имени может ассоциироваться с различными типами, а имя метода может ассоциироваться с различными параметрами и возвращаемыми типами. JAVA поддерживает параметрический полиморфизм, применяя обобщение (дженерики). List< String> list = new ArrayList < String> () ; Почему мы не можем переопределить статический метод в JAVA? Переопределение зависит от наличия экземпляра класса. Идея полиморфизма состоит в том, что вы можете создать подкласс, и объекты, реализуемые теми подклассами, будут вести себя по-другому с теми же методами родителького класса (переопределенными в подклассах). Статический метод не ассоциируется ни к каким экземпляром класса, таким образом, сама концепция переопределения не может быть применена. Создателями JAVA руководили два соображения, которые повлияли на такой подход. Во-первых, это проблемы исполнения кода: лилось очень много критики в адрес Smalltalk из-за медленной работы (сборщик мусора и полиморфизм были частью этой проблемы), и в проектировании JAVA старались этого избежать. Вторым соображением было решение, что целевой аудиторией JAVA станут С++ разработчики. То, что статические методы работают именно таким образом, было очень знакомо C++ программистам, а так же ускоряло работу, так как не было необходимости проходить вверх по иерархии классов, чтобы выяснить, какой метод вызывать. Вы идете прямо к классу и вызываете конкретный метод.

Наследование

Наследование - это включение поведения (т.е. методов) и состояния (т.е. переменных) базового класса в производный класс, таким образом они становятся доступны в этом производном классе. Главное преимущество наследования в том, что оно обеспечивает формальный механизм повторного использования кода и избегает дублирования. Унаследованный класс расширяет функциональность приложения благодаря копированию поведения родительского класса и добавлению новых функций. Это делает код сильно связанным. Если вы захотите изменить суперкласс, вам придется знать все детали подклассов, чтобы не разрушить код. Наследование - это форма повторного использования программного обеспечения, когда из уже существующего класса (суперкласса) создается новый класс (подкласс), который расширяет свою функциональность и при этом использует некоторые свойства суперкласса. Так что, если у вас есть класс-родитель, а потом появляется класс-наследник, то наследник наследует все вещи, которыми обладает родитель. Преимущества:
  • Усовершенствованное повторное использование кода.
  • Устанавливается логическое отношение «is a» (является кем-то, чем-то). Например: Dog is an animal . (Собака является животным).
  • Модуляризация кода.
  • Исключаются повторения.
Недостаток:
  • Сильная связанность: подкласс зависит от реализации родительского класса, что делает код сильно связанным.
Что еще почитать:

Абстракция

Абстракция означает разработку классов исходя из их интерфейсов и функциональности, не принимая во внимание реализацию деталей. Абстрактный класс представляет собой интерфейсы без включения фактической реализации. Он отличает реализацию объекта от его поведения. Абстракция упрощает код, скрывая несущественные детали. Преимущества:
  • Применяя абстракцию, мы можем отделить то, что может быть сгруппировано по какому-либо типу.
  • Часто изменяемые свойства и методы могут быть сгруппированы в отдельный тип, таким образом основной тип не будет подвергаться изменениям. Это усиливает принцип ООП: «Код должен быть открытым для Расширения, но закрытым для Изменений» .
  • Абстракция упрощает представление доменных моделей.
Отличие между абстракцией и инкапсуляцией Инкапсуляция - это стратегия, используемая как часть абстракции. Инкапсуляция относится к структуре объекта: объекты инкапсулируют свои свойства и скрывают их от доступа извне. Пользователи класса взаимодействуют с ним с помощью его методов, но не имеют доступа напрямую к структуре класса. Таким образом класс абстрагирует детали реализации, относящиеся к его строению. Абстракция является более общим термином. Она также может достигаться среди прочего с помощью подклассов. Например, класс List (список) в стандартной библиотеке является абстракцией для последовательности элементов, проиндексированных согласно их места в списке. Конкретными примерами списка List являются ArrayList или LinkedList . Код, который взаимодействует со списком List абстрагируется от деталей, какой именно список он использует. Часто абстракция невозможна без сокрытия основного состояния с помощью инкапсуляции. Если класс раскрывает свою внутреннюю структуру, он не может изменить свои внутренние операции, а, следовательно, не может абстрагироваться. Что такое абстрактный класс и абстрактный метод? Случается, что во время разработки вы хотите, чтобы базовый класс представлял только интерфейс для его производных классов. То есть вы не хотите, чтобы кто-либо создавал экземпляры базового класса. Вам необходимо использовать интерфейс таким образом, чтобы только приводить объекты к нему (это неявное приведение, которое обеспечивает полиморфное поведение). Это достигается путем создания данного класса абстрактным с помощью ключевого слова abstract . Это накладывает некоторые ограничения, такие как невозможность создавать экземпляры абстрактного класса, при использовании абстрактного класса необходимо реализовывать абстрактные методы. Этим обеспечивается полиморфизм. Абстрактный класс может содержать и абстрактные и конкретные методы. Если хоть один метод в классе объявлен абстрактным, весь класс должен так же быть объявлен абстрактным. Тем не менее, в обратную сторону правило не обязано соблюдаться. Если класс объявлен абстрактным, он может и не содержать абстрактные методы. Метод, который всего лишь определяет свои сигнатуры и не обеспечивает реализацию, называется абстрактным. Фактическая его реализация оставлена его подклассам, которые расширяют абстрактный класс. Абстрактный метод не может быть использован объектом, только другой класс может его расширить. Когда необходимо использовать абстрактный класс? Абстрактные классы позволяют вам определить некоторое поведение по умолчанию и заставить подклассы обеспечить любое конкретное поведение. Например: List (список) является интерфейсом, в свою очередь AbstractList определяет основное поведение Списка, которое может быть использовано как есть или уточнено в подклассе, например, в ArrayList (списочный массив). Что такое интерфейс? В концепции интерфейса лежит абстрактный класс, но интерфейс (определяется ключевым словом interface) шагнул дальше. Он предотвращает вообще любую реализацию метода или функции. Вы можете только объявлять метод или функцию, но не обеспечивать их реализацию. Класс, который реализует данный интерфейс, должен как раз и позаботиться о фактической реализации. Интерфейсы очень полезны и повсеместно используются в ООП. Так как они разделяют сам интерфей и реализацию, они предоставляют много преимуществ своего использования:
  1. Множественное наследование .
  2. Слабая связанность . Происходит абстракция операции, такая как разделение на уровни, а конкретной реализацией может быть что угодно: JDBC, JPA, JTA и т.д.
  3. Программа-интерфейс не реализуется .
  4. Полиморфизм с динамическим связыванием : раскрывается програмный интерфейс объекта без раскрытия его фактической реализации.
  5. Абстрактные уровни , разделение функциональностей.
Разница между интерфейсом и абстрактным классом.
  • Интерфейс - это договорные отношения с классами, которые этот интерфейс реализуют, о том, что реализация происходит путём, обозначенным интерфейсом. Это пустая оболочка с объявленными методами.
  • Абстрактный класс определяет некоторое общее поведение и просит свои подклассы определить нетипичное или конкретное поведение для своего класса.
  • Методы и члены абстрактного класса могут быть обозначены любым модификатором доступа, в свою очередь все методы интерфейса обязаны быть открытыми (public).
  • Когда происходит наследование абстрактного класса, класс-наследник должен определить абстрактные методы, в то время как интерфейс может наследовать другой интерфейс и при этом не обязательно определять его методы.
  • Класс-наследник может расширять только один абстрактный класс, а интерфейс может расширять или класс может реализовывать множество других интерфейсов.
  • Класс-наследник может определять абстрактные методы с тем же или менее ограниченным модификатором доступа, при этом класс, реализующий интерфейс, должен определять методы с тем же уровнем видимости.
  • Интерфейс не содержит конструкторы, в том время, как они есть в абстрактном классе.
  • Переменные, объявленные в Java-интерфейсе по умолчанию являются final. Абстрактный класс может содержать переменные, которые не являются final.
  • Все участники Java-интерфейса по умолчанию являются public . Участники абстрактного класса могут позволить себе быть public , protected и др.

Композиция

Повторное использование кода может быть достигнуто с помощью как наследования, так и композиции. Но при этом задействование композиции обеспечивает более высокий уровень инкапсуляции, чем наследование, так как изменения в back-end классе не обязательно затронут код, который относится к front-end классу. Композиция - это техника проектирования, применяющая в классах отношения типа “has-a” (имеет, включает в себя). Для повторного использования кода могут применяться как наследование в java, так и композиция объекта. Суть композиции заключается в выражении отношения "has a" между объектами. Подумайте о стуле. У стула есть (has a) сидение. У стула есть (has a) спинка. У стула есть (has a) определенное количество ножек. Фраза ”has a” / “есть” предполагает отношения, в которых стул имеет или, как минимум, использует другой объект. Это как раз и есть отношения “has-a”, являющиеся основой композиции. Преимущества:
  • Контроль видимости
  • Реализация может быть заменена во время выполнения (run-time)
  • Слабая связанность, так как класс-интерфейс не зависит от реализации.
Различия между композицией и наследованием
Композиция (has a / имеет) Наследование (is a / является)
1 Поддерживает полиморфизм и повторное использование кода.
2 Объект во время выполнения (run-time) уже создан. Объект создается динамически во время компиляции.
3 Реализация может быть заменена во время выполнения (run-time). Реализация может быть заменена во время компиляции.
4 Подкласс не зависит от класса-родителя, что благоприятствует слабому связыванию (особенно под управлением интерфейса). Подкласс завизист от реализации класса-родителя, поэтому связывание считается сильным.
5 Использование: в Доме есть Ванная комната. Неправильно говорить, что Дом - это Ванная комната. Наследование является однонаправленным: Дом - это Здание. Но здание не является домом.
Примечание: Не используйте наследование только для того, чтобы обеспечить повторное использование кода. Если нет отношенией “is a“ (является), для этих целей используется композиция. Разница между композицией и агрегацией в отношениях объектов. Агрегация - это взаимосвязь, при которой один класс вписывается в коллекцию. Это часть целого отношения, где часть может существовать без целого. Такие отношения гораздо слабее. Нет циклической зависимости. Например: заказ и продукт. Композиция - это взаимосвязь, при которой один класс вписывается в коллекцию. Это часть целого отношения, при которой часть не может существовать без целого. Если целое уничтожается, все его составляющие тоже будут уничтожены. Это более сильные отношения. Например: многоугольник и его вершины, заказ и его компонент.

Полиморфи́зм (в языках программирования) - возможность объектов с одинаковой спецификацией иметь различную реализацию.

Язык программирования поддерживает полиморфизм, если классы с одинаковой спецификацией могут иметь различную реализацию - например, реализация класса может быть изменена в процессе наследования.

Кратко смысл полиморфизма можно выразить фразой: «Один интерфейс, множество реализаций».

Полиморфизм - один из четырёх важнейших механизмов объектно-ориентированного программирования (наряду с абстракцией, инкапсуляцией и наследованием).

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

внешняя общность проявляется как одинаковый набор методов с одинаковыми именами и сигнатурами (именем методов и типами аргументов и их количеством);

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

Класс геометрических фигур (эллипс, многоугольник) может иметь методы для геометрических трансформаций (смещение, поворот, масштабирование).

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

В объектно-ориентированных языках

В объектно-ориентированных языках класс является абстрактным типом данных.[Прим. 1] Полиморфизм реализуется с помощью наследования классов и виртуальных функций. Класс-потомок наследует сигнатуры методов класса-родителя, а реализация, в результате переопределения метода, этих методов может быть другой, соответствующей специфике класса-потомка. Другие функции могут работать с объектом как с экземпляром класса-родителя, но если при этом объект на самом деле является экземпляром класса-потомка, то во время исполнения будет вызван метод, переопределенный в классе-потомке. Это называется поздним связыванием. [Примером использования может служить обработка массива, содержащего экземпляры как класса-родителя, так и класса-потомка: очевидно, что такой массив может быть объявлен только как массив типа класса-родителя и у объектов массива могут вызываться только методы этого класса, но если в классе-потомке какие-то методы были переопределены, то в режиме исполнения для экземпляров этого класса будут вызваны именно они, а не методы класса-родителя.]

Класс-потомок сам может быть родителем. Это позволяет строить сложные схемы наследования - древовидные или сетевидные.

Абстрактные (или чисто виртуальные) методы не имеют реализации вообще (на самом деле некоторые языки, например C++, допускают реализацию абстрактных методов в родительском классе). Они специально предназначены для наследования. Их реализация должна быть определена в классах-потомках.

Класс может наследовать функциональность от нескольких классов. Это называется множественным наследованием. Множественное наследование создаёт известную проблему (в C++), когда класс наследуется от нескольких классов-посредников, которые в свою очередь наследуются от одного класса (так называемая «Проблема ромба»): если метод общего предка был переопределён в посредниках, неизвестно, какую реализацию метода должен наследовать общий потомок. Решается эта проблема путём отказа от множественного наследования для классов и разрешением множественного наследования для полностью абстрактных классов (то есть интерфейсов) (C#, Delphi, Java), либо через виртуальное наследование (C++).

В функциональных языках

Полиморфизм в функциональных языках будет рассмотрен на примере языка Haskell.

PHP, ООП, Инкапсуляция, Наследование, Полиморфизм

В этом уроке я расскажу Вам о трех основных понятиях Объектно-Ориентированного Программирования: об Инкапсуляции, Наследовании, Полиморфизме; И научу Вас применять их в разработке.

Инкапсуляция

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

Иными словами при работе с грамотно спроектированным классом мы можем пользоваться только его методами, не вникая в то, как они устроены и как они работают с полями класса. Речь идет о ситуации, когда мы работаем с классом, разработанным другими программистами. Мы же просто пользуемся уже реализованным функционалом.

Приведем другой пример. Принципы инкапсуляции как скрытия внутренней реализации заложены в любой функции PHP. Возможно, вам уже известна функция strpos() для работы со строками из стандартной библиотеки PHP. Эта функция ищет в строке заданную последовательность символов и возвращает ее позицию в виде числа. Если задуматься над реализацией этой функции, то можно предположить, что нам потребуется в цикле просматривать каждый символ от начала строки на совпадение с начальным символом искомой последовательности и в случае такового сравнивать следующие два символа и т. д. Но нам как программистам нет необходимости задумываться над этим и вникать в тонкости реализации данной функции. Нам достаточно знать параметры , которые она принимает, и формат возвращаемого значения . Функция strpos() инкапсулирует в себе решение задачи поиска подстроки, предлагая нам лишь внешний интерфейс для ее использования.

Аналогичным образом правильно спроектированные классы скрывают свою внутреннюю реализацию, предоставляя внешним пользователям интерфейс в виде набора методов.

В языке PHP концепция инкапсуляции реализована в виде специальных модификаторов доступа к полям и методам классов. Об этом мы поговорим далее.

Наследование

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

Давайте приведем пример наследования из реальной жизни. В качестве класса можно взять геометрическую фигуру. При этом мы не уточняем, какой конкретно фигура должна быть. Какие свойства фигуры можно выделить? Предположим, она обладает цветом. Тогда в классе, описывающем фигуру, должно быть соответствующее поле строкового типа, задающее цвет фигуры. Также любая геометрическая фигура обладает площадью. Пусть площадь будет вторым свойством нашего класса.

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

Это значит, что в классах Квадрат и Треугольник нам не придется повторно задавать поля цвета и площади. Достаточно указать, что упомянутые классы наследуются от класса Фигура.

Теперь давайте рассмотрим еще один пример, более приближенный к реалиям веб-программистов. Сейчас в Интернете огромную популярность завоевали различные блоги. Фактически блог - это просто набор статей. И ключевой сущностью при разработке блога является именно статья.

А теперь давайте представим, что в нашем будущем блоге статьи могут иметь различные типы. Для начала остановимся на двух типах: обычная статья-заметка и новостная статья. Для новостной статьи важна дата ее публикации, ведь она несет в себе некоторую конкретную новость.

Чтобы реализовать эту ситуацию в PHP, нам потребуется определить два класса: класс для обычной статьи-заметки и класс для новостной статьи. При этом пусть новостная статья расширяет возможности обычной статьи, т. е. наследуется от нее. Для наследования классов в PHP используется ключевое слово extends .

Class { ... // содержимое класса } class { ... // содержимое класса }

Приведенный код определяет класс NewsArticle как наследника Article . Класс Article в свою очередь является родительским для класса NewsArticle . Это значит, что поля и методы класса Article будут также присутствовать в классе NewsArticle , и заново их определять не нужно.

С помощью наследования можно выстраивать целую иерархию классов, наследуя один от другого. В то же время у любого класса может быть только один родитель:


Иногда у нас может появиться необходимость переопределить один из методов родительского класса. Давайте еще раз приведем реализацию класса Article :

Class Article { ... // поля класса // Функция для вывода статьи function view() { echo "

$this->title

$this->content

"; } }

Предположим, что вывод новостной статьи должен отличаться от представления обычной статьи, и мы должны дополнительно выводить время публикации новости. При этом в классе Article уже существует метод view() , отвечающий за вывод статьи. Можно поступить двумя способами. В первом случае можно придумать новый метод в классе NewsArticle , например, с именем viewNews() специально для вывода новости. Однако правильнее использовать одинаковые методы для выполнения схожих действий в наследуемых классах. Поэтому будет лучше, если метод для вывода новости в классе NewsArticle будет называться так же, как и в родительском классе - view() . Для реализации такой логики в PHP существует возможность переопределять родительские методы, т. е. задавать в дочерних классах методыс названиями, совпадающими в родительских классах. Реализация этих методов в родительских классах в таком случае становится неактуальной для класса-потомка. Давайте приведем пример класса NewsArticle с переопределенным методом view() :

Class NewsArticle extends Article { $datetime; // дата публикации новости // Функция для вывода статьи function view() { echo "

$this->title

". strftime("%d.%m.%y", $this->datetime). " Новость

$this->content

"; } }

В приведенном коде используется функция strftime() , которая позволяет выводить даты в удобном виде. Для лучшего понимания кода ознакомьтесь со спецификацией этой функции в справочнике. Для нас же сейчас важно, чтобы вы обратили внимание на то, что класс NewsArticle , как и Article , определяет метод view() . Соответственно, все объекты этого класса будут использовать метод view() , объявленный в классе NewsArticle , а не в Article .

У вас может возникнуть вопрос: почему же все-таки важно переопределять некоторые методы вместо того, чтобы вводить новые методы у классов-потомков? Понимание этого придет вместе с пониманием следующей важнейшей концепции ООП.

Полиморфизм

Полиморфизм - взаимозаменяемость объектов с одинаковым интерфейсом.

Язык программирования поддерживает полиморфизм, если классы с одинаковой спецификацией могут иметь различную реализацию - например, реализация класса может быть изменена в процессе наследования. Это именно то, что мы видели в предыдущем примере со статьями.

Давайте рассмотрим следующий пример, который дает представление о сути полиморфизма :

Class A { function Test() { echo "Это класс A
"; } function Call() { $this->Test(); } } class B extends A { function Test() { echo "Это класс B
"; } } $a = new A(); $b = new B(); $a->Call(); // выводит: "Это класс A" $b->Test(); // выводит: "Это класс B" $b->

Обратите внимание на комментарии к трем последним строчкам. Попытайтесь самостоятельно объяснить такой результат . Желательно собственноручно реализовать и протестировать этот пример. Потратьте время на то, чтобы работа сценария стала вам полностью ясна, т. к. в этом небольшом примере заложен глубокий смысл ООП. Теперь давайте попробуем вместе разобрать предложенный код.

$a->Call(); // выводит: "Это класс A"

В этой строке происходит вызов метода Call() у объекта класса А . Как и определено в функции Call() класса A , происходит вызов метода Test() . Отрабатывает метод Test() у объекта класса A , и на экран выводится текст "Это класс А ".

$b->Test(); // выводит: "Это класс B"

В данной строке происходит вызов метода Test() у объекта класса B . Метод Test() класса B выводит на экран текст "Это класс В ".

$b->Call(); // выводит: "Это класс B"

Наконец, в последней строке происходит вызов класса Call() у объекта класса В . Но в реализации класса B мы не увидим такого метода, а это значит, что он наследуется от класса A , т. к. класс B - это потомок класса A . Что же мы видим в реализации метода Call() класса A ? Следующий код:

$this->Test();

Метод Call() вызывает метод Test() того объекта, в котором находится. Это значит, что отработает метод Test() объекта класса B . Именно этим объясняется результат, выведенный на экране.

В этом и заключается принцип полиморфизма . Классы могут иметь одинаковые методы , но разную их реализацию. Разрабатывая код сценария, мы можем знать лишь общую для группы классов спецификацию их методов, но не иметь представления, экземпляр какого именно класса будет использоваться в конкретный момент времени.

Понятия инкапсуляции, наследования и полиморфизма можно назвать тремя китами ООП . Понимание и грамотное применение принципов этих концепций - залог успеха разработки с применением ООП.

error: