Null being a part of programming languages is a billion dollar mistake. -Tony Hoare
Writing everyday code irrespective of Language used (either be C# or Java or Python) we write null checks. Well we write null checks so often that a study estimates on an average one developer gives 5% of his entire development time to null checks only. So we can think of the importance of null checks. It's also the main problem of production issues specially for Junior developers.
Well writing null checks is not a problem until 100 lines of code contains 20 lines of null check code 😀
To reduce the number of lines for null checks, so that developer only focus on business logic instead of null check, Null Object design pattern was invented.
Let's look at how we can use Null Object Design Pattern for our Invoice Application. Here we have different types of Invoice logic written in Separate Invoice classes (EmailInvoice, PDFInvoice, SMSInvoice). The User will choose the Invoice Type at runtime which is depicted in Program.cs . In invoice repository at the end of all the if checks, if we return null, than we will have to check for null check for every GetInvoice method output. But when we use NullInvoice class. It just provides empty implementation for every method, so we don't have to check for null.
Invoice.cs
public interface IInvoice { public void GenerateInvoice(); public void SendInvoice(); } public enum InvoiceType { EmailInvoice, PDFInvoice, SMSInvoice, DefaultInvoice } public sealed class EmailInvoice : IInvoice { public void GenerateInvoice() { Console.WriteLine("Email Invoice Generaeted"); } public void SendInvoice() { Console.WriteLine("Email Invoice Sent"); } } public sealed class PDFInvoice : IInvoice { public void GenerateInvoice() { Console.WriteLine("Pdf Invoice Generated"); } public void SendInvoice() { Console.WriteLine("Pdf Invoice Sent"); } } public sealed class SMSInvoice : IInvoice { public void GenerateInvoice() { Console.WriteLine("SMS Invoice Generated"); } public void SendInvoice() { Console.WriteLine("SMS Invoice Sent"); } } public sealed class NullInvoice : IInvoice { private NullInvoice() { } public void GenerateInvoice() { } public void SendInvoice() { } public static NullInvoice Instance { get { return Nested.instance; } } private class Nested { static Nested() { } internal static readonly NullInvoice instance = new NullInvoice(); } }
In NullInvoice class we have used Singleton Pattern, so object will be created only once and will be resued evertime saving some CPU usage.
InvoiceRepository.cs
public class InvoiceRepository { public IInvoice GetInvoice(InvoiceType invoiceType) { if (invoiceType == InvoiceType.EmailInvoice) return new EmailInvoice(); if (invoiceType == InvoiceType.PDFInvoice) return new PDFInvoice(); if (invoiceType == InvoiceType.SMSInvoice) return new SMSInvoice(); return NullInvoice.Instance; } }
Program.cs
class Program { static void Main(string[] args) { InvoiceRepository invoiceRepository = new InvoiceRepository(); IInvoice emailInvoice = invoiceRepository.GetInvoice(Models.InvoiceType.EmailInvoice); emailInvoice.GenerateInvoice(); emailInvoice.SendInvoice(); emailInvoice = invoiceRepository.GetInvoice(Models.InvoiceType.DefaultInvoice); emailInvoice.GenerateInvoice(); emailInvoice.SendInvoice(); } }
As you can see in the above Program.cs, After using GetInvoice method we haven't written any null check because we already know that InvoiceRepository is never gonna return null.
Things to keep in mind:
- Sometimes it's good to have null, We shouldn't overuse Null Object design pattern.
- Also developers working with code should know about this pattern and usage, otherwise they will continue to write null checks despite Null Object pattern being implemented.
- Null Object pattern is useful where we want to return an object of expected type, yet to do nothing.
- Get link
- X
- Other Apps
- Get link
- X
- Other Apps
Comments
Post a Comment