Ооп это: Объектно-ориентированное программирование простым языком — объясняют эксперты

Содержание

Объектно-ориентированное программирование простым языком — объясняют эксперты

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

Зарплату сотрудника знает он сам, его руководитель и бухгалтер, остальные — нет. Такое сокрытие данных называется инкапсуляция. Какие свойства и поведение будет доступно другим объектам обычно определяется на уровне класса. Руководитель отдела также является сотрудником, но он обладает рядом дополнительных свойств, например, у него есть подчиненные. Таким образом класс «руководитель», расширяет класс «сотрудник» или, другими словами, происходит наследование. При этом между классами устанавливается отношение «является» — то есть любой руководитель является сотрудником, но не наоборот — не каждый сотрудник является руководителем. Если у класса больше одного наследника, то образуется иерархия. Классы, которые являются родственниками в иерархии не связаны отношением «является», например, бухгалтер является сотрудником, но бухгалтер не является руководителем.

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

Так мы рассмотрели, как связаны объекты и классы, и такие понятия, как: инкапсуляция, наследование и полиморфизм. Все это — базовые понятия ООП.

ООП в картинках / Хабр

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


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

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

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

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

Классы и объекты

Сразу лирическое отступление: объектно-ориентированный подход возможен и без классов, но мы будем рассматривать, извиняюсь за каламбур, классическую схему, где классы — наше всё.

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

Таким образом, класс — это описание того, какими свойствами и поведением будет обладать объект. А объект — это экземпляр с собственным состоянием этих свойств.

Мы говорим «свойства и поведение», но звучит это как-то абстрактно и непонятно. Привычнее для программиста будет звучать так: «переменные и функции». На самом деле «свойства» — это такие же обычные переменные, просто они являются атрибутами какого-то объекта (их называют полями объекта). Аналогично «поведение» — это функции объекта (их называют методами), которые тоже являются атрибутами объекта. Разница между методом объекта и обычной функцией лишь в том, что метод имеет доступ к собственному состоянию через поля.

Итого, имеем методы и свойства, которые являются атрибутами. Как работать с атрибутами? В большинстве ЯП оператор обращения к атрибуту — это точка (кроме PHP и Perl). Выглядит это примерно вот так (псевдокод):

// объявление класса с помощью ключевого слова class
class Transformer(){
    // объявление поля x
    int x

    // объявление метода конструктора (сюда нам чуть ниже передадут 0)
    function constructor(int x){
        // инициализация поля x 
        // (переданный конструктору 0 превращается в свойство объекта)
        this. x = x
    }
	
    // объявление метода run
    function run(){
        // обращение к собственному атрибуту через this
        this.x += 1
    }
}

// а теперь клиентский код:

// создаем новый экземпляр трансформера с начальной позицией 0
optimus = new Transformer(0)

optimus.run() // приказываем Оптимусу бежать
print optimus.x // выведет 1
optimus.run() // приказывает Оптимусу еще раз бежать
print optimus.x // выведет 2

В картинках я буду использовать такие обозначения:

Я не стал использовать UML-диаграммы, посчитав их недостаточно наглядными, хоть и более гибкими.

Анимация №1

Что мы видим из кода?

1. this — это специальная локальная переменная (внутри методов), которая позволяет объекту обращаться из своих методов к собственным атрибутам. Обращаю внимание, что только к собственным, то бишь, когда трансформер вызывает свой метод, либо меняет собственное состояние. Если снаружи обращение будет выглядеть так: optimus.x, то изнутри, если Оптимус захочет сам обратиться к своему полю x, в его методе обращение будет звучать так: this. x, то есть «я (Оптимус) обращаюсь к своему атрибуту x«. В большинстве языков эта переменная называется this, но встречаются и исключения (например, self)

2. constructor — это специальный метод, который автоматически вызывается при создании объекта. Конструктор может принимать любые аргументы, как и любой другой метод. В каждом языке конструктор обозначается своим именем. Где-то это специально зарезервированные имена типа __construct или __init__, а где-то имя конструктора должно совпадать с именем класса. Назначение конструкторов — произвести первоначальную инициализацию объекта, заполнить нужные поля.

3. new — это ключевое слово, которое необходимо использовать для создания нового экземпляра какого-либо класса. В этот момент создается объект и вызывается конструктор. В нашем примере, конструктору передается 0 в качестве стартовой позиции трансформера (это и есть вышеупомянутая инициализация). Ключевое слово new в некоторых языках отсутствует, и конструктор вызывается автоматически при попытке вызвать класс как функцию, например так: Transformer().

4. Методы constructor и run работают с внутренним состоянием, а во всем остальном не отличаются от обычных функций. Даже синтаксис объявления совпадает.

5. Классы могут обладать методами, которым не нужно состояние и, как следствие, создание объекта. В этом случае метод делают статическим.

SRP

(Single Responsibility Principle / Принцип единственной ответственности / Первый принцип SOLID). С ним вы, наверняка, уже знакомы из других парадигм: «одна функция должна выполнять только одно законченное действие». Этот принцип справедлив и для классов: «Один класс должен отвечать за какую-то одну задачу». К сожалению с классами сложнее определить грань, которую нужно пересечь, чтобы принцип нарушался.

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

Ассоциация

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

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

class Gun(){ // объявляем класс Пушка
    int ammo_count // объявляем количество боеприпасов

    function constructor(){ // конструктор
        this.reload() // вызываем собственный метод "перезарядить"
    }

    function fire(){ // объявляем метод пушки "стрелять"
        this.ammo_count -= 1 // расходуем боеприпас из собственного магазина
    }

    function reload(){ // объявляем метод "перезарядить"
        this. ammo_count = 10 // забиваем собственный магазин боеприпасами
    }
}

class Transformer(){ // объявляем класс Трансформер
    Gun gun_left // объявляем поле "левая пушка" типа Пушка
    Gun gun_right // объявляем поле "правая пушка" тоже типа Пушка
    
    /*
    теперь конструктор Трансформера принимает
    в качестве аргументов две уже конкретные созданные пушки,
    которые передаются извне
    */
    function constructor(Gun gun_left, Gun gun_right){
        this.gun_left = gun_left // устанавливаем левую пушку на борт
        this.gun_right = gun_right // устанавливаем правую пушку на борт
    }
    
    // объявляем метод Трансформер "стрелять", который сначала стреляет...
    function fire(){
        // левой пушкой, вызывая ее метод "стрелять"
        this.gun_left.fire()
        // а затем правой пушкой, вызывая такой же метод "стрелять"
        this.gun_right.fire()
    }
}

gun1 = new Gun() // создаем первую пушку
gun2 = new Gun() // создаем вторую пушку
optimus = new Transformer(gun1, gun2) // создаем трансформера, передавая ему обе пушки

Анимация №2

this. gun_left.fire() и this.gun_right.fire() — это обращения к дочерним объектам, которые происходят так же через точки. По первой точке мы обращаемся к атрибуту себя (this.gun_right), получая объект пушки, а по второй точке обращаемся к методу объекта пушки (this.gun_right.fire()).

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

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

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

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

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

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

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

Оптимус Прайм и Мегатрон — оба трансформеры, но один является автоботом, а второй десептиконом. Допустим, что различия между автоботами и десептиконами будут заключаться только в том, что автоботы трансформируются в автомобили, а десептиконы — в авиацию. Все остальные свойства и поведение не будут иметь никакой разницы. В таком случае можно спроектировать систему наследования так: общие черты (бег, стрельба) будут описаны в базовом классе «Трансформер», а различия (трансформация) в двух дочерних классах «Автобот» и «Десептикон».

class Transformer(){ // базовый класс
    function run(){
        // код, отвечающий за бег
    }
    function fire(){
        // код, отвечающий за стрельбу
    }
}

class Autobot(Transformer){ // дочерний класс, наследование от Transformer
    function transform(){
        // код, отвечающий за трансформацию в автомобиль
    }
}

class Decepticon(Transformer){ // дочерний класс, наследование от Transformer
    function transform(){
        // код, отвечающий за трансформацию в самолет
    }
}

optimus = new Autobot()
megatron = new Decepticon()

Анимация №3

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

Перегрузка

Если же в классе-потомке переопределить уже существующий метод в классе-родителе, то сработает перегрузка. Это позволяет не дополнять поведение родительского класса, а модифицировать. В момент вызова метода или обращения к полю объекта, поиск атрибута происходит от потомка к самому корню — родителю. То есть, если у автобота вызвать метод fire(), сначала поиск метода производится в классе-потомке — Autobot, а поскольку его там нет, поиск поднимается на ступень выше — в класс Transformer, где и будет обнаружен и вызван. Следует отметить, что модификация нарушает LSP из набора принципов SOLID, но мы рассматриваем только техническую возможность.

Неуместное применение

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

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

Применительно к нашей ситуации это будет звучать так:

  1. Автобот является Трансформером? Да, значит выбираем наследование.
  2. Пушка является частью Трансформера? Да, значит — композиция.

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

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

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

Множественное наследование

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

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

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

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

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

Полиморфизм

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

Положим, у нас есть три трансформера: Оптимус, Мегатрон и Олег. Трансформеры боевые, стало быть обладают методом attack(). Игрок, нажимая у себя на джойстике кнопку «воевать», сообщает игре, чтобы та вызвала метод attack() у трансформера, за которого играет игрок. Но поскольку трансформеры разные, а игра интересная, каждый из них будет атаковать каким-то своим способом. Скажем, Оптимус — объект класса Автобот, а Автоботы снабжаются пушками с плутониевыми боеголовками (да не прогневаются фанаты трансформеров). Мегатрон — Десептикон, и стреляет из плазменной пушки. Олег — басист, и он обзывается. А в чем польза?

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

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

Инкапсуляция — это контроль доступа к полям и методам объекта. Под контролем доступа подразумевается не только можно/неможно, но и различные валидации, подгрузки, вычисления и прочее динамическое поведение.

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

  • publiс — к атрибуту может получить доступ любой желающий
  • private — к атрибуту могут обращаться только методы данного класса
  • protected — то же, что и private, только доступ получают и наследники класса в том числе
class Transformer(){
    public function constructor(){ }

    protected function setup(){ }

    private function dance(){ }
}

