Як написати конструктори, які можуть не вдатися належним чином створити об'єкт


12

Іноді потрібно написати конструктор, який може вийти з ладу. Наприклад, скажіть, що я хочу створити об'єкт шляхом до файлу, щось подібне

obj = new Object("/home/user/foo_file")

Поки шлях вказує на відповідний файл, все добре. Але якщо рядок не є дійсним шляхом, речі повинні зламатися. Але як?

Ви можете:

  1. кинути виняток
  2. повернути нульовий об'єкт (якщо ваша мова програмування дозволяє конструкторам повертати значення)
  3. повернути дійсний об'єкт, але з прапором, який вказує, що шлях не встановлений належним чином (ugh)
  4. інші?

Я припускаю, що "найкращі практики" різних мов програмування реалізували б це по-різному. Наприклад, я думаю, що ObjC віддає перевагу (2). Але (2) було б неможливо реалізувати в C ++ там, де конструктори повинні бути недійсними як тип повернення. У такому випадку я вважаю, що використовується (1).

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


1
Винятки на Java - це один із способів вирішення цього питання.
Махмуд Хоссам

Конструктори C ++ не повертаються void- вони повертають об'єкт.
габлін

6
@gablin: Власне, це теж не зовсім вірно. newзакликає operator newвиділити пам'ять, а потім конструктор її заповнити. Конструктор нічого не повертає і newповертає вказівник, який він отримав operator new. Якщо "нічого не повертає", це означає, що "повернення void" вирішується на захоплення.
Джон Перді

@Jon Purdy: Гм, звучить розумно. Гарний дзвінок.
габлін

Відповіді:


8

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

Наприклад (у C #):

public Sprite
{
    public Sprite(string filename)
    {
    }
}

Що станеться, якщо користувач не хоче відразу завантажити файл? Що робити, якщо вони хочуть зробити кешування файлу на вимогу? Вони не можуть. Ви могли б подумати про приміщенні bool loadFileаргументу в конструкторі, але тоді це робить Messy , як тепер ви все ще потрібен Load()спосіб , щоб завантажити файл.

З огляду на поточний сценарій, користувачам класу це стане більш гнучким та зрозумілим:

Sprite sprite = new Sprite();
sprite.Load("mario.png");

Або в якості альтернативи (для чогось типу ресурсу):

Sprite sprite = new Sprite();
sprite.Source = "mario.png";
sprite.Cache();

// On demand caching of file contents.
public void Cache()
{
     if (image == null)
     {
         try
         {
             image = new Image.FromFile(Source);
         }
         catch(...)
         {
         }
     }
}

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

7

У Java ви можете використовувати винятки або використовувати заводський візерунок, який дозволить повернути нуль.

У Scala ви можете повернути опцію [Foo] з заводського методу. Це також працювало б на Java, але було б більш громіздким.


Використання винятків або заводських даних у мовах .NET.
Бет Вайтцел

3

Киньте виняток.

Null потрібно перевірити, чи зможете ви повернути його (і він не буде перевірений)

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


3

У C ++ конструктори використовуються для створення / ініціалізації членів класу.

Правильної відповіді на це питання немає. Але те, що я спостерігав до цього часу, - це те, що більшість випадків клієнт (або той, хто збирається використовувати ваш API) вибирає, як слід поводитися з цими матеріалами.

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

Якщо ви обираєте поведінку, винятки - це спосіб C ++ за замовчуванням для обробки помилок, і ви повинні використовувати їх, коли можете.


1

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


6
Я думаю, повернення непридатного об'єкта, який потребує подальшої ініціалізації, є найгіршим вибором. Тепер у вас є багаторядковий фрагмент, який потрібно копіювати кожного разу, коли використовується клас, або перетворюється на новий метод. Ви також можете змусити конструктора виконати всю роботу та викинути виняток, якщо об’єкт неможливо побудувати.
Кевін Клайн

2
@kevin: Залежить від об'єкта. Збої, ймовірно, будуть пов’язані із зовнішніми ресурсами, тому суб'єкт господарювання може захотіти зареєструвати намір (наприклад, ім'я файлу), але дозволяти повторні спроби повністю ініціалізуватися. Для об'єкта значення я, мабуть, схиляюся до повідомлення про помилку, яка може захопити основну помилку, тому, ймовірно, викидає (обгорнуте?) Виняток.
Дейв

-4

Я вважаю за краще не ініціалізувати жоден клас чи список у конструкторі. Ініціалізуйте клас чи список, коли вам потрібно. Напр.

public class Test
{
    List<Employee> test = null;
}

Не роби цього

public class Test
{
    List<Employee> test = null;

    public Test()
    {
        test = new List<Employee>();
    }
}

Замість цього ініціалізуйте, коли потрібно, напр.

public class Test
{
    List<Employee> emp = null;

    public Test()
    { 
    }

    public void SomeFunction()
    {
         emp = new List<Employee>();
    }
}

1
Це погана порада. Ви не повинні дозволяти об'єктам перебувати в недійсному стані. Ось для чого і конструктор. Якщо вам потрібна складна логіка, тоді використовуйте заводський зразок.
AlexFoxGill

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