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

Чтобы понять основы ООП, давайте сначала определим, что такое ООП:

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

-быстрый поиск в гугле

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

Итак, как и зачем мы все это делаем? Вот здесь и вступают в игру четыре столпа. Они здесь:

A.P.I.E. Что легче запомнить. Потому что пирог!

Если вы похожи на меня, то вы увидели этот список и сказали: «Ну… я определенно знаю, что одно из этих значений означает ¯ \ _ (ツ) _ / ¯». И на это я говорю: «Увы ... вы знаете гораздо больше!»

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

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

Скорее всего, вы придумали что-то вроде этого:

Наверное, не это:

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

Возьмем, к примеру, этот блок кода.

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

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

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

Если мы посмотрим на приведенный выше фрагмент кода, основанный на нашем личном классе, мы увидим, что некоторые данные и функции были абстрагированы. Нам больше не нужно вызывать «first_name» и «last_name» в нашем экземпляре person, а мы можем просто спрашивать объект, кто это или сколько ему лет. Он возвращает красиво отформатированные строки, которые интерполируют данные, которые мы сохранили, или данные, которые мы рассчитали. Пользователю не обязательно это видеть, ему нужен только простой интерфейс и результат. Кроме того, мы делаем наши читатели attr и метод age закрытыми, чтобы в дальнейшем реализовать принцип инкапсуляции и защитить доступ к данным, которые не требуются пользователю.

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

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

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

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

А это Tesla Model X:

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

Дети не взрослые. Возможно, мы сможем отразить это в нашем коде. Когда вы спрашиваете ребенка, кто он такой, он может быть немного более неформальным. Они могут переоценить. Если мы переопределим "who_am_i?" в дочернем классе, чтобы предлагать только имя, но дополнительно указывать возраст, мы все еще не изменили интерфейс с кодом с точки зрения пользователя. Мы можем вызвать этот метод для экземпляра класса Person или класса Child, и оба ответят. Они будут делать это с помощью разных реализаций, и результат будет немного отличаться, но для пользователя оба работают. Он полиморфен.

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

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