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

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

Так зачем писать код в ООП-дизайне?

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

Люди часто спрашивают меня, как я делаю свои рисунки, и, честно говоря, я использую более продвинутую версию Paint под названием Microsoft Whiteboard, и это обычно процесс проектирования.

Я начну с создания моего цветка, компонент за компонентом:

Зачем собирать цветок именно так, покомпонентно?

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

Что еще?

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

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

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

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

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

Например, наш класс Flower может выглядеть примерно так:

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

Почему атрибуты важны?

Атрибуты позволяют нам определить, как будет выглядеть наш объект-цветок. Если я скажу тебе: «Иди сделай цветок», ты, вероятно, спросишь меня: «Какой цветок? Сколько лепестков. Какой цвет? Насколько он вам нужен? "

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

Вы также упомянули методы, какие они?

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

Здесь и появляются методы (которые я часто называю «функциями класса»). Методы позволяют вам изменять функциональность или поведение объектов класса, которые вы создаете. Поэтому, если вы хотите изменить эмоции своего цветочного объекта, вы можете вызвать метод happy () или метод sad ().

Как будут работать эти методы happy () и sad ()?

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

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

Подождите, инкапсулируйте?

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

Когда я учился в университете, я работал ассистентом преподавателя, что означало, что я должен был отмечать задания и экзамены. Те из вас, кто когда-либо был достаточно неудачливым, чтобы отмечать груды заданий и экзаменов, знают, как мучительно спрашивать: «Что это за неразборчивый почерк пытается сказать?» и «Следует ли мне поставить этому ученику дополнительную оценку?». Задания и экзамены должны быть помечены крестиком, чтобы попытаться уменьшить подобную предвзятость.

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

Мы можем объявить частные атрибуты «score» и «feedback» и разрешить их обновление только с помощью методов «updateScore ()» и «updateFeedback ()» внутри класса. Не существует методов, которые позволили бы техническим специалистам получить доступ к оценкам и отзывам других ТА, которые могли бы омрачить их суждения и оценку обратной связи.

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

Я не флорист, но даже знаю, что существуют разные виды цветов, такие как «Подсолнух», «Маргаритки», «Орхидеи», «Лилии», «Маки» и многие другие.

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

Наследование позволяет нам определять родительско-дочерние отношения между классами. Это означает, что вы можете расширить класс Flower (который является суперклассом) и создать подтип flower (который является подклассом).

Почему наследование?

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

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

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

Пример. Мы узнали, что можем создать подкласс Daisy для представления более конкретного типа класса Flower. И наш класс Daisy наследует все черты, определенные в нашем классе Flower, но что, если есть некоторые черты, которые мы хотим изменить?

Например, в нашем классе Flower ранее мы говорили, что будет метод setPetals (), который позволит вам определять количество лепестков в объекте Flower. Что, если у стандартного цветка только 5 лепестков, а у цветка ромашки - 20?

В этом случае мы можем переопределить метод setPetals () в нашем классе Flower с помощью метода setPetals () в классе Daisy.

Таким образом, когда мы создаем экземпляр объекта Daisy и вызываем setPetals (), количество лепестков будет равно 20 вместо 5.

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

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

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

Почему перегрузка?

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

Допустим, у вас есть 3 метода в классе калькулятора. Эти методы позволяют нам суммировать некоторые числа.

  1. int сумма (int x, int y)

Первый метод суммы принимает два параметра, x и y, которые имеют целочисленный тип. Возвращаемое значение также является Целочисленным типом.

Итак, если бы мы вызвали что-то вроде: Calculator (). Sum (2, 2). Получаем 4 обратно.

2 . двойная сумма (двойная x, двойная y)

Второй метод суммы снова принимает два параметра, x и y, но на этот раз они оба имеют тип Double. Возвращаемое значение также Двойной тип.

Если бы мы вызвали: Calculator (). Sum (2.0, 2.0). Получаем 4.0 обратно.

3. двойная сумма (int x, double y)

Третий метод суммы снова принимает два параметра, x и y, но на этот раз это один тип Integer и один тип Double. Значение результата возвращается как тип Double.

Если бы мы вызвали: Calculator (). Sum (2, 2.0). Получаем 4.0 обратно.

Перегрузка - отличная демонстрация простоты. Мы упростили методы нашего класса, чтобы использовать одно и то же имя «сумма», которое может принимать разные типы (а часто и разное количество) параметров. Во время компиляции, в зависимости от того, какие типы аргументов передаются методам, компилятор определяет, будет ли он выполнять метод «сумма» 1, 2 или 3.

Зачем переопределять?

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

На этот раз мы можем добавить больше методов «sum», расширив класс Calculator другим классом под названием «Math». Класс Math унаследует все методы, определенные в классе Calculator, и переопределит метод «sum» своей собственной реализацией. Возможно, мы хотим, чтобы наш математический калькулятор мог вычислять гораздо более сложные вещи, такие как математическое уравнение, поэтому, если мы вызовем: Math (). Sum («2 + 2 + 0»), мы получим в результате «4». .

Ты сделал это.

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

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

Еще раз спасибо за чтение. Я надеюсь, что вы сочли полезным :)