Как правильно выбрать модификатор доступа? В простейшем случае так: если метод должен быть доступен внешнему коду, выбираем public. В противном случае — private. Если есть наследование, то может потребоваться protected в случае, когда метод не должен вызываться снаружи, но должен вызываться потомками.

Аксессоры (геттеры и сеттеры)

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

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

Интерфейсы

Задача интерфейса — снизить уровень зависимости сущностей друг от друга, добавив больше абстракции.

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

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

Классы с интерфейсами состоят в отношении «многие ко многим»: один класс может имплементировать множество интерфейсов, и каждый интерфейс, в свою очередь, может имплементироваться многими классами.

У интерфейса двустороннее применение:

  1. По одну сторону интерфейса — классы, имплементирующие данный интерфейс.
  2. По другую сторону — потребители, которые используют этот интерфейс в качестве описания типа данных, с которым они (потребители) работают.

Например, если какой-то объект помимо основного поведения, может быть сериализован, то пускай он имплементирует интерфейс «Сериализуемый». А если объект можно склонировать, то пусть он имплементирует еще один интерфейс — «Клонируемый». И если у нас есть какой-то транспортный модуль, который передает объекты по сети, он будет принимать любые объекты, имплементирующие интерфейс «Сериализуемый».

Представим, что каркас трансформера оборудован тремя слотами: слот для оружия, для генератора энергии и для какого-нибудь сканера. Эти слоты обладают определенными интерфейсами: в каждый слот можно установить только подходящее оборудование. В слот для оружия можно установить ракетную установку или лазерную пушку, в слот для генератора энергии — ядерный реактор или РИТЭГ (радиоизотопный термоэлектрический генератор), а в слот для сканера — радар или лидар. Суть в том, что каждый слот имеет универсальный интерфейс подключения, а уже конкретные устройства должны соответствовать этому интерфейсу. К примеру, на материнских платах используется несколько типов слотов: слот для процессора позволяет подключать различные процессоры, подходящие под данный сокет, а слот SATA — любой SSD или HDD накопитель или даже CD/DVD.

Обращаю внимание, что получившаяся система слотов у трансформеров — это пример использования композиции. Если же оборудование в слотах будет сменным в ходе жизни трансформера, то тогда это уже агрегация. Для наглядности, мы будем называть интерфейсы, как принято в некоторых языках, добавляя заглавную «И» перед именем: IWeapon, IEnergyGenerator, IScanner.

// описания интерфейсов:

interface IWeapon{
    function fire() {} // декларация метода без имплементации. Ниже аналогично
}

interface IEnergyGenerator{
    // тут уже два метода, которые должны будут реализовать классы:
    function generate_energy() {} // первый
    function load_fuel() {}       // второй
}

interface IScanner{
    function scan() {}
}


// классы, реализующие интерфейсы:

class RocketLauncher() : IWeapon
{
    function fire(){
        // имплементация запуска ракеты
    }
}

class LaserGun() : IWeapon
{
    function fire(){
        // имплементация выстрела лазером
    }
}

class NuclearReactor() : IEnergyGenerator
{
    function generate_energy(){
        // имплементация генерации энергии ядерным реактором
    }
	
    function load_fuel(){
        // имплементация загрузки урановых стержней
    }
}

class RITEG() : IEnergyGenerator
{
    function generate_energy(){
        // имплементация генерации энергии РИТЭГ
    }
	
    function load_fuel(){
        // имплементация загрузки РИТЭГ-пеллет
    }
}

class Radar() : IScanner
{
    function scan(){
        // имплементация использования радиолокации
    }	
}

class Lidar() : IScanner
{
    function scan(){
        // имплементация использования оптической локации
    }
}

// класс - потребитель:

class Transformer() {
    // привет, композиция:
    IWeapon slot_weapon   // Интерфейсы указаны в качестве типов данных.
    IEnergyGenerator slot_energy_generator // Они могут принимать любые объекты,
    IScanner slot_scanner // которые имплементируют указанный интерфейс
	
    /*
    в параметрах методов интерфейс тоже указан как тип данных,
    метод может принимать объект любого класса,
    имплементирующий данный интерфейс:
    */
    function install_weapon(IWeapon weapon){ 
        this.slot_weapon = weapon
    }
	
    function install_energy_generator(IEnergyGenerator energy_generator){
        this.slot_energy_generator = energy_generator
    }
	
    function install_scanner(IScanner scanner){
        this.slot_scanner = scanner
    }
}

// фабрика трансформеров

class TransformerFactory(){
    function build_some_transformer() {
       	transformer = new Transformer()
       	laser_gun = new LaserGun()
       	nuclear_reactor = new NuclearReactor()
       	radar = new Radar()
       	
       	transformer.install_weapon(laser_gun)
       	transformer.install_energy_generator(nuclear_reactor)
       	transformer.install_scanner(radar)
        	
        return transformer
    }
}

// использование

transformer_factory = new TransformerFactory()
oleg = transformer_factory.build_some_transformer()

Анимация №4

К сожалению, в картинку не влезла фабрика, но она все равно необязательна, трансформера можно собрать и во дворе.

Обозначенный на картинке слой абстракции в виде интерфейсов между слоем имплементации и слоем-потребителем дает возможность абстрагировать одних от других. Вы можете это наблюдать, посмотрев на каждый слой в отдельности: в слое имплементации (слева) нет ни слова про класс Transformer, а в слое-потребителе (справа) нет ни слова про конкретные имплементации (там нет слов Radar, RocketLauncher, NuclearReactor и т. д.)

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

Утиная типизация

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

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

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

ISP

(Interface Segregation Principle / Принцип разделения интерфейса / Четвертый принцип SOLID) призывает не создавать жирные универсальные интерфейсы. Вместо этого интерфейсы нужно разделять на более мелкие и специализированные, это поможет гибче их комбинировать в имплементирующих классах, не заставляя имплементировать лишние методы.

Абстракция

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

Уровень абстракции

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

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

Неверный выбор уровня абстракции ведет к одной из двух проблем:

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

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

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

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

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

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

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

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

Паттерны проектирования

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

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

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

Еще одна ценность от паттернов — формализации терминологии. Гораздо проще коллеге сказать, что в этом месте используется «цепочка обязанностей», чем полчаса рисовать поведение и отношения объектов на бумажке.

Заключение

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

Хочется закончить свою песнь важными словами. Любые описанные механизмы, принципы и паттерны, как и ООП в целом не стоит применять там, где это бессмысленно или может навредить. Это ведет к появлению статей со странными заголовками типа «Наследование — причина преждевременного старения» или «Синглтон может приводить к онкологическим заболеваниям».

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

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

Что такое объектно-ориентированное программирование. Урок 1

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

Истоки ООП берут начало с 60-х годов XX века. Однако окончательное формирование основополагающих принципов и популяризацию идеи следует отнести к 80-м годам. Большой вклад внес Алан Кей.

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

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

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

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

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

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

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

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

Понятия объектно-ориентированного программирования

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

Что такое класс или тип? Проведем аналогию с реальным миром. Если мы возьмем конкретный стол, то это объект, но не класс. А вот общее представление о столах, их назначении – это класс. Ему принадлежат все реальные объекты столов, какими бы они ни были. Класс столов дает общую характеристику всем столам в мире, он их обобщает.

То же самое с целыми числами в Python. Тип int – это класс целых чисел. Числа 5, 100134, -10 и т. д. – это конкретные объекты этого класса.

В языке программирования Python объекты принято называть также экземплярами. Это связано с тем, что в нем все классы сами являются объектами класса type. Точно также как все модули являются объектами класса module.

>>> type(list), type(int)
(<class 'type'>, <class 'type'>)
>>> import math
>>> type(math)
<class 'module'>

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

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

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

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

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

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

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

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

Вы уже сталкивались с полиморфизмом операции +. Для чисел она обозначает сложение, а для строк – конкатенацию. Внутренняя реализация кода для этой операции у чисел отличается от реализации таковой для строк.

Практическая работа

Рассмотрите схему. Подумайте над следующими вопросами:

  1. Какие фигуры на ней вы бы назвали классами, а какие – объектами? Что обозначают пунктирные линии?

  2. Может ли объект принадлежать множеству классов? Может ли у класса быть множество объектов?

  3. Звезды скорее обладают разными свойствами или разным поведением? Могут ли свойства оказывать влияние на поведение?

  4. Что могли бы обозначать стрелки?

Курс с примерами решений практических работ и всеми уроками:
android-приложение, pdf-версия

Почему ООП — это плохо

Это перевод статьи Джо Армстронга Why OO Sucks, повествование ведётся от имени автора оригинальной публикации.

Когда я впервые познакомился с объектно-ориентированным программированием (ООП), мне не понравилась эта идея. Не знаю, почему именно — просто почувствовал, что здесь что-то не так. После появления ООП эта парадигма стала очень популярной, а её критика считалась дурным тоном. Объектная ориентированность стала чем-то, что должно обязательно быть в каждом «нормальном» языке программирования.

Когда Erlang стал популярным, у нас часто спрашивали, относится ли он к объектно-ориентированным языкам. Правдивый ответ на этот вопрос должен быть таким: «Конечно, нет, Erlang не объектно-ориентированный язык». Но мы не хотели акцентировать на этом внимание, поэтому придумали уловку. Мы говорили вслух, что Erlang в некотором роде является объектно-ориентированным языком. Такой ответ удовлетворял тех, кому везде нужна объектная ориентированность. Но вдумчивый собеседник слышал в этом ответе правильный посыл: «Erlang — не совсем объектно-ориентированный язык».

В такие моменты я вспоминал слова руководителя подразделения IBM во Франции, которые он сказал на открытии конференции IEEE Logic в Париже. В языке Prolog разработчики IBM добавили много объектно-ориентированных расширений. На вопрос «зачем это сделано» руководитель ответил: «Наши потребители хотели, чтобы Prolog был объектно-ориентированным, поэтому мы сделали его объектно-ориентированным».

Я тогда подумал: «Как всё просто объясняется, и никаких сомнений и колебаний, никаких вопросов насчёт того, действительно ли нужно делать Prolog объектно-ориентированным языком».

Так почему же объектно-ориентированное программирование — это плохо?

Моя оценка ООП касается основных принципов этой парадигмы. Мои возражения против некоторых из них приводятся ниже.

Возражение 1: структуры данных и функции не должны быть связанными друг с другом

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

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

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

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

