Чи можемо ми жити без конструкторів?


25

Скажімо, чомусь усі об'єкти створені таким чином $ obj = CLASS :: getInstance (). Потім ми вводимо залежності за допомогою сеттерів і виконуємо запуску ініціалізації за допомогою $ obj-> initInstance (); Чи є справжні неприємності чи ситуації, які неможливо вирішити, якщо ми взагалі не будемо використовувати конструктори?

Причина створення об'єкта таким чином полягає в тому, що ми можемо замінити клас всередині getInstance () за деякими правилами.

Я працюю в PHP, якщо це важливо


яка мова програмування?
гнат

2
PHP (чому це важливо?)
Аксель Фолі

8
Виглядає так, що те, що ви хочете зробити, можна досягти, використовуючи заводський зразок.
superM

1
Як зауваження: згаданий заводський патерн @superM - це один із способів впровадження інверсії управління (введення залежності) - інший.
Йоахім Зауер

Хіба це не те, що JavaScript робить з об'єктами?
Тхісер

Відповіді:


44

Я б сказав, що це сильно заважає вашому дизайнерському простору.

Конструктори - це чудове місце для ініціалізації та перевірки передаваних параметрів. Якщо ви більше не можете їх використовувати для цього, тоді ініціалізація, обробка станом (або очевидне заперечення конструктора "зламаних" об'єктів) стає набагато важче і частково неможливе.

Наприклад, якщо кожному Fooоб'єкту потрібен Frobnicatorто, він може перевірити його конструктор, якщо значення Frobnicatorне є нульовим. Якщо ви забираєте конструктор, то перевірити це стає складніше. Чи перевіряєте ви в кожній точці, де він би використовувався? У init()методі (ефективно екстерналізуючи метод конструктора)? Ніколи не перевіряйте це і сподіваєтесь на найкраще?

Хоча ви, мабуть, все-таки можете все реалізувати (адже ви все ще готові до завершення), деякі речі зробити це буде набагато складніше.

Особисто я б запропонував вивчити введення залежності / інверсію управління . Ці методи також дозволяють перемикати конкретні класи реалізації, але вони не заважають писати / використовувати конструктори.


Що ви мали на увазі під "або зрозумілим запереченням конструктора" зламаних "об'єктів"?
Geek

2
@Geek: конструктор може перевірити його аргумент і вирішити, чи призведе це до роботи робочого об'єкта (наприклад, якщо ваш об’єкт потребує, HttpClientто він перевіряє, чи не є цей параметр недійсним). І якщо ці обмеження не будуть дотримані, то це може кинути виняток. Це реально неможливо із підходом конструювання-встановлення значень.
Йоахім Зауер

1
Я думаю, що OP просто описував екстерналізацію конструктора всередині init(), що цілком можливо, хоча це просто накладає більше тягаря на обслуговування.
Петро

26

2 переваги для конструкторів:

Конструктори дозволяють виконувати етапи будівництва об'єкта атомно.

Я міг би уникати конструктора і використовувати сетери для всього, але як бути з обов'язковими властивостями, як запропонував Йоахім Зауер? З конструктором об’єкт має власну логіку побудови, щоб упевнитись у тому, що немає недійсних примірників такого класу .

Якщо для створення екземпляра Fooпотрібно встановити 3 властивості, конструктор може взяти посилання на всі 3, перевірити їх та викинути виняток, якщо вони недійсні.

Інкапсуляція

Покладаючись виключно на сеттерів, на споживача об’єкта лягає тягар, щоб його правильно побудувати. Можуть бути різні комбінації властивостей, які є дійсними.

Наприклад, кожному Fooекземпляру потрібен або екземпляр Barяк властивість, barабо екземпляр BarFinderвласності barFinder. Він може використовувати будь-який. Ви можете створити конструктор для кожного дійсного набору параметрів і таким чином застосовувати умови.

Логіка та семантика об'єктів живуть усередині самого об'єкта. Це добре капсулювання.


15

Так, ви можете жити без конструкторів.

Звичайно, у вас може виникнути безліч дублюваних кодів пластини котла. І якщо ваша програма будь-якого масштабу, ви, ймовірно, витратите багато часу, намагаючись знайти джерело проблеми, коли цей код котла не використовується послідовно в програмі.

Але ні, вам не потрібно «суто» потребувати власних конструкторів. Звичайно, вам теж категорично не потрібні класи та об’єкти.

Тепер, якщо ваша мета полягає в тому, щоб використовувати якийсь заводський зразок для створення об'єктів, що не є взаємовиключним для використання конструкторів при ініціалізації ваших об'єктів.


Зовсім. Для початку можна навіть жити без класів та предметів.
JensG

5
Всі вітайте могутнього асемблера!
Давор Ждрало

9

Перевага використання конструкторів полягає в тому, що вони полегшують переконання, що у вас ніколи не буде недійсного об’єкта.

Конструктор надає вам можливість встановити всі змінні учасника об'єкта на дійсний стан. Коли ви переконаєтесь, що жоден мутаторний метод не може змінити об'єкт до недійсного стану, у вас ніколи не буде недійсного об'єкта, що позбавить вас від безлічі помилок.

Але коли новий об’єкт створений у недійсному стані, і ви повинні зателефонувати деяким сеттерам, щоб перевести його у дійсний стан, у якому він може бути використаний, ви ризикуєте, що споживач класу забуде зателефонувати до цих сетерів або викликає їх неправильно та у вас виявляється недійсний об'єкт.

Обхідним способом може бути створення об'єктів лише за допомогою заводського методу, який перевіряє кожен створений ним об'єкт на дійсність до повернення його абоненту.


3

$ obj = КЛАС :: getInstance (). Потім ми вводимо залежності за допомогою сеттерів і виконуємо запуску ініціалізації за допомогою $ obj-> initInstance ();

Я думаю, що ти робиш це складніше, ніж потрібно. Ми можемо ввести залежності просто через конструктор - і якщо у вас їх багато, просто використовуйте структуру, схожу на словник, щоб ви могли вказати, які саме з них ви хочете використовувати:

$obj = new CLASS(array(
    'Frobnicator' => (),
    'Foonicator' => (),
));

А в конструкторі ви можете забезпечити послідовність так:

if (!array_key_exists('Frobnicator', $args)) {
    throw new Exception('Frobnicator required');
}
if (!array_key_exists('Foonicator', $args)) {
    $args['Foonicator'] = new DefaultFoonicator();
}

$args може бути використаний для встановлення приватних членів у міру необхідності.

Якщо це зробити повністю в конструкторі так, ніколи не буде проміжного стану, коли $objіснує, але не ініціалізується, як це було б у системі, описаній у питанні. Краще уникати таких проміжних станів, оскільки ви не можете гарантувати, що об’єкт завжди буде правильно використаний.


2

Я насправді думав про подібні речі.

Питання, яке я задав, було "Що робить конструктор, і чи можна це зробити інакше?" Я дійшов таких висновків:

  • Це забезпечує ініціалізацію деяких властивостей. Приймаючи їх як параметри та встановлюючи їх. Але це може бути легко застосовано компілятором. Просто анотувавши поля або властивості як "потрібні", компілятор перевірить під час створення екземпляра, чи правильно все встановлено. Заклик створити екземпляр, ймовірно, був би таким же, просто не було б ніякого конструкторського методу.

  • Переконайтесь, що властивості дійсні. Цього можна легко досягти умовою ствердження. Знову ви просто зазначаєте властивості за правильних умов. Деякі мови вже роблять це.

  • Дещо більш складна логіка побудови. Сучасні зразки не рекомендують робити це в конструкторі, але пропонують використовувати спеціалізовані фабричні методи або класи. Тож використання конструкторів у цьому випадку мінімально.

Отже, щоб відповісти на ваше запитання: Так, я вважаю, що це можливо. Але це потребує великих змін у дизайні мови.

І я щойно помітив, що моя відповідь є досить ОТ.


2

Так, ви можете зробити майже все, не використовуючи конструктори, але це явно не втрачає переваг об'єктно-орієнтованих мов програмування.

Сучасними мовами (я розповім тут про C #, на який я програмую) ви можете обмежити частини коду, які можна запускати лише в конструкторі. Завдяки чому ви можете уникнути незграбних помилок. Одне з таких речей - це лише модифікатор для читання :

public class A {
    readonly string rostring;

    public A(string arg) {
        rostring = arg;
    }

    public static A CreateInstance(string arg) {
        var result = new A();
        A.rostring = arg;  // < because of this the code won't compile!
        return result;
    }
}

Як рекомендував раніше Йоахім Зауер, замість того, щоб використовувати Factoryдизайнерський малюнок, читайте про Dependency Injection. Я рекомендую прочитати Марк Семанн в Інтернеті .


1

Моментувати об'єкт типу, залежно від вимог, це абсолютно можливо. Це може бути сам об'єкт, використовуючи глобальні змінні системи для повернення певного типу.

Однак мати клас у коді, який може бути "Усі", це поняття динамічного типу . Я особисто вважаю, що такий підхід створює невідповідність у вашому коді, робить тестові комплекси * та "майбутнє стає невизначеним" щодо потоку запропонованої роботи.

* Я маю на увазі той факт, що тести повинні враховувати спочатку тип, другий результат, який ви хочете досягти. Тоді ви створюєте великий вкладений тест.


1

Щоб збалансувати деякі інші відповіді, стверджуючи:

Конструктор надає вам можливість встановити всі змінні учасника об'єкта на дійсний стан ... у вас ніколи не буде недійсного об'єкта, що позбавить вас від безлічі помилок.

і

З конструктором об’єкт має власну логіку побудови, щоб упевнитись у тому, що немає недійсних примірників такого класу.

Такі твердження іноді передбачають припущення, що:

Якщо в класі є конструктор, який до моменту його виведення перетворив об'єкт у дійсний стан, і жоден із методів класу не мутує цей стан, щоб зробити його недійсним, тоді код за межами класу не може виявити об'єкт цього класу в недійсному стані.

Але це не зовсім так. Більшість мов не мають правила проти конструктора, який передає this( selfабо як вона називає) зовнішній код. Такий конструктор повністю дотримується зазначеного вище правила, і все ж він ризикує піддавати напівзбудованим об'єктам зовнішній код. Це другорядний момент, але його легко не помітити.


0

Це дещо анекдотично, але я зазвичай резервую конструкторів для стану, необхідного для того, щоб об'єкт був повним і корисним. Якщо заборонити будь-який інжектор-інжектор, після запуску конструктора мій об’єкт повинен мати можливість виконувати необхідні для нього завдання.

Все, що можна відкласти, я залишаю поза конструктором (готуючи вихідні значення тощо). При такому підході я відчуваю, що не використовую конструктори ні для чого, крім введення залежності має сенс.

Це також має додаткову перевагу в тому, що ви налаштуєте ваш розумовий процес, щоб нічого не робити передчасно. Ви не будете ініціалізувати чи виконувати логіку, яка ніколи не закінчиться використанням, тому що в кращому випадку все, що ви робите, - це основна установка на майбутню роботу.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.