что такое абстрактный класс в ооп

ООП с примерами (часть 2)

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

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

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

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

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

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

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

Инкапсуляция неразрывно связана с понятием интерфейса класса. По сути, всё то, что не входит в интерфейс, инкапсулируется в классе.

Абстракция

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

Абстрагирование – это способ выделить набор значимых характеристик объекта, исключая из рассмотрения незначимые. Соответственно, абстракция – это набор всех таких характеристик.

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

Полиморфизм

Любое обучение вождению не имело бы смысла, если бы человек, научившийся водить, скажем, ВАЗ 2106 не мог потом водить ВАЗ 2110 или BMW X3. С другой стороны, трудно представить человека, который смог бы нормально управлять автомобилем, в котором педаль газа находится левее педали тормоза, а вместо руля – джойстик.

Всё дело в том, что основные элементы управления автомобиля имеют одну и ту же конструкцию и принцип действия. Водитель точно знает, что для того, чтобы повернуть налево, он должен повернуть руль, независимо от того, есть там гидроусилитель или нет.
Если человеку надо доехать с работы до дома, то он сядет за руль автомобиля и будет выполнять одни и те же действия, независимо от того, какой именно тип автомобиля он использует. По сути, можно сказать, что все автомобили имеют один и тот же интерфейс, а водитель, абстрагируясь от сущности автомобиля, работает именно с этим интерфейсом. Если водителю предстоит ехать по немецкому автобану, он, вероятно выберет быстрый автомобиль с низкой посадкой, а если предстоит возвращаться из отдалённого маральника в Горном Алтае после дождя, скорее всего, будет выбран УАЗ с армейскими мостами. Но, независимо от того, каким образом будет реализовываться движение и внутреннее функционирование машины, интерфейс останется прежним.

Полиморфизм – это свойство системы использовать объекты с одинаковым интерфейсом без информации о типе и внутренней структуре объекта.

Например, если вы читаете данные из файла, то, очевидно, в классе, реализующем файловый поток, будет присутствовать метод похожий на следующий: byte[] readBytes( int n );
Предположим теперь, что вам необходимо считывать те же данные из сокета. В классе, реализующем сокет, также будет присутствовать метод readBytes. Достаточно заменить в вашей системе объект одного класса на объект другого класса, и результат будет достигнут.

При этом логика системы может быть реализована независимо от того, будут ли данные прочитаны из файла или получены по сети. Таким образом, мы абстрагируемся от конкретной специализации получения данных и работаем на уровне интерфейса. Единственное требование при этом – чтобы каждый используемый объект имел метод readBytes.

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

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

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

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

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

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

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

Источник

Зачем нужны абстракции и интерфейсы

И что это вообще такое?

Как в старом анекдоте: про объектно-ориентированное программирование можно рассказать просто и неправильно либо сложно и неправильно. Мы попробуем рассказать про очередной аспект ООП просто.

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

В этой статье разберём два сложных понятия из объектно-ориентированного программирования: абстракции и интерфейсы. Это ещё одна ступень в понимании непостижимого.

Основные идеи из ООП

Абстракция

Представьте, что вы попросили нескольких человек описать в общих чертах, что такое телефон и как им пользоваться: пусть это будут бабушка, мама и подруга. Бабушка вспомнит про дисковые телефоны и трубки с витым проводом. Мама расскажет про радиотелефоны, у которых есть база и есть трубка, с которой можно ходить по всей квартире, а подруга начнёт описывать мобильник.

Несмотря на то что рассказы будут сильно отличаться между собой, у них будет несколько общих моментов про телефон:

Получается, что если представить абстрактный телефон, то получится такое устройство с динамиком, микрофоном и средством набора номера.

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

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

Чтобы работать с абстракциями, используют интерфейсы.

Интерфейс

Итак, у нас есть некое устройство с трубкой, микрофоном, динамиком и средством набора номера. Но если вы вспомните рассказы мамы, бабушки и подруги, то обнаружите вот что:

Всё это — интерфейсы. Они позволяют работать с объектом, не вникая в то, как он устроен внутри. Если вы умеете работать с интерфейсом номеронабирателя, то вам всё равно, нужно ли крутить диск, нажимать физические кнопки на радиотрубке или давить пальцем на сенсорный экран.

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

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

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

Сложная терминология

Строго говоря, интерфейсы — это не действия, а методы. Сейчас объясним.

В программировании есть операции — это простейшие действия, например, скопировать значение из одной переменной в другую.

Из простых действий составляются функции — это когда несколько операций «склеиваются» в нечто единое. Мы даём этой склейке название и получаем функцию. Например, может быть функция «проверить правильность электронного адреса», которая состоит из нескольких десятков простых операций.

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

Итого: метод — это набор простых действий, которые склеили в единое целое и засунули в объект.

Для чего это всё

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

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

Чтобы такого не было, поступают так:

Источник

ООП. Часть 6. Абстрактные классы и интерфейсы

Узнайте истинную мощь наследования и полиморфизма! Раскрываем секреты абстрактных классов и интерфейсов.

что такое абстрактный класс в ооп. Смотреть фото что такое абстрактный класс в ооп. Смотреть картинку что такое абстрактный класс в ооп. Картинка про что такое абстрактный класс в ооп. Фото что такое абстрактный класс в ооп

что такое абстрактный класс в ооп. Смотреть фото что такое абстрактный класс в ооп. Смотреть картинку что такое абстрактный класс в ооп. Картинка про что такое абстрактный класс в ооп. Фото что такое абстрактный класс в ооп

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

Все статьи про ООП

что такое абстрактный класс в ооп. Смотреть фото что такое абстрактный класс в ооп. Смотреть картинку что такое абстрактный класс в ооп. Картинка про что такое абстрактный класс в ооп. Фото что такое абстрактный класс в ооп

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

Абстрактные классы

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

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

Чтобы не повторять код несколько раз, можно вынести реализацию этих свойств и методов в абстрактный класс Character:

Тут всё как у обычных классов, но в конце можно заметить объявление свойства и метода без реализации. Реализация этих абстрактных свойств должна находиться в дочернем классе:

Когда объявляется реализация такого члена класса, необходимо указать ключевое слово override. Абстрактными могут быть следующие члены класса:

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

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

что такое абстрактный класс в ооп. Смотреть фото что такое абстрактный класс в ооп. Смотреть картинку что такое абстрактный класс в ооп. Картинка про что такое абстрактный класс в ооп. Фото что такое абстрактный класс в ооп

Абстрактный класс должен быть публичным.

Источник

Абстрактные классы (C++)

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

Абстрактный класс создается путем объявления по крайней мере одной чистой виртуальной функции члена. Это виртуальная функция, объявленная с помощью синтаксиса чистого описателя ( = 0 ). Классы, производные от абстрактного класса, должны реализовывать чисто виртуальную функцию; в противном случае они также будут абстрактными.

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

Единственное различие между этим и предыдущим объявлениями состоит в том, что функция PrintBalance объявлена со спецификатором чисто виртуальной функции pure ( = 0 ).

Ограничения на использование абстрактных классов

Абстрактные классы нельзя использовать для:

переменных и данных членов;

типов возвращаемых функциями значений;

типов явных преобразований.

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

Определенные чистые виртуальные функции

Чистые виртуальные функции в абстрактных классах могут быть определены или иметь реализацию. Вызывать эти функции можно только с помощью полного синтаксиса:

abstract — имя класса::Function-Name()

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

В примере показано, как расширение компилятора Майкрософт позволяет добавить встроенное определение в чистый виртуальный

Когда объект aDerived выходит из области действия, derived вызывается деструктор класса. Компилятор создает код для неявного вызова деструктора класса base после derived деструктора. Пустая реализация чисто виртуальной функции

base гарантирует, что для функции существует хотя бы определенная реализация. Без него компоновщик создает неразрешенную ошибку внешнего символа для неявного вызова.

В предыдущем примере чистая виртуальная функция base::

base вызывается неявно из derived::