Подробнее о представлении функции как ящика можно узнать из нашего бесплатного курса «Введение в программирование».

Обычно «понимание» ограничивается наблюдением, что функция — это часть вычислительной системы, чья работа — изменять структуры данных типа Т1 в структуры данных типа Т2.

Так как функции и структуры данных имеют разную природу, не надо считать их одинаковыми и работать с ними как с одинаковыми сущностями.

Возражение 2: всё есть объект

Представьте себе time. В объектно-ориентированном языке time должно быть объектом. В Smalltalk даже число 3 — объект. Но в языках, которые не относятся к объектно-ориентированным, time — экземпляр типа данных. Например, в Erlang есть много разных вариантов времени, которые можно чётко и однозначно определить.

-deftype day()     = 1..31.
-deftype month()   = 1..12.
-deftype year()    = int().
-deftype hour()    = 1..24.
-deftype minute()  = 1..60.
-deftype second()  = 1..60.
-deftype abstime() = {abstime,year(),month(),day(),hour(),min(),sec()}.
-deftype hms()     = {hms,hour(),min(),sec()}.
…

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

Возражение 3: в объектно-ориентированных языках определения типов данных распространяются везде

В объектно-ориентированных языках определения типов данных принадлежат объектам. Поэтому невозможно найти все определения типов данных в одном месте. В Erlang или C можно определить все типы в одном включаемом файле или словаре данных. В объектно-ориентированном языке такое невозможно — определения распространяются везде.

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

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

Глобальный тип может быть связанным списком, массивом, хэш-таблицей или более сложным объектом.

В объектно-ориентированном языке мне придётся выбирать объект, в котором я определю глобальный тип. Все остальные объекты, которые захотят использовать эту структуру данных, должны наследовать этот объект. Допустим, я хочу создать объект time, в котором определяется эта структура данных, и в котором… Это очень сложно.

Возражение 4: у объектов есть приватное состояние

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

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

С учётом того, что в реальном мире состояния распространены, какие условия должен обеспечить язык программирования для работы с состояниями?

  • Объектно-ориентированные языки говорят «прячьте состояние от программиста». Состояние скрывается, и доступ к нему можно получить с помощью функций.
  • Обычные языки программирования, например, C или Pascal, говорят, что состояние переменных контролируется правилами области видимости языка.
  • Чистые декларативные языки говорят, что состояния не существует. Глобальное состояние системы передаётся во все функции и выходит из всех функций. Механизмы типа монад в функциональных языках и DC-грамматики в логических языках программирования используются, чтобы спрятать состояние от программистов. Поэтому программисты могут работать так, как будто состояние не имеет значение, но при этом у них может быть доступ к состоянию системы, если это необходимо.

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

Почему же ООП популярно?

  1. Считалось, что объектно-ориентированное программирование проще изучать.
  2. Считалось, что в ООП проще переиспользовать код.
  3. ООП было на волне хайпа.
  4. ООП создало целую индустрию программного обеспечения.

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

Вот где реальная причина популярности ООП.

Адаптированный перевод статьи Why OO Sucks by Joe Armstrong. Мнение администрации Хекслета может не совпадать с мнением автора оригинальной публикации.

Объектно-ориентированный JavaScript для начинающих — Изучение веб-разработки

Разобравшись с основами, сосредоточимся на объектно-ориентированном JavaScript (OOJS) — данная статья даёт базовое представление о теории объектно-ориентированного программирования (ООП), далее рассмотрено как JavaScript эмулирует классы объектов с помощью функции-конструктора и как создаются экземпляры объектов.

Необходимые знания:

Базовая компьютерная грамотность, базовое понимание HTML и CSS, знакомство с основами JavaScript (см. Первые шаги и Cструктурные элементы JavaScript) и основы OOJS (см. Введение в объекты).

Цель:Понять основную теорию объектно-ориентированного программирования, как это относится к JavaScript («все является объектом») и как создавать конструкторы и экземпляры объектов.

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

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

Определение шаблона объекта

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

Вернёмся к объекту Person из нашей статьи Основы объектов, который определяет общие сведения и функциональные возможности человека. Есть много вещей, которые вы можете узнать о человеке (его адрес, рост, размер обуви, профиль ДНК, номер паспорта, значимые черты личности …), но в данном случае нас интересует только имя, возраст, пол и интересы, а также мы хотим иметь возможность написать краткую информацию о нём, основываясь на этих данных, и сделать так, чтобы он поздоровался. Это известно как абстракция — создание простой модели более сложной сущности, которая представляет её наиболее важные аспекты таким образом, чтобы с ней было удобно работать для выполнения целей нашей программы.

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

Создание реальных объектов

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

Когда экземпляр объекта создаётся из класса, для его создания выполняется функция-конструктор класса. Этот процесс создания экземпляра объекта из класса называется создание экземпляра (instantiation) — из класса создаётся экземпляр объекта.

Специализированные классы

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

Это действительно полезно — преподаватели и студенты имеют много общих характеристик, таких как имя, пол и возраст, и удобно определить их только один раз. Вы можете также задать одну и ту же характеристику отдельно в разных классах, поскольку каждое определение этой характеристики будет находиться в отдельном пространстве имён. Например, приветствие студента может быть в форме «Yo, I’m [firstName]» (например Yo, I’m Sam), в то время как учитель может использовать что-то более формальное, такое как «Hello, my name is [Prefix] [lastName], and I teach [Subject].» (например Hello, My name is Mr Griffiths, and I teach Chemistry).

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

Теперь вы можете создавать экземпляры объекта из дочерних классов. Например:

Далее мы рассмотрим, как ООП теорию можно применить на практике в JavaScript.

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

Рассмотрим создание классов через конструкторы и создание экземпляров объектов из них в JavaScript. Прежде всего, мы хотели бы, чтобы вы создали новую локальную копию файла oojs.html, который мы видели в нашей первой статье «Объекты».

Простой пример

  1. Давайте рассмотрим как можно определить человека с нормальной функцией. Добавьте эту функцию в элемент script:
    function createNewPerson(name) {
      const obj = {};
      obj.name = name;
      obj.greeting = function() {
        alert('Hi! I\'m ' + this.name + '.');
      };
      return obj;
    }
  2. Теперь вы можете создать нового человека, вызвав эту функцию — попробуйте следующие строки в консоли JavaScript браузера:
    const salva = createNewPerson('Salva');
    salva.name;
    salva.greeting();

    Это работает достаточно хорошо, но код излишне многословен; если мы знаем, что хотим создать объект, зачем нам явно создавать новый пустой объект и возвращать его? К счастью, JavaScript предоставляет нам удобный способ в виде функций-конструкторов — давайте сделаем это сейчас!

  3. Замените предыдущую функцию следующей:
    function Person(name) {
      this.name = name;
      this.greeting = function() {
        alert('Hi! I\'m ' + this.name + '.');
      };
    }

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

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

Итак, как мы вызываем конструктор для создания некоторых объектов?

  1. Добавьте следующие строки под предыдущим добавлением кода:
    let person1 = new Person('Bob');
    let person2 = new Person('Sarah');
  2. Сохраните код и перезагрузите его в браузере и попробуйте ввести следующие строки в консоль JS:
    person1.name
    person1.greeting()
    person2.name
    person2.greeting()

Круто! Теперь, как вы видите, у нас есть два новых объекта на странице, каждый из которых хранится в отдельном пространстве имён — при доступе к их свойствам и методам вы должны начинать вызовы с person1 или person2; функциональность, содержащаяся внутри, аккуратно упакована, поэтому она не будет конфликтовать с другими функциями. Тем не менее, у них есть одно и то же свойство name и greeting(). Обратите внимание, что они используют своё собственное значение name, которое было присвоено им, когда они были созданы; это одна из причин, почему очень важно использовать this, таким образом они будут использовать свои собственные значения, а не какие-либо другие.

Давайте снова посмотрим на вызовы конструктора:

let person1 = new Person('Bob');
let person2 = new Person('Sarah');

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

function Person(name) {
  this.name = name;
  this.greeting = function() {
    alert('Hi! I\'m ' + this.name + '.');
  };
}

После создания новых объектов переменные person1 и person2 содержат следующие объекты:

{
  name: 'Bob',
  greeting: function() {
    alert('Hi! I\'m ' + this.name + '.');
  }
}

{
  name: 'Sarah',
  greeting: function() {
    alert('Hi! I\'m ' + this.name + '.');
  }
}

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

Создавая наш готовый конструктор

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

  1. Замените весь предыдущий код новой функцией конструктора — это, в принципе, тот же самое что и в наглядном примере, но несколько сложнее:
    function Person(first, last, age, gender, interests) {
      this.name = {
        first : first,
        last: last
      };
      this.age = age;
      this.gender = gender;
      this.interests = interests;
      this.bio = function() {
        alert(this.name.first + ' ' + this.name.last + ' is ' + this.age + ' years old. He likes ' + this.interests[0] + ' and ' + this.interests[1] + '.');
      };
      this.greeting = function() {
        alert('Hi! I\'m ' + this.name.first + '.');
      };
    };
  2. Теперь добавьте следующую строку ниже, чтобы создать экземпляр объекта из него:
    let person1 = new Person('Bob', 'Smith', 32, 'male', ['music', 'skiing']);

Как вы могли заметить, вы можете получить доступ к свойствам и методам, как это было ранее, — попробуйте использовать их в консоли JS:

person1['age']
person1.interests[1]
person1.bio()

Примечание: Если у вас возникли проблемы с работой кода, попробуйте сравнить его с нашей версией — см. oojs-class-finished.html (также смотрите, как он работает в прямом эфире).

Дальнейшие упражнения

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

Кроме того, есть несколько проблем с нашим методом bio() — вывод всегда включает местоимение «He» («Он» в пер. с англ.), даже если ваш человек является женщиной или какой-либо другой предпочтительной гендерной классификацией. И bio будет включать только два интереса, даже если в массиве interests указано больше. Можете ли вы решить, как исправить это в определении класса (конструкторе)? Вы можете поместить любой код, который вам нравится внутри конструктора (вам, вероятно, понадобятся несколько условий и цикл). Подумайте о том, как предложения должны быть структурированы по-разному в зависимости от пола и в зависимости от того, имеет ли число перечисленных интересов 1, 2 или более 2.

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

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

