зачем нужно ооп в программировании
Зачем нужно понимать ООП
Часто я встречаю разработчиков, которые пишут код на объектно-ориентированном языке программирования, но не понимают принципов ООП. Это могут быть начинающие девелоперы, которые еще на собеседованиях сталкиваются с проблемами объяснения принципов. А также это могут быть, казалось бы, опытные программисты, которые не понимают принципов, заложенных в язык программирования, на котором они пишут. Второй случай хотелось бы встречать реже, но на практике это не так. Часто разработчики смотрят на наследование или полиморфизм, как на особенности языка, как на какой-то технический инструмент и не думают, о вещах, которые лежат в основе этих механизмов.
Все, что будет изложено ниже — сугубо мои размышления, я не претендую на статус истины в последней инстанции и не жду, что все примут мою точку зрения. Я надеюсь, эта статья натолкнет на размышления и даст толчок к развитию собственного понимания у каждого читателя.
Примеры кода буду приводить из iOS разработки.
Я считаю, если ты пишешь код на объектно-ориентированном языке программирования, ты обязан не только знать определения, но и понимать суть, которая вложена в эту парадигму.
На мой взгляд, если функциональное или структурное программирование — принципы которые больше относятся к написанию именно кода, то ООП это уже не про код, а моделирование сложных систем. Наш мозг воспринимает мир как набор объектов, которые взаимодействуют друг с другом. Если функция — просто действие без контекста, то метод класса это уже действие в определенном контексте, это действие, которое относится к определенному объекту. И система приобретает вид взаимодействия различных объектов между собой. Таким образом, ООП позволяет сделать описание системы более понятным для восприятия.
Также заранее хочу добавить, это описание идеального сферического программирования в вакууме и в реальности множество вещей нарушаются в угоду практичности. Но стремление к идеалу только улучшит качество кода. И не стоит забывать, что гонка за крайностью — тоже плохо.
Наследование
Что такое программирование в целом — это написание детальной инструкции, которую должна выполнить машина. К тому же эта инструкция должна быть понятной как машине, так и человеку, который будет вносить изменения в инструкцию в будущем.
Так как ООП — это про моделирование, то код мы пишем начиная с абстракции частей системы и взаимодействия между этими частями, которые мы должны записать в виде кода. Например, социальная сеть, которая состоит из пользователей, взаимодействующих друг с другом. Помимо пользователей, система состоит из более мелких компонентов, таких как сообщения, посты, лайки, комментарии. Даже сам пользователь может являть собой подсистему в системе. Как человек состоит из различных органов и частей тела: сердце, мозг, руки, пальцы, так и пользователь в системе может состоять из более мелких составляющих. Но уже не руки или глаза, а адрес, интересы, записи об образовании, которые можно выносить как отдельные объекты. Уже на этапе анализа можно проследить принципы объектно-ориентированного подхода. У нас есть пользователь — абстракция реального человека. Но у пользователя могут быть разные роли: админ, обычный пользователь, VIP пользователь, анонимный посетитель. Они все являются абстракциями реальных людей и пользователями данной системы. Но каждая из вышеперечисленных ролей имеет свои особенности и при этом все имеют общее — они пользуются системой и должны зарегистрироваться в системе (у каждого может быть свой способ) и они все должны пройти процедуру входа в систему.
Это и есть принцип наследования, где каждый админ/VIP-клиент/аноним являются пользователями, но не каждый пользователь должен быть админом или VIP-пользователем.
Неправильное понимание принципа приводит к ошибкам в коде. Типичная ошибка — если есть общие поля или методы, значит нужно делать базовый класс, хотя классы наследники по логике не имеют ничего общего.
Еще пример ошибочной трактовки принципа наследования, это когда базовый класс и наследник являются представителями разных логических групп. Выглядит это следующим образом, реализуем MVC в iOS проекте, где UIViewController это Controller с абстрактными методами, которые должен реализовать наследник. А наследник — это уже Model. Там где по логике проектирования должно быть взаимодействие между двумя группами классов, один класс становиться одновременно и Model и Controller. Не говорим уже о том, что UIViewController в реалиях iOS разработки еще и берет на себя роль View. В итоге мы получаем один объект, который делает все сам. Если у нас есть пользователь (User), то он будет и Controller и View одновременно.
Представляю эту картину в реальной жизни, у нас есть регистратура, где некий администратор ведет записи о посетителях в тетрадку. Также в этой тетради можно читать данные о посетителях. Мы получаем посетителя — Model, человека в регистратуре — Controller и тетрадь View. И в вышеуказанной интерпретации принципа наследования мы получаем, что посетитель всегда является регистратором и тетрадкой. Уже дико выглядит то, что человек и тетрадка одно целое. И логика наследования нарушается. Если рассматривать, что посетитель и регистратор — люди и принять тот факт, что представитель класса регистратора может стать посетителем, то логичней сказать, что регистратор является наследником посетителя.
Это пример, когда непонимание принципа наследования приводит к сложности понимания системы.
По принципу наследования, базовый класс содержит общие свойства для некоторой группы наследников, которая связана общим логическим смыслом. Если наследники не имеют общего смысла, а просто имеют случайные общие свойства, то скорей всего наследование неправильно реализовано.
Абстракция
Я уже затронул принцип наследования, хотя хотелось бы начать с такого принципа как абстракция. На мой взгляд это базовый принцип ООП (также абстракция относится и к остальным парадигмам) и он незаслуженно перешел в разряд опционального принципа.
Абстракция гласит — останавливаем внимание на важных и необходимых аспектах объекта и игнорируем ненужные для нас.
Как это выглядит: когда мы описываем что-то, мы упоминаем только о тех вещах, которые важны в нашем повествовании. Например, когда парень рассказывает другу о том, как в салоне видел крутую машину, он говорит о важных для них вещах: мощность двигателя, систему тормозов, диаметр колес. Хотя особенностей автомобиля безграничное множество. В своем разговоре ребята не упоминают о молекулярном составе автомобиля, хотя такая характеристика определенно существует у физического тела. Незнание законов физики заставляет упустить такой показатель как сила трения между определенными деталями. Даже дело не в образовании, в данном разговоре все эти детали не важны, они упускаются.
В этом примере используется модель описания автомобиля с набором только необходимых качеств.Так же и в программировании. Когда будет создаваться мобильное приложения для продажи автомобилей, программисту определенно нужно будет описать модель этого автомобиля. Естественно разработчик не будет писать модель у которой 100500 полей и методов.
Наверно пренебрежением этим принципом в реальной разработке, является добавление ненужных методов и свойств классу. Впоследствии, получаем непонимание и нарушение принципа единственной ответственности с SOLID.
Также к абстракции я бы отнес декомпозицию, когда сложный объект разбивается на систему. Мы абстрагируемся от некоторых особенностей и переносим их в отдельный компонент. Пример: пользователь у которого есть место проживания, то есть адрес. Адрес в свою очередь состоит из города, улицы, номера дома и т. д. В этот момент мы думаем, а нужно ли указывать страну или регион? Если это приложения для пользования администрацией конкретного района города, то можно упустить такие детали. В итоге мы получаем пользователя, который абстрагируется от некоторых деталей адреса. Опять-таки, непонимание того, что мы не только пишем код, но и занимаемся моделированием, приводит к тому, что у нас есть, допустим, MenuViewController, который состоит из 5000+ строк кода.
Полиморфизм
Следующим стоило бы написать об инкапсуляции, принцип, наверное, самый спорный и интересный. Так что я его оставлю на закуску. Наследование уже упомянул, так что можно перейти к полиморфизму.
Полиморфизм плавно вытекает из наследования. Гласит он следующее: можно создавать классы наследники, которые будут имитировать интерфейс базового класса, но со своей собственной реализацией. Этот принцип отражается в таком принципе SOLID как принцип Барбары Лисков: мы можем подставлять объекты классов наследников там, где предполагается использование базового класса, при этом замена не должна никак себя проявлять.
Если взять пример с регистратурой, когда у нас есть просто человек (посетитель, не будем абстрагироваться и делать посетителя наследником человека) и есть регистратор. Должна быть возможность регистратору из другого отдела пройти через текущую регистратуру как обычному посетителю. В жизни это вполне реальный пример.
Пример в коде: множество наследников UIViewController, которые пушатся, презентятся и добавляются в UITabBarController как обычные UIViewController.
Как по мне, самый простой принцип. Но он довольно часто нарушается, когда класс наследник превращается в что-то новое и его уже нельзя использовать там, где использовался базовый класс. Также к нарушению относятся многочисленные опциональные методы и поля, которые в ходе неправильного наследования не нужны классу-наследнику. В этот момент, когда предполагается выполнение определенного метода базового класса, ничего не происходит. В лучшем случае ничего не произойдет, но может получиться так, что приложение либо неадекватно начинает себя вести либо крашится вовсе.
Полиморфизм — когда наследники делают все по своему, но результат работы такой же как у базового класса. Если наследник занимается чем-то своим и не дает результат такой, который ожидается от базового класса — значит с наследованием и полиморфизмом что-то не так.
Пример: есть базовый класс автомобиль, который предположительно должен заехать в гараж. Создаем летающий автомобиль-наследник (как в фильме «Назад в будущее 2»). Какой будет результат при попытке загнать этого монстра в гараж? Да, он может залететь в гараж, если функция езды будет полностью заменена на функцию полета. А если функция полета была новым функционалом, а функция езды вообще заблокирована? То мы не сможем спрятать нашего летуна от непогоды. Это уже не будет автомобиль, это будет что-то новое. Результат — из-за неправильного моделирования и наследования был нарушен принцип полиморфизма и получился нежелательный результат.
Добавлю, что полиморфизм тесно связан с наследованием и проблемы в наследовании отзываются еще большими проблемами в полиморфизме.
Инкапсуляция
И вот пришла очередь инкапсуляции. Ходит много дискуссий, что же собой представляет инкапсуляция. И существует как минимум два определения:
Инкапсуляция — это сокрытие методов и полей класса, которые не нужны при использовании объектов этого класса.
Инкапсуляция — это обьединение данных и методов, которые обрабатывают эти данные.
Первый вариант иногда воспринимается как инкапсуляция = сокрытие, что, как я считаю, не совсем верное понимание.
К примеру, я разрабатываю кнопку, которая в момент нажатия становится немного светлее. Проще говоря, когда кнопка в состоянии “нажата”, цвет фона становится светлее.
Для этого я создаю класс наследник UIButton и в наследнике добавляю метод, который устанавливает цвет кнопки в цвет с альфа каналом 50% от оригинального. А также я добавляю метод, который возвращает бэкграунд цвет кнопки в оригинальный, без альфа канала.
Если эти два метода будут доступны при использовании моей кастомной кнопки, то может нарушиться поведение этой кнопки. К примеру в момент нажатия можно вызвать метод, который устанавливает оригинальный цвет. Или же вдруг сделать кнопку светлее, как будто она нажата, хотя это не так.
Инкапсуляция — не бездумное сокрытие каких-то полей или методов, а проектирование класса с определенным набором возможностей, которые не должны нарушаться.
По поводу объединения данных и методов, которые обрабатывают эти данные, я с этим согласен, но с оговоркой. В моем понимании ООП — моделирование объектов в коде, что само собой подразумевает, наличие состояния и действий у этого объекта.
Если просуммировать, то инкапсуляция — проектирование самостоятельной единицы (объекта), которая выполняет некую роль в системе, имеет набор параметров и методов. При этом все, что может нарушить роль этого объекта, скрыто.
Объектно-ориентированное программирование простым языком — объясняют эксперты
Авторизуйтесь
Объектно-ориентированное программирование простым языком — объясняют эксперты
В интернете можно найти много описаний ООП, однако начинающий программист рискует их не понять. Мы попросили экспертов объяснить суть этой методологии простыми словами.
руководитель группы Java-разработчиков ИТ-компании КРОК
Самый простой способ объяснить и понять ООП — воспользоваться метафорой. Метафорой объекта в ООП является объект реального мира, например, человек. Объекты надо отличать между собой и у них есть что-то, что их определяет. Например, для человека это может быть имя, когда мы говорим про нашего знакомого Васю, и все понимают о ком речь. Люди неким образом похожи друг на друга. Подмножество людей, обладающих одинаковым набором свойств (имя, фамилия, возраст и т.д.) и общим поведением, будет называться класс. Возьмем для примера сотрудников нашей компании. Для каждого из нас определен департамент (я, например, в департаменте разработки ПО числюсь, ДРПО), должность, уровень зарплаты и т.д. Эти свойства обычно определяют в момент, когда в компанию приходит новый сотрудник. У человека можно запросить информацию по его навыкам или попросить помочь коллеге — это общее поведение для всех сотрудников.
Зарплату сотрудника знает он сам, его руководитель и бухгалтер, остальные — нет. Такое сокрытие данных называется инкапсуляция. Какие свойства и поведение будет доступно другим объектам обычно определяется на уровне класса. Руководитель отдела также является сотрудником, но он обладает рядом дополнительных свойств, например, у него есть подчиненные. Таким образом класс «руководитель», расширяет класс «сотрудник» или, другими словами, происходит наследование. При этом между классами устанавливается отношение «является» — то есть любой руководитель является сотрудником, но не наоборот — не каждый сотрудник является руководителем. Если у класса больше одного наследника, то образуется иерархия. Классы, которые являются родственниками в иерархии не связаны отношением «является», например, бухгалтер является сотрудником, но бухгалтер не является руководителем.
При помощи этих правил иерархию можно проверить на корректность. Если взять ведомость со списком всех сотрудников, в нее очевидным образом попадут и руководители, и бухгалтеры, но в общем списке они не будут отличаться от других сотрудников. Если мы захотим уточнить список подчиненных у каждого руководителя, то нам понадобится подготовить отдельную ведомость со свойствами, специфичными для класса «руководитель». Такое свойство объектов называется полиморфизмом, где состав свойств и поведение будет определяться классом, через который мы смотрим на объект: мы можем обращаться к объекту, как и к любому из предков его класса, но это не верно для потомков или других родственников.
Так мы рассмотрели, как связаны объекты и классы, и такие понятия, как: инкапсуляция, наследование и полиморфизм. Все это — базовые понятия ООП.
ведущий инженер-программист IT-компании DD Planet
Объектно-ориентированное программирование – это подход, при котором вся программа рассматривается как набор взаимодействующих друг с другом объектов. При этом нам важно знать их характеристики.
У каждого объекта в системе есть свойства и поведение, как и у любого реального объекта. Например, рассмотрим объект «машина». У него есть свойства (цвет, вес, стоимость) и поведение (машина может ехать, сигналить, потреблять топливо).
Такой подход помогает строить сложные системы более просто и естественно благодаря тому, что вся предметная область разбивается на объекты и каждый из них слабо связан с другими объектами. Слабая связанность возникает вследствие соблюдения трех принципов: инкапсуляции, наследования и полиморфизма.
ООП позволяет упростить сложные объекты, составляя их из более маленьких и простых, поэтому над программой могут работать сотни разработчиков, каждый из которых занят своим блоком. Большинство современных языков программирования — объектно-ориентированные, и, однажды поняв суть, вы сможете освоить сразу несколько языков.
старший разработчик систем автоматизации и поддержки сервисов мониторинга и реагирования на киберугрозы в BI.ZONE
Методология объектно-ориентированного программирования (ООП) подразумевает представление всей программы или ее частей объектами. У каждого объекта есть тип — в ООП он называется классом. Классы можно объявлять или наследовать и создавать из них экземпляры. Собственно, объект — это и есть экземпляр класса.
Обычно объект объединяет в себе данные и методы для работы с ними. Представим, что у нас есть тип «Позвоночное существо», у которого есть свойство «Класс». У каждого из Позвоночных существ это свойство равно одному из пяти значений: Рыба, Земноводное, Птица, Пресмыкающееся, Млекопитающее. Добавим метод получить_класс — он будет возвращать это значение. Далее объявим тип «Человек», который наследует типу «Позвоночное существо». Создадим несколько экземпляров: Иван Иванов, Марина Иванова, Антон Антонов. Добавим присущие только Человеку свойства: Имя и Фамилию. У каждого из них будет метод получить_имя/получить_фамилию, а также перешедший от Позвоночного существа метод получить_класс.
Так можно продолжать сколь угодно долго: повторно описывать методы родительских классов нам не нужно, и любой экземпляр класса будет обладать заявленными свойствами.
Основные задачи ООП — структурировать код, повысить его читабельность и ускорить понимание логики программы. Косвенно выполняются и другие задачи: например, повышается безопасность кода и сокращается его дублирование.
Дело в том, что человеку гораздо удобнее работать с реальными объектами, чем отдельно с набором данных и функциями. Представляя данные в программе как свойства объекта, а функции по обработке данных — как возможные методы объекта, мы приближаем процесс программирования к процессу описания метода решения задачи. Это достигается за счет добавления знакомой человеку структуры абстракций: ведь даже язык, на котором мы говорим, следует принципам ООП. У каждой буквы есть произношение и написание, каждое слово включает буквы и имеет свое произношение и написание, то же верно и для предложений, и для более крупных конструкций. Все в этом мире — объект!
Главное, о чем не стоит забывать: ООП — это не единственная парадигма. У нее есть свои плюсы и минусы, для каких-то задач она подходит, для каких-то — нет. Например, ООП не даст особых преимуществ, если вы пишете «однострочники» и простые скрипты. Однако в больших проектах неразделенный на отдельные сущности код быстро превратится в «лапшу» и перестанет читаться, и ООП здесь сильно упростит работу.
разработчик хостинг-провайдера и регистратора доменов REG.RU
Наиболее классическое определение, к которому прибегают при необходимости объяснить что такое ООП, это — «способ моделирования реального мира». Можно предположить, что ООП делает код более простым и наглядным, однако такая формулировка слишком размыта и уклончива, она не открывает самой сути ООП.
ООП стоит на трёх китах:
Если резюмировать: ООП даёт контроль над зависимостями в коде. Это способ сделать так, чтобы высокоуровневый код не зависел от низкоуровневой реализации. ООП позволяет вести разработку раздельно, поскольку взаимодействие между сущностями определено интерфейсами.
технический директор в Creonit
Суть ООП заключается в том, чтобы представить программу в виде объектов, которые каким-то образом взаимодействуют друг с другом.
Все, что угодно, можно представить в виде объекта: человека, воздушный шарик, сообщение в мессенджере. У объекта могут быть свойства, например, цвет – красный, размер – большой. Также у объекта могут быть методы для совершения операций. Например, если объект телевизор, вызываем метод «включить», и телевизор включается.
Объект — это экземпляр какого-то класса. Класс — это шаблон, в котором описаны все свойства будущего объекта и его методы. При этом если класс воздушного шарика определяет свойство цвет, то сам класс никакого значения цвета не имеет. Но экземпляры этого класса, которых, к слову, можно создавать сколько угодно, уже будут раскрашены в любые цвета.
Классы могут выстраиваться в хитрые витиеватые структуры. Чем структура хитрее, тем программа гибче, легче поддается изменениям и внедрениям нового функционала, но не обязательно. Такие слова как наследование, полиморфизм, инкапсуляция позволяют создавать структуры объектов еще витиеватее, при этом избавляют код от дублирования и делают его интуитивно понятным, но не всегда.
Понимание только лишь принципа работы объектов не сделает человека ООП-гуру. Суть мастерства ООП в умении конструировать многоуровневые структуры из классов, при этом оставляя код читаемым, надежным и гибким. Чтобы это постичь, потребуется пройти долгий и изнурительный путь, но в конечном итоге ООП станет лучше.
руководитель практики Java центра разработки компании «Рексофт» в Воронеже
Часто статьи про ООП начинаются с кучи терминов, теории и сложных объяснений подходов и парадигм. В своем курсе программирования на Java для начинающих в Воронежском государственном университете я сначала объясняю на практике роль объектов, их связь и операции с ними, используя обычные слова, которые мы используем в повседневной жизни. Например, инкапсуляцию удобно объяснять с помощь магазина, где есть витрина, на которой все видно и красиво расставлено и есть склад, куда обычного покупателя не пускают.
Когда студенты начинают понимать и могут строить объектные модели, можно вводить первые термины, такие как: инкапсуляция, наследование и полиморфизм. Понимая работу ООП на практике, даже на совсем примитивном уровне, эти слова уже не кажутся такими страшными и непонятными. Дальше я ввожу больше теории и обязательно добавляю практические вещи, например, паттерны проектирования.
Проще говоря, преподавать ООП стоит от практики к теории. Очень много в этом процессе дают правильные примеры. В первое время они должны отражать окружающий нас мир и только потом трансформироваться в абстракции и переходить к языку программирования.
Главное — в деталях. Что на самом деле даёт ООП?
Я уже более двух лет денно и нощно копаюсь в ООП. Прочитал толстую стопку книг, потратил человеко-месяцы на рефакторинг кода с процедурного стиля в объектно-ориентированный и обратно. Друг говорит, что я заработал ООП головного мозга. Но есть ли у меня уверенность, что я умею решать сложные задачи и писать понятный код?
Я завидую людям, которые умеют уверенно пропихивать свое бредовое мнение. Особенно, когда это касается разработки, архитектуры. В общем того, к чему я страстно стремлюсь, но в чём испытываю бесконечные сомнения. Потому что я не гений, и не ФП-шник, у меня нет истории успеха. Но позвольте вставить 5 копеек.
Инкапсуляция, полиморфизм, объектное мышление…?
Любите, когда вас грузят терминами? Я прочитал достаточно, но слова выше до сих пор не говорят мне ни о чем конкретном. Я привык объяснять вещи на понятном мне языке. Уровне абстракции, если хотите. И давно хотел знать ответ на простой вопрос: «Что даёт ООП?». Желательно с примерами кода. И сегодня я попробую ответить на него сам. Но сперва небольшая абстракция.
Сложность задачи
Разработчик так или иначе занимается решением задач. Каждая задача состоит из множества деталей. Начиная от специфики АПИ взаимодействия с компьютером, заканчивая деталями бизнес-логики.
На днях я собирал с дочкой мозаику. Раньше мы собирали пазлы большого размера, буквально из 9 частей. А теперь она справляется и с мелкой мозаикой для детей от 3-х лет. Это интересно! Как мозг находит среди разбросанных пазлов каждому своё место. И чем определяется сложность?
Судя по мозаикам для детей, сложность в первую очередь определяется количеством деталей. Я не уверен, что аналогия с собиранием пазла покроет весь процесс разработки. Но с чем ещё сравнить рождение алгоритма в момент написания тела функции? И мне видится, что уменьшение количества деталей — одно из самых существенных упрощений.
Чтобы наглядней показать главную фишку ООП, поговорим о задачах, количество деталей которых не позволяет собрать пазл за приемлемое время. В таких случаях нам требуется декомпозиция.
Декомпозиция
Как известно со школы, сложную задачу можно разбить на задачи попроще, чтобы решать их по отдельности. Суть подхода заключается в ограничении количества деталей.
Так уж получается, что еще при обучении программированию мы привыкаем к работе с процедурным подходом. Когда на входе есть кусок данных, который мы преобразовываем, закидываем в подфункции, и мапим в result. И в конечном счёте, мы проводим декомпозицию во время рефакторинга, когда решение уже есть.
В чём проблема с процедурным подходом при декомпозиции? Нам по привычке нужны исходные данные, и желательно с окончательно сформированной структурой. Причем, чем больше задача, тем сложнее структура этих исходных данных, тем больше деталей нужно держать в голове. Но как быть уверенным, что исходных данных хватит для решения подзадач, и при этом избавиться от суммы всех деталей на верхнем уровне?
Рассмотрим пример. Не так давно я писал скрипт, который делает сборки проектов и закидывает в нужные папки.
Может показаться, что я применил ООП в этом решении. Можно заменять реализации сервисов, можно даже что-то тестировать. Но на самом деле это яркий пример процедурного подхода.
Взгляните на интерфейс BuildConfig. Это структура, которую я создал ещё в самом начале написания кода. Я заранее понимал, что не смогу предусмотреть все параметры наперед, и просто добавлял в эту структуру поля по мере необходимости. Уже к середине работы конфиг оброс кучей полей, которые использовались в разных частях системы. Меня раздражало наличие «объекта», который нужно допиливать при каждом изменении. В нем сложно ориентироваться, и легко что-то сломать, перепутав названия полей. И при этом, все части системы сборки зависят от BuildConfig. Так как эта задача не столь объемная и критичная, катастрофы не произошло. Но ясно, что будь система посложней, я бы запорол проект.
Объект
Основная проблема процедурного подхода — данные, их структура и количество. Сложная структура данных привносит детали, усложняющие понимание задачи. А теперь, следите за руками, здесь нет никакого обмана.
Давайте вспомним, зачем нам данные? Чтобы произвести над ними операции и получить результат. Часто мы знаем какие подзадачи нужно решить, но не понимаем, что за данные для этого потребуются.
Внимание! Мы можем манипулировать операциями, зная, что они заранее владеют данными для своего выполнения.
Объект позволяет заменить набор данных набором операций. И если это уменьшает количество деталей, то и упрощает часть задачи!
Теперь Builder получает только необходимые для него данные, как и другие части системы. При этом классы, принимающие Builder через конструктор, не зависят от параметров, которые нужны для его инициализации. Когда детали на своих местах, понимать программу проще. Но есть и слабое место.
Все детали, связанные со сложной структурой изначального конфига, легли на процесс создания объекта Project и его зависимостей. За всё нужно платить. Но порой это выгодное предложение — избавиться от второстепенных деталей в целом модуле, и сосредоточить их внутри одной фабрики.
Таким образом, ООП даёт возможность скрыть детали, переложив их на момент создания объекта. С точки зрения проектирования, это суперспособность — возможность избавиться от лишних деталей. Это имеет смысл, если сумма деталей в интерфейсе объекта меньше, чем в структуре, которую он инкапсулирует. И если вы можете разделить создание объекта и его использование в большей части системы.
SOLID, абстракция, инкапсуляция.
Есть куча книг, посвященных ООП. В них проводятся глубокие исследования, отражающие опыт написания объектно-ориентированных программ. Но мой взгляд на разработку перевернуло именно осознание того, что ООП упрощает код в первую очередь за счет ограничения деталей. И буду полярным. но если вы не избавляетесь от деталей с помощью объектов, вы не используете ООП.
Можно пытаться соблюсти SOLID, но в этом нет большого смысла, если вы не скрыли второстепенные детали. Можно привести интерфейсы к виду, отражающему объекты реального мира, но в этом нет большого смысла, если вы не скрыли второстепенные детали. Можно улучшить семантику через использование существительных в коде, но… Вы поняли.
Я нахожу SOLID, паттерны и прочие рекомендации по написанию объектов отличным руководством к рефакторингу. После сборки пазла вы видите картину в целом, и можете выделить более простые части. В общем, это важные инструменты и метрики, требующие внимания, но часто разработчики переходят к их изучению и использованию до преобразования программы к объектному виду.
Когда ты знаешь правду
ООП — инструмент решения сложных задач. Сложные задачи побеждаются разделением на простые за счет ограничения деталей. Способ уменьшить количество деталей — заменить данные набором операций.
Теперь, когда ты знаешь правду, попробуй избавиться от лишнего в своем проекте. Приведи получившиеся объекты к соответствию SOLID. Потом попробуй привести их к объектам реального мира. Не наоборот. Главное — в деталях.
Недавно написал расширение VSCode для рефакторинга Extract class. Считаю, это неплохой пример обектно-ориентированного кода. Лучший, что у меня есть. Буду рад замечаниям по реализации, или предложениям по улучшению кода/функциональности. Хочу в ближайшее время оформить ПР в Abracadabra