Найти тему
ZDG

ООП: Конструкторы

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

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

Когда транслятор языка видит строчку типа

var a = new Account();

Он понимает, что нужно создать новый объект из класса Account. Он обращается к описанию этого класса и создает такой объект, у которого есть все свойства и доступ к методам данного класса. Но что значит "создает"? До сих пор мы это не обсуждали. Объект как будто просто появляется и всё.

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

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

Работает и работает, ну и ладно. Нужно ли нам как-то использовать конструкторы?

Во-первых, как это сделать, если конструктор от нас скрыт?

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

конструктором является метод, у которого имя Account совпадает с именем класса – именно так транслятор понимает, что это конструктор. А вот тут:

конструктором является метод со специальным "магическим" именем __construct().

В общем, когда будете учить какой-то язык, там и узнаете, это не принципиально.

Если у нас теперь есть собственный конструктор, то кто будет заниматься собственно созданием объекта? Должны ли мы теперь это делать сами? Нет, не должны.

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

Вызов "родительского" конструктора происходит через специальные инструкции. Это тоже зависит от конкретного языка. Такой инструкцией может быть super():

или parent:

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

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

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

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

var a = new Account();
a.currency = 'USD';
a.sum = 100;

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

Теперь вы можете создавать счета вот так, просто передавая параметры в конструктор:

var a = new Account('USD', 100);

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