Конструктор Object ()

Прежде всего, вы можете использовать конструктор Object() для создания нового объекта. Да, даже общие объекты имеют конструктор, который генерирует пустой объект.

  1. Попробуйте ввести это в консоль JavaScript вашего браузера:
    let person1 = new Object();
  2. Это сохраняет ссылку на пустой объект в переменную person1. Затем вы можете добавить свойства и методы к этому объекту с использованием точечной или скобочной нотации по желанию; попробуйте эти примеры в консоли:
    person1.name = 'Chris';
    person1['age'] = 38;
    person1.greeting = function() {
      alert('Hi! I\'m ' + this.name + '.');
    };
  3. Вы также можете передать литерал объекта конструктору Object() в качестве параметра, чтобы заполнить его свойствами / методами. Попробуйте это в консоли JS:
    let person1 = new Object({
      name: 'Chris',
      age: 38,
      greeting: function() {
        alert('Hi! I\'m ' + this.name + '.');
      }
    });

Использование метода create()

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

Однако некоторые люди предпочитают создавать экземпляры объектов без предварительного создания конструкторов, особенно если они создают только несколько экземпляров объекта. JavaScript имеет встроенный метод create(), который позволяет вам это делать. С его помощью вы можете создать новый объект на основе любого существующего объекта.

  1. Закончив упражнение из предыдущего раздела, загруженное в браузер, попробуйте это в консоли JavaScript:
    let person2 = Object.create(person1);
  2. Теперь попробуйте:
    person2.name
    person2.greeting()

Вы увидите, что person2 был создан на основе person1 — он имеет те же свойства и метод, доступные для него.

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

Подробнее мы рассмотрим особенности метода create() немного позже.

В этой статье представлен упрощённый взгляд на объектно-ориентированную теорию — это ещё не вся история, но она даёт представление о том, с чем мы имеем дело. Кроме того, мы начали рассматривать различные способы создания экземпляров объектов.

В следующей статье мы рассмотрим прототипы объектов JavaScript.

Объектно-ориентированное программирование. Классы и объекты

Сегодня мы поговорим об объектно-ориентированном программировании и о его применении в python.

Объектно-ориентированное программирование (ООП) — парадигма программирования, в которой основными концепциями являются понятия объектов и классов.

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

Python соответствует принципам объектно-ориентированного программирования. В python всё является объектами — и строки, и списки, и словари, и всё остальное.

Но возможности ООП в python этим не ограничены. Программист может написать свой тип данных (класс), определить в нём свои методы.

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

Приступим теперь собственно к написанию своих классов на python. Попробуем определить собственный класс:

>>> # Пример простейшего класса, который ничего не делает
... class A:
...     pass

Теперь мы можем создать несколько экземпляров этого класса:

>>> a = A()
>>> b = A()
>>> a.arg = 1  # у экземпляра a появился атрибут arg, равный 1
>>> b.arg = 2  # а у экземпляра b - атрибут arg, равный 2
>>> print(a.arg)
1
>>> print(b.arg)
2
>>> c = A()
>>> print(c.arg)  # а у этого экземпляра нет arg
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'A' object has no attribute 'arg'

Классу возможно задать собственные методы:

>>> class A:
...     def g(self): # self - обязательный аргумент, содержащий в себе экземпляр
...                  # класса, передающийся при вызове метода,
...                  # поэтому этот аргумент должен присутствовать
...                  # во всех методах класса.
...         return 'hello world'
...
>>> a = A()
>>> a.g()
'hello world'

И напоследок еще один пример:

>>> class B:
...     arg = 'Python' # Все экземпляры этого класса будут иметь атрибут arg,
...                    # равный "Python"
...                    # Но впоследствии мы его можем изменить
...     def g(self):
...         return self.arg
...
>>> b = B()
>>> b.g()
'Python'
>>> B.g(b)
'Python'
>>> b.arg = 'spam'
>>> b.g()
'spam'

Объектно-ориентированное Программирование в Python

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

Содержание

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

Есть вопросы по Python?

На нашем форуме вы можете задать любой вопрос и получить ответ от всего нашего сообщества!

Telegram Чат & Канал

Вступите в наш дружный чат по Python и начните общение с единомышленниками! Станьте частью большого сообщества!

Паблик VK

Одно из самых больших сообществ по Python в социальной сети ВК. Видео уроки и книги для вас!

Один из очевидных ответов на этот вопрос — гоночный болид. Условный болид может обладать такими характеристиками как:

  • мощность двигателя;
  • марка;
  • модель;
  • производитель, и т. д.

Соответственно, болид можно запустить, остановить, ускорить, и так далее. Гонщик может быть еще одним объектом в Формуле-1. Гонщик имеет национальность, возраст, пол, и так далее, кроме этого, он обладает таким функционалом, как управление болидом, рулевое управление, переключение передач.

Как и в этом примере, в объектно-ориентированном программировании мы создадим объекты, которые будут соответствовать реальным аспектам.

Стоит обратить внимание на то, что объектно-ориентированное программирование — не зависящая от языка программирования концепция. Это общая концепция программирования и большинство современных языков, такие как Java, C#, C++ и Python поддерживают объектно-ориентированное программирование.

В этой статье мы разберем подробную инструкцию объектно-ориентированного программирования в Python, но перед этим, рассмотрим некоторые преимущества и недостатки объектно-ориентированного программирования.

Преимущества и недостатки ООП Python

Рассмотрим несколько основных преимуществ объектно-ориентированного программирования:

  1. Объектно-ориентированное программирование подразумевает повторное использование. Компьютерная программа написанная в форме объектов и классов может быть использована снова в других проектах без повторения кода;
  2. Использование модулярного подхода в объектно-ориентированном программировании позволяет получить читаемый и гибкий код;
  3. В объектно-ориентированном программировании каждый класс имеет определенную задачу. Если ошибка возникнет в одной части кода, вы можете исправить ее локально, без необходимости вмешиваться в другие части кода;
  4. Инкапсуляция данных (которую мы рассмотрим дальше в статье) вносит дополнительный уровень безопасности в разрабатываемую программу с использованием объектно-ориентированного подхода;

Хотя объектно-ориентированное программирование обладает рядом преимуществ, оно также содержит определенные недостатки, некоторые из них находятся в списке ниже:

  1. Для создания объектов необходимо иметь подробное представление о разрабатываемом программном обеспечении;
  2. Не каждый аспект программного обеспечения является лучшим решением для реализации в качестве объекта. Для новичков может быть тяжело прочертить линию в золотой середине;
  3. С тем, как вы вносите все новые и новые классы в код, размер и сложность программы растет в геометрической прогрессии;

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

Как и следует из названия, объектно-ориентированное программирование — это речь об объектах. Однако, перед тем как создать объект, нам нужно определить его класс.

Класс

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

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

Отношение между классом и объектом можно представить более наглядно, взглянув на отношение между машиной и Audi. Да, Audi – это машина. Однако, нет такой вещи, как просто машина. Машина — это абстрактная концепция, которую также реализуют в Toyota, Honda, Ferrari, и других компаниях.

Ключевое слово class используется для создания класса в Python. Название класса следует за ключом class, за которым следует двоеточие. Тело класса начинается с новой строки, с отступом на одну вкладку влево.

Давайте рассмотрим, как мы можем создать самый простой класс в Python. Взглянем на следующий код:

# Создаем класс Car
class Car:

# создаем атрибуты класса
name = «c200»
make = «mercedez»
model = 2008

# создаем методы класса
def start(self):
print («Заводим двигатель»)

def stop(self):
print («Отключаем двигатель»)

# Создаем класс Car

class Car:

 

    # создаем атрибуты класса

    name = «c200»

    make = «mercedez»

    model = 2008

 

    # создаем методы класса

    def start(self):

        print («Заводим двигатель»)

 

    def stop(self):

        print («Отключаем двигатель»)

В примере выше мы создали класс под названием Car с тремя атрибутами: имя name, марка make и модель model. Наш класс также содержит два метода: start() и stop().

Объекты

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

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

Объект также называется экземпляром. Тем не менее, процесс создания объекта класса называется инициализация. В Python, чтобы создать объект класса, нам просто нужно вписать название класса, с последующими открывающимися и закрывающимися скобками.

Давайте создадим объект класса Car, который мы создали в предыдущем разделе.

# Создаем объект класса Car под названием car_a
car_a = Car()

# Создаем объект класса Car под названием car_b
car_b = Car()

# Создаем объект класса Car под названием car_a

car_a = Car()

 

# Создаем объект класса Car под названием car_b

car_b = Car()

В этом скрипте мы создали два объекта класса Car: car_a и car_b. Чтобы узнать тип созданных нами объектов, мы можем использовать метод type и передать ему названия наших объектов. Выполните следующий код:

В выдаче вы увидите:

Это говорит нам о том, что тип объекта car_b – класс Car.

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

В этом скрипте мы вызываем метод start() через объект car_b. Выдача будет выглядеть следующим образом:

Заводим двигатель

Заводим двигатель

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

В выдаче вы увидите значение атрибута модели, как показано ниже:

Атрибуты класса

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

В Python, каждый объект содержит определенные атрибуты по умолчанию и методы в дополнение к определенным пользователем атрибутами. Чтобы посмотреть на все атрибуты и методы объекта, используйте встроенную функцию под названием dir(). Попробуем взглянуть на все атрибуты объекта car_b, который мы создали в предыдущем разделе. Выполните следующий скрипт:

В выдаче вы увидите следующие атрибуты:

[‘__class__’,
‘__delattr__’,
‘__dict__’,
‘__dir__’,
‘__doc__’,
‘__eq__’,
‘__format__’,
‘__ge__’,
‘__getattribute__’,
‘__gt__’,
‘__hash__’,
‘__init__’,
‘__init_subclass__’,
‘__le__’,
‘__lt__’,
‘__module__’,
‘__ne__’,
‘__new__’,
‘__reduce__’,
‘__reduce_ex__’,
‘__repr__’,
‘__setattr__’,
‘__sizeof__’,
‘__str__’,
‘__subclasshook__’,
‘__weakref__’,
‘make’,
‘model’,
‘name’,
‘start’,
‘stop’]

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

