Sunday 5 August 2012

Learn The Simple, but widely used Adapter Pattern in 5 minutes

Whenever there is a design that needs to be done for any new project requirement or an enhancement, people always throw up some patterns to be implemented, the first pattern comes out of their mouth is always Adapter, I used to think the pattern seems to be very complex, because everybody use it as a jargon, but the irony is, it is the simplest pattern next to Façade pattern.

Let’s test drive

Let us discuss with a scenario. We need to create two types of report in our project by batch process which runs at night everyday.

1.       PDF report
2.       Doc report. 

Part of the problem is that user favorite type of report is saved in the database and developer will not have any control of the report type.  Report that needs to be generated is known at run-time based on user choice. We need to adapt two types of report into one type, so we can use it across the layer irrespective of the report type. 

 In this scenario, PDF Report will be created by the open source code which you have control to develop and change the code, change the functionality and development is done by the development team of the company.

For Doc report, We use Microsoft office Interop DLL to create the doc report, where you do not have any control over the code. You just have to call the wordDoc.SaveAs (filename). Microsoft dll and PdfReport DLL are incompatible as per our current scenario. 

Before solving the problem with Adapter pattern, we need to understand the common terms that are used in the Adapter Pattern world.

Target: defines the domain-specific interface that Client uses. In our case IReportTarget is our Target. Client has defined an interface for creating report.

Adapter: adapts the interface Adaptee to the Target interface. In our case it is DocReportAdapter which takes care adapting the Microsoft Office Interop dll to IReportTarget

Adaptee: defines an existing interface that needs adapting. In our case it is the Microsoft Office Interop dll which needs to adapted.

Client: One that consumes the Target. 




Below is the code for PdfReport. This is implemented to the type IReportTarget. This is our Target class. Here we have defined an interface which creates the report and PdfReport implements IReportTarget


namespace AdapterPattern
{
    public interface IReportTarget
    {
         void CreateReport();
    }
}



namespace AdapterPattern
{
    public class PdfReport:IReportTarget
    {
        public void CreateReport()
        {
            //Own Internal Implementation for creating PDF report
        }
    }
}

. In the below code the argument type defines the report type based on the value from database. If the argument value is doc then word report is created by Microsoft dll and when the argument value is PDF then pdf report is created using the open source code PdfReport
using Microsoft.Office.Interop.Word;
       static void Main(string[] args)
        {
            if (args[0] == "Doc")
            {
                Document wordDoc = new Document();
                wordDoc.SaveAs(ref FileName, ref FileFormat, ref LockComments,
                  ref missing, ref AddToRecentFiles, ref missing,
                  ref ReadOnlyRecommended, ref EmbedTrueTypeFonts,
                  ref SaveNativePictureFormat, ref SaveFormsData,
                  ref SaveAsAOCELetter, ref missing, ref InsertLineBreaks,
                  ref AllowSubstitutions, ref LineEnding, ref AddBiDiMarks);
            }
            else if (args[0] == "Pdf")
            {
                IReportTarget pdfReport = new PdfReport();
                pdfReport.CreateReport();
            }
            else
            {
                throw new Exception("Invalid Report Type");
            }
        }



Let us look into the adaptee which needs to be adapted, the Microsoft office DLL we use to create the doc report needs to be adapted so it will be compatible with the report application. Since the Adaptee is not compatible we now have the redundant code

The above code exists because the Microsoft.Office.Interop.Word, is not compatible with IReportTarget.

Let’s make the document of Microsoft.Office.Interop.Word compatible with adaptee, so we can use to be compatible with all reports.

using Microsoft.Office.Interop.Word;

namespace AdapterPattern
{
    public class DocReportAdapter : IReportTarget
    {

     public DocReportAdapter()
      {

      }

      public void CreateReport()
      {
          Document wordDoc = new Document();

          object FileName = "myfile.doc";

          if (wordDoc.SaveFormat == (int)WdSaveFormat.wdFormatDocument)
          {
              wordDoc.SaveAs(ref FileName, ref FileFormat, ref LockComments,
                  ref missing, ref AddToRecentFiles, ref missing,
                  ref ReadOnlyRecommended, ref EmbedTrueTypeFonts,
                  ref SaveNativePictureFormat, ref SaveFormsData,
                  ref SaveAsAOCELetter,ref missing, ref InsertLineBreaks,
                  ref AllowSubstitutions, ref LineEnding, ref AddBiDiMarks);
          }


      }

    }
}

We now create a class file which incorporates all the implementation details for the microsoft DLL and exposes only the CreateReport method which inturn has implemented IReportTarget. DocReportAdapter is created which has all the implementation details and exposes only CreateReport()

Now the Microsoft.Office.Interop.Word.Document is compatible with our IReportTarget. So we get DocReportAdapter as Adapter

static void Main(string[] args)
        {
            IReportTarget favoriteReport = null;
            if (args[0] == "Doc")
            {
                favoriteReport = new DocReportAdapter();
                favoriteReport.CreateReport();
            }
            else if (args[0] == "Pdf")


            {
                 favoriteReport = new PdfReport();
                 favoriteReport.CreateReport();
            }
            else
            {
                throw new Exception("Invalid Report Type");
            }
        }


We can use the DocReportAdapter to be used in our client Program.cs with IReportTarget, since it is compatible with our application.Now we have two report functionality implemented with IReportTarget, so we have compatible report creator

If we have another type of report that is needed in our project, we need to just implement the functionality in a separate class file encapsulated and then implement with IReportTarget, so it will compatible with existing reports.

 Try these examples with a class project in .Net or Java and understand all the keywords such as Adapter, Adaptee and Target. You can also define your own scenario, where it will be more easy to understand, This is the simple of all patterns, Try and let me know your comments.


Leave a comment to share what you learned, and hit the “like” button to let others know about it (make sure you’re logged into Facebook to like and comment). 


Build Bot using LUIS