Статичний метод створення - плюси і мінуси порівняно з конструкторами


11

Які плюси та мінуси використання статичних методів створення об'єктів над конструкторами?

class Foo {
  private Foo(object arg) { }

  public static Foo Create(object arg) {
    if (!ValidateParam(arg)) { return null; }
    return new Foo(arg);
  }
}

Мало що я можу придумати:

Плюси:

  • Повернути null замість викидання виключення (назвіть його TryCreate). Це може зробити код більш лаконічним та чистим на стороні клієнта. Клієнти рідко очікують, що конструктор вийде з ладу.
  • Створюйте різні види об’єктів з чіткою семантикою, наприклад, CreatFromName(String name)таCreateFromCsvLine(String csvLine)
  • При необхідності може повернути кешований об'єкт або похідну реалізацію.

Мінуси:

  • Менш відкритий, складніший для прокрутки коду.
  • Деякі зразки, як-от серіалізація або відображення, є складнішими (наприклад Activator<Foo>.CreateInstance())

1
Це повільніше. У будь-якому випадку вам доведеться обробляти нульову посилання.
Амір Резай

1
@Amir Обробка null є ( Foo x = Foo.TryCreate(); if (x == null) { ... }). Обробка виключення ctor - це ( Foo x; try { x = new Foo(); } catch (SomeException e) { ... }). Під час виклику звичайного методу я віддаю перевагу виняткам з кодів помилок, але зі створенням об'єкта TryCreateздається більш чистим.
dbkk

Чи хотіли б у вашій валідації перевірити будь-який тип?
Амір Резай

Що стосується другого Pro, "Створюйте різні види об'єктів з чіткою семантикою, наприклад, CreatFromName (ім'я рядка) та CreateFromCsvLine (String csvLine)", можливо, вам буде краще створювати Nameта вводити CsvLineтипи, а не виражати вимоги через назви методів. Це дозволить вам перевантажити Create. Використання рядків для обох можна вважати "примітивною одержимістю" (якщо припустити, що ви не зробили цього вибору з відомих причин продуктивності). Ознайомтесь з предметами Каліністики, щоб отримати цікавий спосіб вивчити це.
bentayloruk

Відповіді:


7

Найбільший недолік статичних " творців ", ймовірно, обмежує спадщину. Якщо ви або користувач вашої бібліотеки виводить клас із вашої Foo, то Foo::Create()стає набагато марним. Вся визначена там логіка доведеться знову переписати у спадок Create().

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


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

7

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

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