[‘__class__’,

‘__delattr__’,

‘__dict__’,

‘__dir__’,

‘__doc__’,

‘__eq__’,

‘__format__’,

‘__ge__’,

‘__getattribute__’,

‘__gt__’,

‘__hash__’,

‘__init__’,

‘__init_subclass__’,

‘__le__’,

‘__lt__’,

‘__module__’,

‘__ne__’,

‘__new__’,

‘__reduce__’,

‘__reduce_ex__’,

‘__repr__’,

‘__setattr__’,

‘__sizeof__’,

‘__str__’,

‘__subclasshook__’,

‘__weakref__’,

‘make’,

‘model’,

‘name’,

‘start’,

‘stop’]

Эта встроенная функция очень полезна при изучении атрибутов и функций объекта, особенно при использовании через REPL.

Атрибуты класса против атрибутов экземпляров

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

  • атрибуты класса
  • атрибуты экземпляров

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

Атрибуты экземпляра объявляются внутри любого метода, в то время как атрибуты класса объявляются вне любого метода.

Следующий пример прояснит эту разницу:

class Car:

# создаем атрибуты класса
car_count = 0

# создаем методы класса
def start(self, name, make, model):
print(«Двигатель заведен»)
self.name = name
self.make = make
self.model = model
Car.car_count += 1

class Car:

 

    # создаем атрибуты класса

    car_count = 0

 

    # создаем методы класса

    def start(self, name, make, model):

        print(«Двигатель заведен»)

        self.name = name

        self.make = make

        self.model = model

        Car.car_count += 1

В указанном выше скрипте мы создаем класс Car с одним атрибутом класса под названием car_count и три атрибута экземпляра под названием name, make и model. Класс содержит один метод start(), который содержит наши три атрибута экземпляров. Значения атрибутов экземпляров переданы в качестве аргументов методу start(). Внутри метода start, атрибут car_count увеличен на один.

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

Давайте создадим объект класса Car и вызовем метод start().

car_a = Car()
car_a.start(«Corrola», «Toyota», 2015)
print(car_a.name)
print(car_a.car_count)

car_a = Car()  

car_a.start(«Corrola», «Toyota», 2015)  

print(car_a.name)  

print(car_a.car_count)

В скрипте выше мы вывели название атрибута экземпляра и атрибута класса car_count. В выдаче вы увидите, что атрибут car_count будет иметь значение 1, как показано ниже:

Двигатель заведен
Corrola
1

Двигатель заведен

Corrola  

1

Теперь создадим еще один объект класса Car и вызываем метод start().

car_b = Car()
car_b.start(«City», «Honda», 2013)
print(car_b.name)
print(car_b.car_count)

car_b = Car()  

car_b.start(«City», «Honda», 2013)  

print(car_b.name)  

print(car_b.car_count)

Сейчас если вы выведите значение атрибута car_count, вы увидите 2 в выдаче. Это связано с тем, что атрибут car_count является атрибутом класса и таким образом он разделяется между экземплярами. Объект car_a увеличил свое значение до 1, в то время как car_b увеличил свое значение еще раз, так что итоговое значение равняется 2. Выдача выглядит следующим образом:

Двигатель заведен
City
2

Двигатель заведен

City  

2

Методы

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

Статичные методы

Для объявления статического метода, вам нужно указать дескриптор @staticmethod перед названием метода, как показано ниже:

class Car:

@staticmethod
def get_class_details():
print («Это класс Car»)

Car.get_class_details()

class Car:

 

    @staticmethod

    def get_class_details():

        print («Это класс Car»)

 

Car.get_class_details()

В коде выше мы создали класс Car с одним статичным методом get_class_details(). Давайте вызовем этот метод, используя название класса.

Вы можете видеть что нам не нужно создавать экземпляр класса Car для вызова метода get_class_details(), вместо этого мы просто использовали название класса. Стоит упомянуть, что статические методы могут иметь доступ только к атрибутам класса в Python, вы не сможете обратиться к методам через self.

Возврат множественных значений из метода

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

class Square:

@staticmethod
def get_squares(a, b):
return a*a, b*b

print(Square.get_squares(3, 5))

class Square:

 

    @staticmethod

    def get_squares(a, b):

        return a*a, b*b

 

print(Square.get_squares(3, 5))

В скрипте выше мы создали класс под названием Square со статичным методом get_squares(). Метод принимает два параметра. Он умножает каждый параметр на себя и возвращает оба результата при помощи оператора return. В выдаче указанного выше скрипта вы увидите квадраты 3 и 5.

Метод str

До этого момента мы выводили атрибуты при помощи метода print(). Посмотрим, что случится, если мы выведем объект класса.

Для этого нам нужно создать простой класс Car с одним методом и попытаться вывести объект класса в консоль. Выполним следующий скрипт:

class Car:

# создание методов класса
def start(self):
print («Двигатель заведен»)

car_a = Car()
print(car_a)

class Car:

 

    # создание методов класса

    def start(self):

        print («Двигатель заведен»)

 

car_a = Car()  

print(car_a)

В скрипте выше мы создали объект car_a класса Car и вывели его значение на экран. По сути мы относимся к объекту car_a как к строке. Выдача выглядит следующим образом:

<__main__.Car object at 0x000001CCCF4335C0>

<__main__.Car object at 0x000001CCCF4335C0>

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

# создание класса Car
class Car:

# создание методов класса
def __str__(self):
return «Car class Object»

def start(self):
print («Двигатель заведен»)

car_a = Car()
print(car_a)

# создание класса Car

class Car:

 

    # создание методов класса

    def __str__(self):

        return «Car class Object»

 

    def start(self):

        print («Двигатель заведен»)

 

car_a = Car()  

print(car_a)

В скрипте выше, мы переопределили метод __str__ , предоставив наше собственное определение метода. Теперь, если вы выведите объект car_a, вы увидите сообщение «Car class Object» в консоли. Это сообщение, которое мы внесли в наш пользовательский метод __str__ .

Использование этого метода позволяет вам создавать пользовательские и более осмысленные описания, когда объект выводится. Вы можете даже отобразить кое-какие данные внутри класса, такие как название класса Car.

Конструкторы

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

Для создания конструктора вам нужно создать метод с ключевым словом __init__. Взгляните на следующий пример:

class Car:

# создание атрибутов класса
car_count = 0

# создание методов класса
def __init__(self):
Car.car_count +=1
print(Car.car_count)

class Car:

 

    # создание атрибутов класса

    car_count = 0

 

    # создание методов класса

    def __init__(self):

        Car.car_count +=1

        print(Car.car_count)

В скрипте выше мы создали класс Car с одним атрибутом класса car_count. Класс содержит конструктор, который увеличивает значение car_count и выводит итоговое значение на экран.

Теперь, когда объект класса Car будет создан, конструктор также будет вызван, значение car_count увеличится и отобразится на экране. Создадим простой объект и посмотрим, что выйдет:

car_a = Car()
car_b = Car()
car_c = Car()

car_a = Car()  

car_b = Car()  

car_c = Car()

В выдаче вы увидите выведенное значение 1, 2 и 3, поскольку для каждого объекта значение переменной car_count увеличивается и отображается на экране.

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

Локальные переменные против глобальных

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

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

Локальные переменные

Локальная переменная в классе — это переменная, доступ к которой возможен только внутри блока кода, в котором она определена. Например, если вы определите переменную внутри метода, к нему не удастся получить доступ откуда-либо вне метода. Посмотрим на следующий скрипт:

# создаем класс Car
class Car:
def start(self):
message = «Двигатель заведен»
return message

# создаем класс Car

class Car:  

    def start(self):

        message = «Двигатель заведен»

        return message

В скрипте выше мы создали локальную переменную message внутри метода start() класса Car. Теперь создадим объект класса Car и попытаемся получить доступ к локальной переменной message, как показано ниже:

car_a = Car()
print(car_a.message)

car_a = Car()  

print(car_a.message)

Скрипт выше приводит к следующей ошибке AttributeError:

AttributeError: ‘Car’ object has no attribute ‘message’

AttributeError: ‘Car’ object has no attribute ‘message’

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

Глобальная переменная

Глобальная переменная определяется вне любого блока, то есть метода, операторов-if, и тому подобное. Доступ к глобальной переменной может быть получен где угодно в классе. Рассмотрим следующий пример.

# создаем класс Car
class Car:
message1 = «Двигатель заведен»

def start(self):
message2 = «Автомобиль заведен»
return message2

car_a = Car()
print(car_a.message1)

# создаем класс Car

class Car:  

    message1 = «Двигатель заведен»

 

    def start(self):

        message2 = «Автомобиль заведен»

        return message2

 

car_a = Car()  

print(car_a.message1)

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

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

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

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

Модификаторы доступа

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

  1. публичный — public;
  2. приватный — private;
  3. защищенный — protected.

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

Для создания приватной переменной, вам нужно проставить префикс двойного подчеркивание __ с названием переменной.

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

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

class Car:
def __init__(self):
print («Двигатель заведен»)
self.name = «corolla»
self.__make = «toyota»
self._model = 1999

class Car:  

    def __init__(self):

        print («Двигатель заведен»)

        self.name = «corolla»

        self.__make = «toyota»

        self._model = 1999

Здесь мы создали простой класс Car с конструктором и тремя переменными: name, make, и model (название, марка и модель). Переменная name является публичной, в то время как переменные make и model являются приватными и защищенными, соответственно.

Давайте создадим объект класса Car и попытаемся получить доступ к переменной name. Выполним следующий скрипт:

car_a = Car()
print(car_a.name)

car_a = Car()  

print(car_a.name)

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

Теперь попробуем вывести значение переменной make. Выполняем следующий скрипт:

В выдаче мы получим следующее уведомление об ошибке:

AttributeError: ‘Car’ object has no attribute ‘make’

AttributeError: ‘Car’ object has no attribute ‘make’

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

  • Полиморфизм;
  • Наследование;
  • Инкапсуляция.

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

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

В объектно-ориентированном программировании, наследование означает отношение IS-A. Например, болид — это транспорт. Наследование это одна из самых удивительных концепций объектно-ориентированного программирования, так как оно подразумевает повторное использование.

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

Рассмотрим на очень простой пример наследования. Выполним следующий скрипт:

# Создание класса Vehicle
class Vehicle:
def vehicle_method(self):
print(«Это родительский метод из класса Vehicle»)

# Создание класса Car, который наследует Vehicle
class Car(Vehicle):
def car_method(self):
print(«Это метод из дочернего класса»)

