Как написать конструкторы, которые могут неправильно создать экземпляр объекта

11

Иногда вам нужно написать конструктор, который может потерпеть неудачу. Например, скажем, я хочу создать экземпляр объекта с путем к файлу, что-то вроде

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

Пока путь указывает на соответствующий файл, все в порядке. Но если строка не является допустимым путем, все должно сломаться. Но как?

Ты мог:

  1. бросить исключение
  2. вернуть нулевой объект (если ваш язык программирования позволяет конструкторам возвращать значения)
  3. вернуть действительный объект, но с флагом, указывающим, что его путь не был установлен правильно (тьфу)
  4. другие?

Я предполагаю, что «лучшие практики» различных языков программирования будут реализовывать это по-разному. Например, я думаю, что ObjC предпочитает (2). Но (2) было бы невозможно реализовать в C ++, где конструкторы должны иметь void в качестве возвращаемого типа. В этом случае я понимаю, что (1) используется.

На выбранном вами языке программирования вы можете показать, как вы справитесь с этой проблемой, и объяснить, почему?

garageàtrois
источник
1
Исключения в Java - один из способов справиться с этим.
Махмуд Хоссам
Конструкторы C ++ не возвращают void- они возвращают объект.
Габлин
6
@ Габлин: На ​​самом деле, это тоже не совсем верно. newвызывает operator newдля выделения памяти, а затем конструктор для ее заполнения. Конструктор не возвращает ничего и newвозвращает указатель, полученный от него operator new. Однако, «ничего не возвращает» подразумевает, что «возвращается void» - дело за хваткой.
Джон Перди
@ Джон Парди: Хм, звучит разумно. Хороший звонок.
Габлин

Ответы:

8

Никогда не стоит полагаться на конструктора для грязной работы. Кроме того, другому программисту также неясно, будет ли работа выполняться в конструкторе, если нет явной документации, подтверждающей это (и что пользователь класса прочитал ее или ему так сказали).

Например (в C #):

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

Что произойдет, если пользователь не захочет сразу загрузить файл? Что если они захотят сделать кэширование файла по требованию? Они не могут. Вы можете подумать о том, чтобы поместить bool loadFileаргумент в конструктор, но тогда это становится грязным, поскольку теперь вам все еще нужен 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, чем реальный вопрос :)
автомат
7

В Java вы можете использовать исключения или использовать шаблон фабрики, который позволит вам возвращать ноль.

В Scala вы можете вернуть Option [Foo] из фабричного метода. Это будет работать и на Java, но будет более громоздким.

Ким
источник
Использование исключений или фабрики в .NET языках тоже.
Бет Уайтзел
3

Брось исключение.

Необходимо проверить наличие нулевого значения, если вы можете вернуть его (и оно не будет проверено)

Для этого и нужны проверенные исключения. Вы знаете, что это может потерпеть неудачу. Абоненты должны справиться с этим.

Тим Виллискрофт
источник
3

В C ++ конструкторы используются для создания / инициализации членов класса.

Там нет правильного ответа на этот вопрос. Но то, что я наблюдал до сих пор, - это то, что большую часть времени клиент (или тот, кто собирается использовать ваш API) выбирает, как вы должны справиться с этим.

Иногда они могут попросить вас выделить все ресурсы, которые могут понадобиться объекту в конструкторе, и выдать исключение в случае сбоя (прерывание создания объекта), или ничего не делать в конструкторе, и убедиться, что создание всегда будет успешным. оставляя эти задачи для некоторой функции-члена.

Если вы сами выбираете поведение, исключения являются стандартным способом C ++ для обработки ошибок, и вы должны использовать их, когда можете.

karlphillip
источник
1

Вы также можете создать экземпляр объекта без параметров или только с параметрами, которые наверняка никогда не потерпят неудачу, и затем вы используете функцию или метод инициализации, из которых вы можете безопасно генерировать исключение или делать все, что пожелаете.

gablin
источник
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
Конструкторы предназначены для установки инвариантов классов, пример кода вовсе не помогает утверждать это.
Найл