Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I abort an object initialization?

Tags:

c#

oop

I am writing an object that must always have certain values. Most notably, it must always have a value for Name property.

public class User
{
    public string Name { get; set; }

    public User(string name)
    {
        Name = name;
    }
}

Now, there are a couple of business rules that I need to implement in this class. One of which is that the Name property must be a unique name. So, I would think that the initializer for this object would look something this:

    public User(string name, IQueryable<User> allUsers)
    {
        var matches = allUsers.Where(q => q.Name == name).ToList();
        if(matches.Any())
        {
            // abort object initialization
        }
        Name = name;
    }

But I'm not sure how I would abort the object initialization. In fact, is that even possible?

Is there a way to abort an object initialization (ie: set object to null) or is there a better way of accomplishing this?

like image 910
quakkels Avatar asked May 08 '12 14:05

quakkels


3 Answers

Well, you'd just throw an exception. But I don't like this way of handling this problem at all. Rather, you should be creating the user through the service, and have the service check if the name is valid or not.

like image 85
jason Avatar answered Nov 01 '22 10:11

jason


I suppose you could check and throw an exception in the object's constructor or Name setter, but eeeehhhh that can come with a host of issues and mixed concerns. I say create the object through a factory that does this check and returns null (or a nicely named exception). Or create the POCO object and do the validation via a separate class/method.

like image 26
Chris Sinclair Avatar answered Nov 01 '22 12:11

Chris Sinclair


Aborting initialization of an object is done by throwing an exception in the constructor, and is recommended to reject invalid input.

public class User
{
    public User(String name) {
        if (String.IsNullOrWhiteSpace(name)) {
            if (name == null) {
                throw new System.ArgumentNullException("Cannot be null.", "name");
            }
            else {
                throw new System.ArgumentException("Cannot be empty.", "name");
            }
        }
    }
}

The business logic you wish to define in the constructor doesn't fit there. Constructors should be lightweight, and instantiation only. Querying some data source is too expensive for a constructor. Because of this, you should use the factory pattern instead. With the factory pattern, a caller might expect there to be some heavy lifting involved with object creation.

public class User
{
    private User(String name) {
        if (String.IsNullOrWhiteSpace(name)) {
            if (name == null) {
                throw new System.ArgumentNullException("Cannot be null.", "name");
            }
            else {
                throw new System.ArgumentException("Cannot be empty.", "name");
            }
        }
    }

    public static User CreateUser(String name) {
        User user = new User(name); // Lightweight instantiation, basic validation

        var matches = allUsers.Where(q => q.Name == name).ToList();

        if(matches.Any())           
        {           
            throw new System.ArgumentException("User with the specified name already exists.", "name");         
        }     

        Name = name;
    }

    public String Name {
        get;
        private set; // Optionally public if needed
    }
}

You can see that the factory pattern fits better, and because it's a method a caller might expect there to be some work going on by invoking it. Whereas with a constructor one would expect it to be lightweight.

If you wanted to go the constructor route, then you would want to try some other method of enforcing your business rules such as when the actual insert into the data source is attempted.

public class User
{
    public User(String name) {
        if (String.IsNullOrWhiteSpace(name)) {
            if (name == null) {
                throw new System.ArgumentNullException("Cannot be null.", "name");
            }
            else {
                throw new System.ArgumentException("Cannot be empty.", "name");
            }
        }
    }
}

public class SomeDataSource {
    public void AddUser(User user) {
        // Do your business validation here, and either throw or possibly return a value
        // If business rules pass, then add the user
        Users.Add(user);
    }
}
like image 29
David Anderson Avatar answered Nov 01 '22 10:11

David Anderson