10 January, 2015

How to replace "switch" with the polymorphism?

Problem

A few days ago we were discussing the coding standards, that we are going to adhere. One of the points was preventing from creating of the very big conditional statements.

Lets take the "switch" statement into consideration. It is nothing wrong in using this statement until it does one thing, but as we all know, "switch" usually makes a lot of things and it is much bigger, then it should be. If we want to adhere SOLID principles, we cannot write huge, multi-actions conditional statements.

In this post I want to show you one of the most popular methods of replacing of the "switch" with the polymorphism. Lets see how to do this?

Solution

public class DocumentsHandler
{
    public void HandleDocument(Document document)
    {
        //...
        List importedDocumentElementsIds = GetImportedDocumentElementsIds(clientName, document);
        //...
    }

    public List GetImportedDocumentElementsIds(string clientName, object document)
    {
        /* Somewhere in the code we have:
         * - entity context
         * - name of the client
         * 
         * Some operations ...
         * 
         * */
        switch typeof(document)
        {
            case Sales :
                return BaseDocumentElementsImport(clientName, context.GetSalesDocumentsElements())
            case Purchase :
                return BaseDocumentElementsImport(clientName, context.GetPurchaseDocumentsElements())
            case Custom :
                return BaseDocumentElementsImport(clientName, context.GetCustomDocumentsElements())
            // other document types...
            case default :
                return BaseDocumentElementsImport(clientName, context.GetOtherDocumentsElements())
        }
    }
}

Now we will replace an old Document with a new one. It will be an abstract class with an template method, which we are going to override.

public abstract class Document
{
    public abstract List ImportDocumentElements(string clientName)
}

In the next step we create a set of derived classes which will contain overriden template method.

    public class SalesDocument : Document
    {
        //...
          public override List ImportDocumentElements(string clientName) 
          {
            return BaseDocumentElementsImport(clientName, context.GetSalesDocumentsElements());
          } 
        //...
    }

    public class PurchaseDocument : Document
    {
        //...
          public override List ImportDocumentElements(string clientName) 
          {
            return BaseDocumentElementsImport(clientName, context.GetPurchaseDocumentsElements());
          } 
        //...
    }

    public class CustomDocument : Document
    {
        //...
          public override List ImportDocumentElements(string clientName) 
          {
            return BaseDocumentElementsImport(clientName, context.GetCustomDocumentsElements());
          } 
        //...
    }

    public class OtherDocument : Document
    {
        //...
          public override List ImportDocumentElements(string clientName) 
          {
            return BaseDocumentElementsImport(clientName, context.GetOtherDocumentsElements());
          } 
        //...
    }

Now in the code we can write simple, short line of code like below:

List importedDocumentElementsIds = document.ImportDocumentElements(clientName));

Benefits

It seems that the code is now more complicated and we wrote more LOCs, so why did we do this?

  • We do not duplicate the code because we do not have identical conditions.
  • Usage is simpler and more elegant, because we write smaller numer of LOCs.
  • Object knows everything about itself. We do not need to pass the state the the object under some conditions. We only need to tell the object to perform the operation and it will decide how to do this.
  • Now our code is extensible. We can add new class with new implementation of the overriden method instead of adding new case to the "switch". To be honest, "switch" conditions like to grow very quickly.

0 comments:

Post a Comment