Источник

Введение в ООП с примерами на C#. Часть четвёртая. Абстрактные классы

Авторизуйтесь

Введение в ООП с примерами на C#. Часть четвёртая. Абстрактные классы

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

Что такое абстрактные классы

В плане терминологии давайте доверимся MSDN:

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

Абстрактные классы в действии

Итак, попробуем создать абстрактный класс:

Попытаемся скомпилировать этот код:

Compile time error: Cannot create an instance of the abstract class or interface ‘InheritanceAndPolymorphism.ClassA’

Описание методов в абстрактном классе

Попробуем добавить в наш абстрактный класс немного кода:

С кодом класса никаких проблем нет, но скомпилировать снова не получается, потому что нельзя создавать экземпляры абстрактных классов.

Использование абстрактного класса в качестве базового

Давайте попробуем создать ещё один класс:

Вау. Теперь всё спокойно компилируется.

Что нужно запомнить: Мы можем унаследовать обычный класс от абстрактного.

Декларация методов в абстрактном классе

Теперь попробуем сделать вот так:

И… мы получаем ошибку компиляции:

Compile time error: ‘InheritanceAndPolymorphism.ClassA.YYY()’
must declare a body because it is not marked abstract, extern, or partial

…и снова получим ошибку компиляции:

Compiler error: ‘InheritanceAndPolymorphism.ClassB’ does not implement
inherited abstract member ‘InheritanceAndPolymorphism.ClassA.YYY()’

Что нужно запомнить: Если мы объявляем абстрактный метод в абстрактном классе, то этот метод должен реализовываться в неабстрактных наследниках этого класса.

Реализация абстрактного метода в производном классе

Так, давайте тогда попробуем реализовать метод YYY() в классе ClassB :

На первый взгляд всё отлично, правда? Но на этот раз мы получим сразу две ошибки компиляции:

Compile time error: ‘InheritanceAndPolymorphism.ClassB’ does not implement
inherited abstract member ‘InheritanceAndPolymorphism.ClassA.YYY()’

Compile time warning: ‘InheritanceAndPolymorphism.ClassB.YYY()’ hides
inherited member ‘InheritanceAndPolymorphism.ClassA.YYY()’.

Дело в том, что в C# нужно явно объявить, что мы реализуем абстрактный метод класса-родителя с помощью ключевого слова override :

Ура! ^_^ У нас наконец-то нет никаких ошибок!

Абстрактный метод базового класса и метод с override класса-наследника должны быть одинаковы

Это значит, что мы не можем менять тип возвращаемого значения или аргументы, которые передаются в метод. Например, если мы напишем такое:

То в консоли увидим следующую ошибку:

Compile time error: ‘InheritanceAndPolymorphism.ClassB.YYY()’: return type must be ‘void’
to match overridden member ‘InheritanceAndPolymorphism.ClassA.YYY()’

Инициализация переменных в абстрактных классах

В примерах выше, переменная int a будет обладать значением по умолчанию (0). Мы можем изменить его на нужное нам прямо в абстрактном классе — с этим не связано никаких особенностей.

Абстрактные методы в неабстрактных классах

Такой код не скомпилируется:

Compiler error: ‘InheritanceAndPolymorphism.ClassA.YYY()’ is abstract
but it is contained in non-abstract class ‘InheritanceAndPolymorphism.ClassA’

Что нужно запомнить: Абстрактные методы могут быть объявлены только в абстрактных классах.

Вызов абстрактного метода родителя

Compile time error : Cannot call an abstract base member:
‘InheritanceAndPolymorphism.ClassA.YYY()’

Разумеется, мы не можем исполнить код, которого не существует.

Абстрактный класс, который наследуется от другого абстрактного класса

Почему вывод именно такой, вы должны понимать из материалов третьей статьи этой серии.

Может ли абстрактный класс быть sealed

Compile time error: ‘InheritanceAndPolymorphism.ClassA’:
an abstract class cannot be sealed or static

Что мы узнали сегодня:

Что нужно запомнить

Источник

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *