Насправді це досить просто:
Замість того, щоб мати конструктор, який виконує налаштування,
// c-family pseudo-code
public class Thing {
public Thing (a, b, c, d) { this.x = a; this.y = b; /* ... */ }
}
... дозвольте своєму конструктору зробити мало або взагалі нічого, і напишіть метод, який називається .initабо .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);
Те ж саме стосується знищення. Якщо у вас є особливі потреби (видалення слухачів подій, видалення екземплярів з масивів / будь-яких структур, з якими ви працюєте тощо), ви б вручну викликали їх, щоб ви точно знали, коли і де в програмі, що відбувається.
EDIT
Як підкреслив Кріотан, нижче це відповідає на оригінальний пост "Як" , але насправді не робить гарної роботи "Чому".
Як ви, напевно, бачите у відповіді вище, різниці між:
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(або, навпаки, ще більше розбиваємо ініціалізацію, відокремлюючи завантаження, налаштування завантажених речей та запуск програми, коли все налаштовано).