Это довольно просто, на самом деле:
Вместо того, чтобы иметь конструктор, который делает ваши настройки,
// c-family pseudo-code
public class Thing {
public Thing (a, b, c, d) { this.x = a; this.y = b; /* ... */ }
}
... пусть ваш конструктор вообще мало или вообще ничего не делает, и напишите метод с именем .init
or .initialize
, который будет делать то, что обычно делает ваш конструктор.
public class Thing {
public Thing () {}
public void initialize (a, b, c, d) {
this.x = a; /*...*/
}
}
Так что теперь вместо того, чтобы просто так:
Thing thing = new Thing(1, 2, 3, 4);
Вы можете пойти:
Thing thing = new Thing();
thing.doSomething();
thing.bind_events(evt_1, evt_2);
thing.initialize(1, 2, 3, 4);
Преимущество заключается в том, что теперь вы можете более легко использовать инжекцию зависимостей / инверсию управления в своих системах.
Вместо того чтобы говорить
public class Soldier {
private Weapon weapon;
public Soldier (name, x, y) {
this.weapon = new Weapon();
}
}
Вы можете построить солдата, дать ему метод экипировки, где вы под рукой ему оружие, а затем вызвать все остальные функции конструктора.
Так что теперь вместо того, чтобы создавать подклассы у врагов, у одного из которых есть пистолет, у другого - винтовка, а у другого - дробовик, и это единственное отличие, вы можете просто сказать:
Soldier soldier1 = new Soldier(),
soldier2 = new Soldier(),
soldier3 = new Soldier();
soldier1.equip(new Pistol());
soldier2.equip(new Rifle());
soldier3.equip(new Shotgun());
soldier1.initialize("Bob", 32, 48);
soldier2.initialize("Doug", 57, 200);
soldier3.initialize("Mike", 92, 30);
То же самое касается разрушения. Если у вас есть особые потребности (удаление прослушивателей событий, удаление экземпляров из массивов / любых структур, с которыми вы работаете, и т. Д.), Вы должны вручную вызывать их, чтобы точно знать, когда и где в программе происходило.
РЕДАКТИРОВАТЬ
Как указал Криотан ниже, это отвечает на первоначальный пост «Как» , но на самом деле не справляется с «Почему».
Как вы, вероятно, можете видеть в ответе выше, разница между:
var myObj = new Object();
myObj.setPrecondition(1);
myObj.setOtherPrecondition(2);
myObj.init();
и писать
var myObj = new Object(1,2);
при этом просто имея большую функцию конструктора.
Существует аргумент для объектов, которые имеют 15 или 20 предварительных условий, что сделало бы конструктор очень, очень трудным для работы, и это сделало бы вещи проще для просмотра и запоминания, вытаскивая эти вещи в интерфейс , так что вы можете увидеть, как работает экземпляр, на один уровень выше.
Необязательная конфигурация объектов является естественным продолжением этого; необязательно установка значений в интерфейсе, прежде чем запускать объект.
У JS есть несколько отличных ярлыков для этой идеи, которые кажутся неуместными в более строгих типах c-подобных языков.
Тем не менее, есть вероятность, что, если вы имеете дело со списком аргументов, которые длинны в вашем конструкторе, ваш объект слишком велик и делает слишком много, как есть. Опять же, это вещь личного предпочтения, и есть исключения повсюду, но если вы передаете 20 вещей в объект, велика вероятность, что вы сможете найти способ заставить этот объект делать меньше, делая меньшие объекты ,
Более уместная и широко применимая причина заключается в том, что инициализация объекта основана на асинхронных данных, которых у вас нет в настоящее время.
Вы знаете, что вам нужен объект, поэтому вы все равно создадите его, но для правильной работы ему нужны данные с сервера или из другого файла, который ему сейчас нужно загрузить.
Опять же, независимо от того, передаете ли вы необходимые данные в гигантскую инициализацию или строите интерфейс, это не очень важно для концепции, поскольку это важно для интерфейса вашего объекта и дизайна вашей системы ...
Но с точки зрения построения объекта вы можете сделать что-то вроде этого:
var obj_w_async_dependencies = new Object();
async_loader.load(obj_w_async_dependencies.async_data, obj_w_async_dependencies);
async_loader
может быть передано имя файла или имя ресурса или что-то еще, загрузить этот ресурс - может быть, он загружает звуковые файлы или данные изображения, или, возможно, он загружает сохраненную статистику символов ...
... а затем он будет передавать эти данные обратно obj_w_async_dependencies.init(result);
.
Такая динамика часто встречается в веб-приложениях.
Не обязательно в конструкции объекта для приложений более высокого уровня: например, галереи могут загружаться и инициализироваться сразу, а затем отображать фотографии по мере их потоковой передачи - это не совсем асинхронная инициализация, но там, где это наблюдается чаще, в библиотеках JavaScript.
Один модуль может зависеть от другого, и поэтому инициализация этого модуля может быть отложена до завершения загрузки зависимых элементов.
С точки зрения конкретных случаев игры, рассмотрим фактический Game
класс.
Почему мы не можем позвонить .start
или .run
в конструктор?
Ресурсы должны быть загружены - все остальное в значительной степени определено и это хорошо, но если мы попробуем запустить игру без подключения к базе данных, без текстур, моделей, звуков или уровней, это не произойдет. особенно интересная игра ...
... так в чем же разница между тем, что мы видим типичным Game
, за исключением того, что мы даем его методу "идти вперед" имя, которое более интересно, чем .init
(или, наоборот, нарушить инициализацию еще дальше, разделить загрузку, настройка вещей, которые были загружены, и запуск программы, когда все было установлено).