# Создание класса Vehicle

class Vehicle:  

    def vehicle_method(self):

        print(«Это родительский метод из класса Vehicle»)

 

# Создание класса Car, который наследует Vehicle

class Car(Vehicle):  

    def car_method(self):

        print(«Это метод из дочернего класса»)

В скрипте выше мы создаем два класса: Vehicle и Car, который наследует класс Vehicle. Чтобы наследовать класс, вам нужно только вписать название родительского класса внутри скобок, которая следует за названием дочернего класса. Класс Vehicle содержит метод vehicle_method(), а дочерний класс содержит метод car_method(). Однако, так как класс Car наследует класс Vehicle, он также наследует и метод vehicle_method().

Рассмотрим это на практике и выполним следующий скрипт:

car_a = Car()
car_a.vehicle_method() # Вызываем метод родительского класса

car_a = Car()  

car_a.vehicle_method() # Вызываем метод родительского класса

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

Это родительский метод из класса Vehicle

Это родительский метод из класса Vehicle

Множественное наследование Python

В Python, родительский класс может иметь несколько дочерних, и, аналогично, дочерний класс может иметь несколько родительских классов. Давайте рассмотрим первый сценарий. Выполним следующий скрипт:

# создаем класс Vehicle
class Vehicle:
def vehicle_method(self):
print(«Это родительский метод из класса Vehicle»)

# создаем класс Car, который наследует Vehicle
class Car(Vehicle):
def car_method(self):
print(«Это дочерний метод из класса Car»)

# создаем класс Cycle, который наследует Vehicle
class Cycle(Vehicle):
def cycleMethod(self):
print(«Это дочерний метод из класса Cycle»)

# создаем класс Vehicle

class Vehicle:  

    def vehicle_method(self):

        print(«Это родительский метод из класса Vehicle»)

 

# создаем класс Car, который наследует Vehicle

class Car(Vehicle):  

    def car_method(self):

        print(«Это дочерний метод из класса Car»)

 

# создаем класс Cycle, который наследует Vehicle

class Cycle(Vehicle):  

    def cycleMethod(self):

        print(«Это дочерний метод из класса Cycle»)

В этом скрипте, родительский класс Vehicle наследуется двумя дочерними классами — Car и Cycle. Оба дочерних класса будут иметь доступ к vehicle_method() родительского класса. Запустите следующий скрипт, чтобы увидеть это лично:

car_a = Car()
car_a.vehicle_method() # вызов метода родительского класса
car_b = Cycle()
car_b.vehicle_method() # вызов метода родительского класса

car_a = Car()  

car_a.vehicle_method() # вызов метода родительского класса

car_b = Cycle()  

car_b.vehicle_method() # вызов метода родительского класса

В выдаче вы увидите выдачу метода vehicle_method() дважды, как показано ниже:

Это родительский метод из класса Vehicle
Это родительский метод из класса Vehicle

Это родительский метод из класса Vehicle

Это родительский метод из класса Vehicle

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

class Camera:
def camera_method(self):
print(«Это родительский метод из класса Camera»)

class Radio:
def radio_method(self):
print(«Это родительский метод из класса Radio»)

class CellPhone(Camera, Radio):
def cell_phone_method(self):
print(«Это дочерний метод из класса CellPhone»)

class Camera:  

    def camera_method(self):

        print(«Это родительский метод из класса Camera»)

 

class Radio:  

    def radio_method(self):

        print(«Это родительский метод из класса Radio»)

 

class CellPhone(Camera, Radio):  

     def cell_phone_method(self):

        print(«Это дочерний метод из класса CellPhone»)

В скрипте выше мы создали три класса: Camera, Radio, и CellPhone. Классы Camera и Radio наследуются классом CellPhone. Это значит, что класс CellPhone будет иметь доступ к методам классов Camera и Radio. Следующий скрипт подтверждает это:

cell_phone_a = CellPhone()
cell_phone_a.camera_method()
cell_phone_a.radio_method()

cell_phone_a = CellPhone()  

cell_phone_a.camera_method()  

cell_phone_a.radio_method()

Выдача будет выглядеть следующим образом:

Это родительский метод из класса Camera
Это родительский метод из класса Radio

Это родительский метод из класса Camera

Это родительский метод из класса Radio

Полиморфизм

Термин полиморфизм буквально означает наличие нескольких форм. В контексте объектно-ориентированного программирования, полиморфизм означает способность объекта вести себя по-разному.

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

Перегрузка метода

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

# создаем класс Car
class Car:
def start(self, a, b=None):
if b is not None:
print (a + b)
else:
print (a)

# создаем класс Car

class Car:  

   def start(self, a, b=None):

        if b is not None:

            print (a + b)

        else:

            print (a)

В скрипте выше, если метод start() вызывается передачей одного аргумента, параметр будет выведен на экран. Однако, если мы передадим 2 аргумента методу start(), он внесет оба аргумента и выведет результат суммы.

Попробуем с одним аргументом для начала:

car_a = Car()
car_a.start(10)

car_a = Car()  

car_a.start(10)

В выдаче мы можем видеть 10. Теперь попробуем передать два аргумента:

В выдаче вы увидите 30.

Переопределение метода

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

# создание класса Vehicle
class Vehicle:
def print_details(self):
print(«Это родительский метод из класса Vehicle»)

# создание класса, который наследует Vehicle
class Car(Vehicle):
def print_details(self):
print(«Это дочерний метод из класса Car»)

# создание класса Cycle, который наследует Vehicle
class Cycle(Vehicle):
def print_details(self):
print(«Это дочерний метод из класса Cycle»)

# создание класса Vehicle

class Vehicle:  

    def print_details(self):

        print(«Это родительский метод из класса Vehicle»)

 

# создание класса, который наследует Vehicle

class Car(Vehicle):  

    def print_details(self):

        print(«Это дочерний метод из класса Car»)

 

# создание класса Cycle, который наследует Vehicle

class Cycle(Vehicle):  

    def print_details(self):

        print(«Это дочерний метод из класса Cycle»)

В скрипте выше, классы Cycle и Car наследуют класс Vehicle. Класс Vehicle содержит метод print_details(), который переопределен дочерним классом. Теперь, если вы вызовите метод print_details(), выдача будет зависеть от объекта, через который вызывается метод. Выполните следующий скрипт, чтобы понять суть на деле:

car_a = Vehicle()
car_a. print_details()

car_b = Car()
car_b.print_details()

car_c = Cycle()
car_c.print_details()

car_a = Vehicle()  

car_a. print_details()

 

car_b = Car()  

car_b.print_details()

 

car_c = Cycle()  

car_c.print_details()

Выдача будет выглядеть вот так:

Это родительский метод из класса Vehicle
Это дочерний метод из класса Car
Это дочерний метод из класса Cycle

Это родительский метод из класса Vehicle

Это дочерний метод из класса Car

Это дочерний метод из класса Cycle

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

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

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

Чтобы предоставить контролируемый доступ к данным класса в Python, используются модификаторы доступа и свойства. Мы уже ознакомились с тем, как действуют модификаторы доступа. В этом разделе мы посмотрим, как действуют свойства.

Предположим, что нам нужно убедиться в том, что модель автомобиля должна датироваться между 2000 и 2018 годом. Если пользователь пытается ввести значение меньше 2000 для модели автомобиля, значение автоматически установится как 2000, и если было введено значение выше 2018, оно должно установиться на 2018. Если значение находится между 2000 и 2018 — оно остается неизменным. Мы можем создать свойство атрибута модели, которое реализует эту логику. Взглянем на пример:

# создаем класс Car
class Car:

# создаем конструктор класса Car
def __init__(self, model):
# Инициализация свойств.
self.model = model

# создаем свойство модели.
@property
def model(self):
return self.__model

# Сеттер для создания свойств.
@model.setter
def model(self, model):
if model < 2000:
self.__model = 2000
elif model > 2018:
self.__model = 2018
else:
self.__model = model

def getCarModel(self):
return «Год выпуска модели » + str(self.model)

carA = Car(2088)
print(carA.getCarModel())

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

# создаем класс Car

class Car:

 

    # создаем конструктор класса Car

    def __init__(self, model):

        # Инициализация свойств.

        self.model = model

 

    # создаем свойство модели.

    @property

    def model(self):

        return self.__model

 

    # Сеттер для создания свойств.

    @model.setter

    def model(self, model):

        if model < 2000:

            self.__model = 2000

        elif model > 2018:

            self.__model = 2018

        else:

            self.__model = model

 

    def getCarModel(self):

        return «Год выпуска модели » + str(self.model)

 

carA = Car(2088)  

print(carA.getCarModel())

Свойство имеет три части. Вам нужно определить атрибут, который является моделью в скрипте выше. Затем, вам нужно определить свойство атрибута, используя декоратор @property. Наконец, вам нужно создать установщик свойства, который является дескриптором @model.setter в примере выше.

Теперь, если вы попробуете ввести значение выше 2018 в атрибуте модели, вы увидите, что значение установлено на 2018. Давайте проверим это. Выполним следующий скрипт:

car_a = Car(2088)
print(car_a.get_car_model())

car_a = Car(2088)  

print(car_a.get_car_model())

Здесь мы передаем 2088 как значение для модели, однако, если вы введете значение для атрибута модели через функцию get_car_model(), вы увидите 2018 в выдаче.

Подведем итоги

В этой статье мы освоили часть важнейших основ объектно-ориентированного программирования. Этот тип программирования — один из самых популярных и используемых парадигм.

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

Являюсь администратором нескольких порталов по обучению языков программирования Python, Golang и Kotlin. В составе небольшой команды единомышленников, мы занимаемся популяризацией языков программирования на русскоязычную аудиторию. Большая часть статей была адаптирована нами на русский язык и распространяется бесплатно.

E-mail: [email protected]

Образование
Universitatea Tehnică a Moldovei (utm.md)

  • 2014 — 2018 Технический Университет Молдовы, ИТ-Инженер. Тема дипломной работы «Автоматизация покупки и продажи криптовалюты используя технический анализ»
  • 2018 — 2020 Технический Университет Молдовы, Магистр, Магистерская диссертация «Идентификация человека в киберпространстве по фотографии лица»

Что такое объектно-ориентированное программирование? Подробное объяснение ООП

