16

Sometimes you need to write a constructor which can fail. For instance, say I want to instantiate an object with a file path, something like

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

As long as the path points to an appropriate file everything's fine. But if the string is not a valid path things should break. But how?

You could:

  1. throw an exception
  2. return null object (if your programming language allows constructors to return values)
  3. return a valid object but with a flag indicating that its path wasn't set properly (ugh)
  4. others?

I assume that the "best practices" of various programming languages would implement this differently. For instance I think ObjC prefers (2). But (2) would be impossible to implement in C++ where constructors must have void as a return type. In that case I take it that (1) is used.

In your programming language of choice can you show how you'd handle this problem and explain why?

gnat
  • 21,442
  • 29
  • 112
  • 288
pseudosudo
  • 262
  • 1
  • 2
  • 7
  • 1
    Exceptions in Java are one way of handling this. – Mahmoud Hossam Mar 20 '11 at 19:29
  • C++ constructors don't return `void` - they return an object. – gablin Mar 20 '11 at 20:59
  • 6
    @gablin: Actually, that's not strictly true either. `new` calls `operator new` to allocate the memory, then the constructor to fill it. The constructor doesn't return anything, and `new` returns the pointer it got from `operator new`. Whether "doesn't return anything" implies "returns `void`" is up for grabs, though. – Jon Purdy Mar 20 '11 at 21:16
  • @Jon Purdy: Hm, sounds reasonable. Good call. – gablin Mar 21 '11 at 11:51

6 Answers6

9

In Java, you could use exceptions or use the factory pattern, which would allow you to return null.

In Scala, you could return an Option[Foo] from a factory method. That would work in Java too, but would be more cumbersome.

Kim
  • 1,135
  • 1
  • 7
  • 14
9

It's never good to rely on a constructor to do the dirty work. Besides, it's also unclear to another programmer whether or not work is going to be done in the constructor unless there is explicit documentation stating so (and that the user's of the class have read it or been told so).

For example (in C#):

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

What happens if the user doesn't want to load the file straight away? What if they want to do on-demand caching of the file? They can't. You might think of putting a bool loadFile argument in the constructor, but then this makes this messy as you now still need a Load() method to load the file.

Given the current scenario, it's going to be more flexible and clearer to the users of the class to do this:

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

Or alternatively (for something like a resource):

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(...)
         {
         }
     }
}
Nick Bedford
  • 288
  • 1
  • 9
  • in your case if load(..) is failed, we have totally useless object. I'm not sure that I want sprite without any sprite... But the question looks more like holywar subject than real question :) – avtomaton Oct 31 '16 at 21:09
3

Throw an exception.

Null needs to be checked for if you can return it ( and it won't be checked for)

This is what checked exceptions are for. You know it could fail. Callers have to handle this.

Tim Williscroft
  • 3,563
  • 1
  • 21
  • 26
  • It's not true because: 1-exceptions are exceptional. but bad input for example must be handled by developer 2-the users of the API not expects an constructor as this is not common. so, I think **the factory pattern** and **initialiser method** is the most true solution. – S.M.Mousavi Mar 31 '22 at 12:23
3

In C++, constructors are used to create/initialize members of the class.

There's no right answer to this question. But what I've observed so far, is that most of the times is the client (or whoever is going to use your API) who chooses how you should handle this stuff.

Sometimes they might ask you to allocate all the resources the object might need on the constructor and throw an exception if something fails (aborting the creation of the object), or do none of that on the constructor, and make sure the creation will always succeed, leaving these tasks for some member function to do.

If it's up to you to choose the behavior, exceptions are the default C++ way for handling errors and you should use them when you can.

karlphillip
  • 1,548
  • 2
  • 13
  • 25
1

You could also instantiate the object with no parameters, or with only parameters which are sure never to fail, and then you use an initialization function or method from which you can safely thrown an exception or do whatever you wish.

gablin
  • 17,377
  • 22
  • 89
  • 138
  • 7
    I think returning an unusable object that requires further initialization is the worst choice. Now you have a multiple-line fragment that has to be copied whenever the class is used, or factored into a new method. You may as well have the constructor do all the work and throw an exception if the object can't be constructed. – kevin cline Mar 20 '11 at 21:09
  • 2
    @kevin: Depends on the object. Failures will probably be due to external resources so an entity might want to register the intention (e.g. filename) but allow for repeated attempts to fully initialize. For a value object I'd probably lean towards reporting an error that can capture the underlying error, so probably throwing a (wrapped?) exception. – Dave Mar 20 '11 at 22:25
-3

I prefer not to initialize any class or list in constructor. Initialize a class or list whenever you need. Eg.

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

Dont do this

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

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

Instead initialize when required Eg.

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

    public Test()
    { 
    }

    public void SomeFunction()
    {
         emp = new List<Employee>();
    }
}
test
  • 11
  • 1
  • 1
    This is poor advice. You should not allow objects to be in an invalid state. That's what the constructor is for. If you need complex logic then use the factory pattern. – Alex Aug 04 '15 at 09:42
  • 1
    Constructors are there to establish the class invariants, the sample code doesn't help to assert that at all. – Niall Aug 04 '15 at 09:44
  • @Alex I think base of this idea might be true. imagine an initial sate that needs to be transferred to other state in order to be transferable to other states. although this might be a logical mistake by developer to integrate some operations. – S.M.Mousavi Mar 31 '22 at 12:32