Найти в Дзене

Зачем нужны классы?

Оглавление

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

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

Классы не нужны?

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

Что же такое класс?

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

Как на практике применяются классы?

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

Рис. 1 Код без классов
Рис. 1 Код без классов

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

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

И последнее, кроме обилия аргументов, есть проблема с возвращаемым значением get_user. Чтобы показать связь между переменными (полями) пользователя, я объеденил их в кортеж, чтобы вернуть их все разом. Но, при модификации пользователя, я должен буду во всех местах в программе менять эти списки переменных. Здесь вступает в силу человеческий фактор. Чем больше и чаще я буду делать такие изменения, тем выше шанс, что когда-нибудь я ошибусь. Шанс ошибки также пропорционально увеличивается, если над кодом работает несколько человек. Как же это исправить?

ООП новое решение?

Выше я описал часто возникающую проблему, она не нова. До появления ООП их вполне успешно решали с помощью объединения данных в структуры. Они есть в С, С++, С# и во многих других языках. Мы можем объединить данные пользователя в структуру:

Рис. 2 Пример использования структур данных
Рис. 2 Пример использования структур данных

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

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

И так, чем же отличается класс от структуры? В языках с “неполноценным” ООП, таких как C или Golang, поведение классов имитируют с помощью структур и наборов связанных с ними методов. Но там, где классы есть, можно создать практически ту же самую структуру данных и внутри, помимо полей, определить еще методы для работы с ней. В моем примере, должно быть два класса: User и UserRepository. Один представляет из себя тип данных User, а второй нужен для работы с базой данных. Не стоит их смешивать в один, хотя и есть такая возможность.

Рис. 3 Объедение кода и данных внутри класса
Рис. 3 Объедение кода и данных внутри класса

Код почти не изменился, даже можно было оставить объявление User через struct и ничего не изменилось бы. Но в строках 15 и 23 я убрал первый аргумент — connection, теперь он берется из общего контекста класса UserRepository (9-ая строка). Обратите внимание на новый метод, который я добавил к уже существующим createUser и getUser, который объявлен на 11 строке и его название полностью совпадает с именем класса. Зачем он нужен? Этот метод является конструктором класса.

Конструктор класса создает класс

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

Ссылка на оригинал статьи:
https://abritov.medium.com/зачем-нужны-классы-347a7798dcc