Примечание. Родительский класс также известен как суперкласс или базовый класс. Дочерний класс также можно назвать производным классом или расширенным классом.

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

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

Метод herding () определен в дочернем классе HerdingDog , поэтому два объекта, Maisel и Fluffy , созданные из класса HerdingDog , имеют доступ к методу herding () .

Rufus — это объект, созданный из родительского класса Dog , поэтому Rufus имеет доступ только к методу bark () .

Объект Создан из класса Родительский класс Методы
Руфус Собака НЕТ кора ()
Майзель Пастушья собака Собака кора (), стадо ()
Пушистый Пастушья собака Собака кора (), стадо ()

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

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

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

Инкапсуляция требует определения некоторых полей как частных, а некоторых как общедоступных.

  • Частный / внутренний интерфейс: методов и свойств, доступных из других методов того же класса.
  • Открытый / внешний интерфейс: методов и свойств, доступных также извне класса.

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

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

Если все это ненавидят, почему ООП до сих пор так широко распространен?

В августовском выпуске журнала Byte за 1981 г. Дэвид Робсон открывает свою статью, ставшую для многих введением в «объектно-ориентированные программные системы», сразу признавая, что это отход от того, что многие знакомы с императивом. вниз привыкли к программированию.

«Многие люди, не имеющие представления о том, как работает компьютер, находят идею объектно-ориентированного программирования вполне естественной.Напротив, многие люди, имеющие опыт работы с компьютерами, сначала думают, что в объектно-ориентированных системах есть что-то странное ».

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

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

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

Успех был просто совпадением?

ООП по-прежнему остается одной из доминирующих парадигм. Но это может быть связано с успехом языков, которые оказываются ООП. Java, C ++ и Kotlin управляют мобильными устройствами для Android и Swift и Objective-C для iOS, поэтому вы не сможете разрабатывать программное обеспечение для мобильных устройств, если не понимаете объектно-ориентированный подход. Для Интернета это JavaScript, Python, PHP и Ruby.

Спрашивать, почему так много широко используемых языков являются ООП, можно путать причину и следствие.Ричард Фельдман в своем выступлении утверждает, что это могло быть просто совпадением. C ++ был разработан в начале 1980-х Бьярном Страуструпом, первоначально как набор расширений языка программирования C. Основываясь на C, C ++ добавил объектную ориентацию, но Фельдман утверждает, что он стал популярным благодаря общему обновлению с C, включая безопасность типов и добавленную поддержку автоматического управления ресурсами, общего программирования и обработки исключений, среди других функций.

Затем Java захотела обратиться к программистам на C ++ и удвоила объем ООП.В конечном итоге Sun Microsystems захотела повторить трюк с C ++, стремясь сделать его максимально понятным для разработчиков, внедряющих Java.

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

Что уникального может делать ООП?

У ООП есть несколько ценных аспектов, некоторые из которых делают его вездесущим, даже если у него есть свои недостатки.Давайте посмотрим на краеугольные камни ООП.

Инкапсуляция . Это означает, что данные обычно скрыты от других частей языка — помещены в капсулу, если хотите. ООП по умолчанию инкапсулирует данные; объекты содержат как данные, так и методы, которые влияют на эти данные, и хорошая практика ООП означает, что вы предоставляете методы получения и установки для управления доступом к этим данным. Это защищает изменяемые данные от изменения волей-неволей и делает данные приложения более безопасными.

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

Наследование . Поскольку объекты могут быть созданы как подтипы других объектов, они могут наследовать переменные и методы этих объектов. Это позволяет объектам поддерживать операции, определенные предшествующими типами, без необходимости предоставлять собственное определение. Цель состоит в том, чтобы не повторяться — сложно поддерживать многократное использование одного и того же кода. Но функциональное программирование может также достичь СУХОГО за счет функций многократного использования. То же самое и с эффективностью памяти. Несмотря на то, что наследование действительно способствует этому, концепция замыканий в FP тоже вносит свой вклад.

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

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

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

Что нас ждет?

Однако

ООП оказался чрезвычайно успешным. Возможно, этот успех является следствием огромной индустрии, которая поддерживает ООП.

Так что же сами разработчики? Наш опрос разработчиков в этом году показывает, что они приобретают все больше и больше покупательского влияния. Что ж, если мы также посмотрим на то, с чем предпочитают работать разработчики, Haskell и Scala — одни из самых любимых языков программирования. Scala приносит вам вторую по величине зарплату.Так что, возможно, с большим количеством проповедей FP, они тоже поднимутся в списке самых популярных языков.

Есть некоторое движение, большие компании, такие как Twitter, почти полностью используют свой бэкэнд на коде Scala. Facebook, который недавно применял Haskell и многие из основных языков ООП, также принимает функциональные возможности. В .NET есть LINQ, а в Java 8 появились Lambdas. JavaScript становится все более функциональным, несмотря на введение классов в ES6. Swift может быть золотой серединой между объектно-ориентированным и функциональным языками.Так что, возможно, нет необходимости выбирать: у вас тоже может быть торт класса и EatCake () .

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

Теги: функциональное программирование, объектно-ориентированное программирование, oop

Как объяснить концепции объектно-ориентированного программирования 6-летнему

Александра Петкова

Вы замечали, что одни и те же клише вопросы всегда задаются на собеседовании — снова и снова?

Я уверен, вы понимаете, о чем я.

Например:

Где вы видите себя через пять лет?

или, что еще хуже:

Что вы считаете своей самой большой слабостью?

Ух… дай мне передохнуть. Считаю ответ на этот вопрос большой слабостью! Во всяком случае, я не о том.

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

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

Сегодня я хочу поговорить о подобном типе вопросов в мире программирования:

Каковы основные принципы объектно-ориентированного программирования?

Я был по обе стороны этого вопроса. Это одна из тех тем, которые задают так часто, что вы не можете позволить себе не знать.

На него обычно приходится отвечать младшим разработчикам и разработчикам начального уровня. Потому что это простой способ для интервьюера сказать три вещи:

  1. Подготовился ли кандидат к этому собеседованию?
    Бонусные баллы, если ответ услышишь сразу — это серьезный подход.
  2. Кандидат прошел этап обучения?
    Понимание принципов объектно-ориентированного программирования (ООП) показывает, что вы вышли за рамки копирования и вставки из руководств — вы уже видите вещи с более высокой точки зрения.
  3. Понимание кандидата глубокое или поверхностное?
    Уровень компетенции по этому вопросу часто равен уровню компетенции по большинству других предметов . Поверьте мне.

Как выглядит разработчик начального уровня после ответа на этот вопрос!

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

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

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

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

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

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

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

Допустим, мы создаем крошечную игру Sims.Есть люди и есть кошка. Они общаются друг с другом. Мы хотим применить инкапсуляцию, поэтому мы инкапсулируем всю логику «cat» в класс Cat . Это может выглядеть так:

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

Здесь «состояние» кота — это частных переменных настроение , голод и энергия . Он также имеет частный метод meow () . Он может вызывать его, когда захочет, другие классы не могут сказать кошке, когда мяукать.

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

Это инкапсуляция.

Абстракция

Абстракцию можно рассматривать как естественное расширение инкапсуляции.

В объектно-ориентированном дизайне программы часто бывают очень большими.А отдельные объекты много общаются друг с другом. Так что поддерживать такую ​​большую кодовую базу в течение многих лет — с постоянными изменениями — сложно.

Абстракция — это концепция, призванная облегчить эту проблему.

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

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

Думаю — кофеварка.Он много чего делает и издает причудливые звуки под капотом. Но все, что вам нужно сделать, это налить кофе и нажать кнопку.

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

Еще один реальный пример абстракции?
Подумайте о том, как вы используете свой телефон:

Сотовые телефоны сложны. Но пользоваться ими просто.

Вы взаимодействуете со своим телефоном, используя всего несколько кнопок.Что происходит под капотом? Вам не обязательно знать — детали реализации скрыты. Вам нужно знать лишь небольшой набор действий.

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

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

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

Но знаете ли вы, что является еще одной распространенной проблемой в дизайне ООП?

Объекты часто очень похожи. У них общая логика.Но они не совсем те же . Ух…

Так как же нам повторно использовать общую логику и выделить уникальную логику в отдельный класс? Один из способов добиться этого — наследование.

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

Дочерний класс повторно использует все поля и методы родительского класса (общая часть) и может реализовать свою собственную (уникальную часть).

Например:

Частный учитель — это тип учителя.А любой учитель — это тип Человека.

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

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

Полиморфизм

Мы подошли к самому сложному слову! Полиморфизм в переводе с греческого означает «множество форм».

Итак, мы уже знаем силу наследования и с радостью пользуемся ею.Но возникает такая проблема.

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

Это можно решить с помощью полиморфизма.

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

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

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

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

Треугольник, Круг и Прямоугольник теперь можно использовать в одной коллекции

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

Затем, если этот список пытается вычислить поверхность для элемента, правильный метод будет найден и выполнен.Если элемент является треугольником, вызывается функция треугольника CalculateSurface () . Если это круг, то вызывается функция круга CalculateSurface () . И так далее.

Если у вас есть функция, которая работает с фигурой, используя ее параметр, вам не нужно определять ее три раза — один раз для треугольника, круга и прямоугольника.

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

Надеюсь, это помогло. Вы можете напрямую использовать те же самые объяснения на собеседовании.

Если вам что-то все еще сложно понять — не стесняйтесь спрашивать в комментариях ниже.

Что дальше?

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

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

Следите за обновлениями.

Понравилось прочитанное? Если вы хотите поддержать меня, вы можете купить мне кофе 🙂

Объектно-ориентированное программирование Значение и определение

O bject- o riented p rogramming ( OOP ) относится к типу компьютерного программирования (разработка программного обеспечения), в котором программисты определяют тип данных структуры данных, а также типы операций. (функции), которые можно применить к структуре данных.

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

Основные концепции ООП

