Design Decision: Null Objects

25 בדצמבר 2008

4 תגובות

Have you ever seen this code:

class Program
{
    static void Main(string[] args)
    {
        Person p = new PersonManager().GetPersonById(1);
    }
}

class PersonManager
{
    public List<Person> People { get; set; }

    public PersonManager()
    {
        People = new List<Person>();
    }
    public Person GetPersonById(int id)
    {
        Person res = (from p in People
                   where p.Id == id
                   select p).FirstOrDefault();

        return res;
    }
}

class Person
{
    public int Id { get; set; }
    public string Name { get; set; }
}

Take a look at the GetPersonById method within the PersonManager class. What will be the outcome?

That is right, it can be either NULL or the person object that we found in the list. Now, do not get me wrong, it is not that there is something wrong with this code…It is the fact that we have a design decision to make regarding it that we usually ignore.

The Problem

So, what is the design decision we need to make? As the post title implies, I mean that we need to decide whether objects returned from methods can return as NULL. We have three options when returning objects from a method:

  1. The actual value – on success.
  2. NULL – on failure.
  3. Empty object – on failure.

Option number 2 and 3 are a result of failure to execute the method. In my example, it indicates that the Person was not found in the list.

Now, because we have two options we can select when to use the NULL and when to use the Empty object. This allows each developer to do what he feels like. This can lead to inconsistency in the code. This can lead to bugs…and believe me, there will be bugs.

The Solution

We must make a decision. We need to create a standardization to this issue. I like calling it "NULL Object" decision. Every developer should use the same standard for returning objects from methods. It can be either of the above. Both is fine! But make sure you follow it always. 

How To Decide?

I like my team to help me decide, so I usually gather my team together and hold a discussion. At the end of it, we vote. The result will be our decision. The best time to have this meeting is at the beginning of the development. However, if you are using an Agile methodology, the next iteration is a good candidate.

 

 

kick it on DotNetKicks.com

הוסף תגובה
facebook linkedin twitter email

כתיבת תגובה

האימייל לא יוצג באתר. (*) שדות חובה מסומנים

4 תגובות

  1. Leonardo Diaz25 בדצמבר 2008 ב 21:22

    I've had those problems too, but I never gave that attention. I develop with php and Java and usually I return null when that happens, but I don't have any argument why I do that.
    I'm wondering and hoping you post what was your decision about this subject.

    להגיב
  2. ekampf26 בדצמבר 2008 ב 6:31

    I find returning an empty object odd and counter intuitive which can obviously lead to bugs. You can't return a Person instance if there's no person…
    Even if you do return an empty object you have to have some standard method to verify emptiness (IsEmpty() ?), so why do it instead of using null which is more standard?

    The design choice in case of failure should be NULL vs. Exception.
    Never use empty objects unless you have to…

    P.S.
    Another design choice\patern I like using in my data access layer is returning IQueryable and using Filter extension methods, and only triggering the actual call to DB when required.

    So in this case the design would be:
    1. Method GetPeople() in PersonManager that returns all the people in IQueryable 2. Extension method on IQueryable called WithId that does the WHERE condition
    3. The call in the program code would be:

    new PersonManager().GetPeople().WithId(123).FirstOrDefault();

    Another advantage is that the FirstOrDefault() call is in the program code. So its very clear wether NULL check is required, or exception is expected etc… להגיב

  3. kolbis26 בדצמבר 2008 ב 8:10

    Leonardo, thanks for sharing…
    Eran, Thanks for your answer. Let me comment on your answer…First let me say that your solution is based on .NET and even more (.NET 3.5). You sujested an interesting idea, however this require you to get all people from the database first and them filter them…This can cause a perfromance hit, no? You may not agree but another thing is the length of the method. I do not like methods that are that long…An one more thing is the "Metod in Method" coding stile. This is yet another standard that I usually agree with the team. In my case I never use it.

    The bottum line is that I would try using the NULL return option rather then using the Empty Object. However there are cases where Empty Objects can work better.

    להגיב
  4. ekampf26 בדצמבר 2008 ב 11:15

    No no no. Because you're using IQueryable and only issuing the actual query when calling FirstOrDeault. The query is constructed then, with filters applied.

    So the sample I described will issue the exact same SQL query as yours… (ofcourse getting all records from DB and filtering after makes no sense).

    As for length, I dont think GetPeople().WithId(123) is long.
    However if your code contains a lot of complex queries, by all means turn GetPeople().WithCity("TLV").WithAge(18)… to its own method…
    The convention is made to make things easier not more complex :)

    However the main takeaway is returning IQueryable so that:
    1. You don't cross LINQ provider boundaries so you can continue modifying the query in calling code without performance hits.
    2. Calling code makes the actual query. Its very the decision wether to check for NULL, throw an exception etc. is there…

    Can you give an example for a good empty object scenario?

    להגיב