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
  • 20,543
  • 29
  • 115
  • 306

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

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
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,525
-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