Если вы новичок в объектно-ориентированных языках программирования, вам необходимо знать несколько основ, прежде чем вы сможете начать работу с кодом. Следующие определения Webopedia помогут вам лучше понять объектно-ориентированное программирование:

  • Абстракция: Процесс выделения (абстрагирования) общих черт объектов и процедур.
  • Класс: Категория объектов. Класс определяет все общие свойства различных объектов, которые ему принадлежат.
  • Инкапсуляция: Процесс объединения элементов для создания нового объекта. Процедура — это тип инкапсуляции, поскольку она объединяет серию компьютерных инструкций.
  • Скрытие информации: Процесс скрытия деталей объекта или функции. Скрытие информации — это мощный метод программирования, поскольку он снижает сложность.
  • Наследование: функция, которая представляет связь «есть» между различными классами.
  • Интерфейс: языки и коды, которые приложения используют для связи друг с другом и с оборудованием.
  • Обмен сообщениями: Передача сообщений — это форма связи, используемая в параллельном программировании и объектно-ориентированном программировании.
  • Объект: автономный объект, который состоит из данных и процедур для управления данными.
  • Полиморфизм: Способность языка программирования обрабатывать объекты по-разному в зависимости от их типа данных или класса.
  • Процедура: раздел программы, выполняющий определенную задачу.

Преимущества объектно-ориентированного программирования

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

OOPL — языки объектно-ориентированного программирования

An o bject- o riented p rogramming l anguage ( OOPL ) — это язык программирования высокого уровня, основанный на объектно-ориентированной модели. Для выполнения объектно-ориентированного программирования необходим объектно-ориентированный язык программирования.Многие современные языки программирования являются объектно-ориентированными, однако некоторые старые языки программирования, такие как Паскаль, предлагают объектно-ориентированные версии. Примеры объектно-ориентированных языков программирования включают Java, C ++ и Smalltalk.

Первое ООПЛ

Simula, разработанный в 1960-х годах в Норвежском вычислительном центре в Осло, считается первым объектно-ориентированным языком программирования. Несмотря на то, что он является первым, Smaslltalk считается единственной настоящей объектно-ориентированной средой программирования, с которой следует сравнивать все остальные.Впервые он был разработан для использования в образовательных целях в исследовательском центре Xerox Corporation в Пало-Альто в конце 1960-х годов и выпущен в 1972 году.

Рекомендуемая литература: Учебные пособия Webopedia — Основы Java: переменные, синтаксис и соглашения и основы Java, часть 2: Операторы, модификаторы и структуры.

Каковы четыре основы объектно-ориентированного программирования?

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

Связанный: 15 хорошо оплачиваемых вакансий в области информатики

Что такое объектно-ориентированное программирование?

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

Этот стиль программирования широко используется в широко используемых языках программирования. например Java, C ++ и PHP. Эти языки помогают упростить структуру и организацию программ. Программисты часто используют ООП, когда им нужно создавать сложные программы.

Связано: Написание технического резюме: советы и примеры

четыре основы объектно-ориентированного программирования?

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

  • Инкапсуляция
  • Абстракция
  • Наследование
  • Полиморфизм

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

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

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

Абстракция

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

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

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

Вместо переопределения свойств и методов для каждого типа элемента HTML вы можете определить их один раз в универсальном объекте. Называя этот объект чем-то вроде «HTMLElement», другие объекты унаследуют его свойства и методы, так что вы сможете сократить ненужный код.

Главный объект — это суперкласс, а все следующие за ним объекты — подклассы. Подклассы могут иметь отдельные элементы при добавлении того, что им нужно от суперкласса.

Полиморфизм

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

Связано: Компьютерные навыки: определения и примеры

Примеры объектно-ориентированного программирования

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

  • Пример инкапсуляции
  • Пример абстракции
  • Пример наследования
  • Пример полиморфизма

Пример инкапсуляции

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

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

Пример абстракции

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

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

Пример наследования

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

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

Пример полиморфизма

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

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

10 приложений объектно-ориентированного программирования

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

Основные концепции объектно-ориентированного программирования

Абстракция

Это процесс выделения (абстрагирования) схожих характеристик процедур и объектов.

Класс

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

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

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

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

Он определяется как способность одного класса унаследовать свои характеристики от другого класса.

Интерфейс

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

Объект

Это самостоятельная сущность. Он состоит из данных, а также процедур.

Полиморфизм

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

Процедура

Это часть программы, выполняющей определенную задачу.

Сообщение передается

Это форма связи, которая используется в параллельном программировании и ООП.

Основы объектно-ориентированного программирования Python

Самостоятельное обучение

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

Изучить курс

Хотите начать свою карьеру опытного программиста? Курсы программирования QuickStart и учебный курс по веб-разработке могут вам помочь! Начните 7-дневную БЕСПЛАТНУЮ ПРОБНУЮ версию с QuickStart.

Что такое предметы?

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

Вот некоторые приложения объектно-ориентированного программирования

  1. Клиент-серверные системы

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

  • Клиент-сервер
  • Объектно-ориентированное программирование
  • Интернет
  1. Объектно-ориентированные базы данных

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

Атрибуты: Атрибуты — это данные, которые определяют характеристики объекта. Эти данные могут быть как целыми, так и действительными числами. Также это может быть ссылка на сложный объект.

Методы: Они определяют поведение и также называются функциями или процедурами.

  1. Объектно-ориентированные базы данных

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

  1. Проектирование систем реального времени

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

  1. Система моделирования и моделирования

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

  1. Гипертекст и гипермедиа

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

С другой стороны,

Hypermedia — это надмножество гипертекста. Документы, содержащие гипермедиа, содержат ссылки не только на другие фрагменты текста и информации, но и на множество других форм мультимедиа, от изображений до звука.

  1. Нейронные сети и параллельное программирование

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

  1. Системы автоматизации делопроизводства

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

  • Электронная почта
  • Обработка текста
  • Интернет-календари
  • Настольные издательские системы
  1. Системы CIM / CAD / CAM

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

  1. Экспертные системы AI

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

Имеет следующие характеристики:

  • Надежный
  • Очень отзывчивый
  • Понятно
  • Высокопроизводительный

Новички выбирают Python в качестве предпочтительного языка программирования, так как он делает кодирование быстрее и проще.Если вы хотите изучить объектно-ориентированное программирование, вам следует изучить наши основы объектно-ориентированного программирования с помощью Python.

Нужен совет по вопросам карьеры или наставничество? Заинтересованы в нашем учебном лагере по веб-разработке? Свяжитесь с нашими экспертами и начните свое тренировочное путешествие прямо сейчас!

Лучшие курсы по объектно-ориентированному программированию

  • Объектно-ориентированное программирование на C #

    Новичок

    Самостоятельное обучение

  • Объектно-ориентированное программирование на Java

    Средний

    Самостоятельное обучение

  • Объектно-ориентированное программирование PHP

    Средний

    Самостоятельное обучение

  • Основы объектно-ориентированного программирования на Python

    Новичок

    Самостоятельное обучение


4 Преимущества объектно-ориентированного программирования

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

ООП стало фундаментальной частью разработки программного обеспечения. Благодаря повсеместному распространению таких языков, как Java и C ++, вы не сможете разрабатывать программное обеспечение для мобильных устройств, если не понимаете объектно-ориентированного подхода. То же самое и с серьезной веб-разработкой, учитывая популярность языков ООП, таких как Python, PHP и Ruby.

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

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

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

Преимущества объектно-ориентированного программирования заключаются в такой инкапсуляции. Вот подробный обзор некоторых основных преимуществ ООП:

1.Модульность для упрощения поиска и устранения неисправностей

Что-то пошло не так, и вы не знаете, где искать. Проблема в файле виджетов или в WhaleFlumper? Придется ли вам тащиться через этот файл «sewage.c»? Надеюсь, вы прокомментировали свой код!

При работе с объектно-ориентированными языками программирования вы точно знаете, где искать. «О, автомобильный объект сломался? Проблема должна быть в классе автомобилей! » Вам не нужно ничего гадать.

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

Мы поможем вам найти вашу следующую работу программиста:

2. Повторное использование кода по наследству

Предположим, что в дополнение к вашему объекту Car одному коллеге нужен объект RaceCar, а другому — объект Limousine.Каждый строит свои объекты отдельно, но обнаруживает между ними общие черты. Фактически, каждый объект — это просто особый вид Автомобиля. Здесь техника наследования экономит время: создайте один универсальный класс (Car), а затем определите подклассы (RaceCar и Limousine), которые должны наследовать черты универсального класса.

Конечно, у Limousine и RaceCar есть свои уникальные атрибуты и функции. Если объекту RaceCar нужен метод для «fireAfterBurners», а объекту Limousine требуется шофер, каждый класс может реализовать отдельные функции только для себя.Однако, поскольку оба класса наследуют ключевые аспекты от класса Car, например методы «drive» или «fillUpGas», ваши наследующие классы могут просто повторно использовать существующий код вместо того, чтобы заново писать эти функции.

Что делать, если вы хотите внести изменения во все объекты «Автомобиль», независимо от типа? Это еще одно преимущество объектно-ориентированного подхода. Просто внесите изменения в свой класс Car, и все объекты car просто унаследуют новый код.

3. Гибкость за счет полиморфизма

Рассматривая этот пример, теперь вам нужно всего несколько драйверов или функций, таких как «driveCar», «driveRaceCar» и «DriveLimousine».«Водители RaceCarDrivers имеют некоторые общие черты с водителями LimousineDrivers, но другие вещи, такие как RaceHelmets и BeverageSponsorships, уникальны.

Именно здесь вступает в игру сладкий полиморфизм объектно-ориентированного программирования. Поскольку одна функция может изменять форму для адаптации к любому классу, в котором она находится, вы можете создать одну функцию в родительском классе Car под названием «drive» — не «driveCar» или «driveRaceCar», а просто «drive». Эта функция будет работать с RaceCarDriver, LimousineDriver и т. Д.Фактически, у вас даже может быть «raceCar.drive (myRaceCarDriver)» или «limo.drive (myChauffeur)».

4. Эффективное решение проблем

Такой язык, как C, имеет удивительное наследие в истории программирования, но писать программы на языке сверху вниз — все равно что играть в Jenga в рукавицах. Чем сложнее он становится, тем больше шансов, что он рухнет. Между тем, написание программы в функциональном стиле на таком языке, как Haskell или ML, может оказаться рутинной задачей.

Объектно-ориентированное программирование часто оказывается наиболее естественным и прагматичным подходом, если вы освоите его.Языки ООП позволяют вам разбить ваше программное обеспечение на небольшие задачи, которые вы затем сможете решать — по одному объекту за раз.

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

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