如何编写可能无法正确实例化对象的构造函数


11

有时您需要编写可能会失败的构造函数。例如,假设我要实例化带有文件路径的对象,例如

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

只要路径指向适当的文件,一切都很好。但是,如果字符串不是有效路径,则应该中断。但是如何?

你可以:

  1. 抛出异常
  2. 返回空对象(如果您的编程语言允许构造函数返回值)
  3. 返回一个有效的对象,但带有一个标志,指示其路径设置不正确(ugh)
  4. 其他?

我假设各种编程语言的“最佳实践”将以不同的方式实现此目标。例如,我认为ObjC更喜欢(2)。但是(2)不可能在C ++中实现,在C ++中,构造函数必须将void作为返回类型。在这种情况下,我认为使用(1)。

您可以使用您选择的编程语言来说明如何处理此问题并解释原因吗?


1
Java中的异常是处理这种情况的一种方法。
Mahmoud Hossam

C ++构造函数不返回void-他们返回一个对象。
gablin 2011年

6
@gablin:实际上,这也不完全正确。new调用operator new分配内存,然后构造函数填充它。构造函数不返回任何内容,并new返回从中获取的指针operator new。但是,“不返回任何东西”是否意味着“返回void”值得商grab。
乔恩·普迪

@Jon Purdy:嗯,听起来很合理。好决定。
gablin 2011年

Answers:


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(...)
         {
         }
     }
}

在您的情况下,如果load(..)失败,则我们有完全无用的对象。我不确定我是否需要没有任何精灵的精灵...但是这个问题看起来更像是圣战主题,而不是真实的问题:)
avtomaton

7

在Java中,您可以使用异常或使用工厂模式,这将允许您返回null。

在Scala中,您可以从工厂方法返回Option [Foo]。这也可以在Java中使用,但是会比较麻烦。


也可以在.NET语言中使用例外或工厂。
Beth Whitezel'3

3

引发异常。

如果可以返回Null,则需要检查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 2015年

1
构造函数在那里建立类不变式,示例代码根本无法断言这一点。
Niall
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.