Это довольно просто, на самом деле:
Вместо того, чтобы иметь конструктор, который делает ваши настройки,
// c-family pseudo-code
public class Thing {
public Thing (a, b, c, d) { this.x = a; this.y = b; /* ... */ }
}
... пусть ваш конструктор вообще мало или вообще ничего не делает, и напишите метод с именем .initor .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(или, наоборот, нарушить инициализацию еще дальше, разделить загрузку, настройка вещей, которые были загружены, и запуск программы, когда все было установлено).