FeedC# TutorialArchiveAbout

Adapter Design Pattern In C#

Adapter Design Pattern In C#

Contents

What is Adapter Design Pattern?

The Adapter Pattern is a software design pattern that attempts to reconcile the differences between two otherwise-incompatible interfaces. This pattern is especially useful when attempting to adapt to an interface which cannot be refactored.

When you have a class that needs to utilize a particular interface, and you have a library that includes the functionality you need, but it doesn’t use the interface that you require. You can achieve the reuse of that library’s code by creating an Adapter class. This adapter class sits between your client code, and the code that’s in this library, and adapts one interface to the other. The Adapter design pattern is one of the most common, and most useful patterns available to us as software developers.

Adapter Pattern Structure

Adapter Design Pattern Structure Let’s look at the structure of the Adapter Pattern using this UML diagram. The two basic players within this example are the Client, and the Adaptee, shown above.

Now,

The Client needs some of the logic that exists within the Adaptee. Specifically, there is this AdaptedOperation that has the code that the Client wants to be able to utilize. Unfortunately, the Client has been written in such a way that it cannot directly call this AdaptedOperation because its interface is not the one that the Client expects. This is where the Adapter Pattern comes into play.

First,

The Adapter interface is created, exposing an Operation that has the interface the Client expects.

Next,

For each different implementation required, at a minimum, one, its different ConcreteAdapter is created that takes that Operation and implements it, such that that code calls the AdaptedOperation. In this way, the Client will now be able to call the Operation on the ConcreteAdapter, which in turn will call the AdaptedOperation on the Adaptee.

The Client really wants to use the Adaptee directly, but unfortunately it can’t due to the incompatible interface. The Adapter Pattern is simply allowing us to achieve this despite this incompatibility.

Example 1

First, let’s see a very simple implementation of Adapter pattern in C#,

public interface IPerson
{
    string Name { get; set; }
}

public interface IFrenchPerson
{
    string Nom { get; set; }
}

public class Person : IPerson
{
    public string Name { get; set; }
}

public class FrenchPerson : IFrenchPerson
{
    public string Nom { get; set; }
}

public class PersonService
{
    public void PrintName(IPerson person)
    {
        Debug.Write(person.Name);
    }
}

public class FrenchPersonAdapter : IPerson
{
    private readonly IFrenchPerson frenchPerson;

    public FrenchPersonAdapter(IFrenchPerson frenchPerson)
    {
        this.frenchPerson = frenchPerson;
    }

    public string Name
    {
        get { return frenchPerson.Nom; }
        set { frenchPerson.Nom = value; }
    }
}

In the above example PersonService class becomes Client, IPerson interface becomes our Adapter Interface and FrenchPerson class becomes Adaptee, as FrenchPerson is incompatible with PersonService we create FrenchPersonAdapter by implementing IPerson Adapter Interface.

var service = new PersonService();
var person = new Person();
var frenchPerson = new FrenchPerson();

service.PrintName(person);
service.PrintName(new FrenchPersonAdapter(frenchPerson));

As you can see from the above code FrenchPerson is now behaving like a Person Class with the use of FrechPersonAdapter and thus is now compatible with PersonService.

Example 2

Let’s see another implementation of Adapter pattern in C#, with IDbDataAdapter example,

IDbDataAdapter is one of .Net built in interfaces under System.Data namespace.

The IDbDataAdapter interface inherits from the IDataAdapter interface and allows an object to create a DataAdapter designed for use with a relational database.

The IDbDataAdapter interface allow an inheriting class to implement a DataAdapter class, which represents the bridge between a data source and a DataSet. For more information about DataAdapter classes, see Populating a DataSet from a DataAdapter.

public class DataRenderer
    {
        private readonly IDbDataAdapter _dataAdapter;

        public DataRenderer(IDbDataAdapter dataAdapter)
        {
            _dataAdapter = dataAdapter;
        }

        public void Render(TextWriter writer)
        {
            writer.WriteLine("Rendering Data:");
            var myDataSet = new DataSet();

            _dataAdapter.Fill(myDataSet);

            foreach (DataTable table in myDataSet.Tables)
            {
                foreach (DataColumn column in table.Columns)
                {
                    writer.Write(column.ColumnName.PadRight(20) + " ");
                }
                writer.WriteLine();
                foreach (DataRow row in table.Rows)
                {
                    for (int i = 0; i < table.Columns.Count; i++)
                    {
                        writer.Write(row[i].ToString().PadRight(20) + " ");
                    }
                    writer.WriteLine();
                }
            }
        }
    }

The above DataRenderer class only renders data that comes from data adapters in form of data Tables.

Now,

public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
}
List<Person> persons = new List<Person>() {
    new Person(){ Name ="Foo", Age = "25"} ,
    new Person(){ Name ="Bar", Age = "25"}    
};

Suppose,

If we want to render the above persons list using the above DataRenderer class, in the same format the Renderer method is writing content in TextWriter, but our DataRenderer class is incompatible with persons datatype in that case we have to create another DataRenderer just for Generic Person List datatype.

However,

What if we could convert this List<Person> persons into the format that is compatible with DataRenderer class then we dont have to write the same repeatable code for rendering data.

Let’s create a PersonCollectionDbAdapter class to resolve this problem.

class PersonCollectionDbAdapter : IDbDataAdapter
{
    private readonly IEnumerable<Person> _persons;

    public PersonCollectionDbAdapter(IEnumerable<Person> patterns)
    {
        _persons = patterns;
    }

    public int Fill(DataSet dataSet)
    {
        var myDataTable = new DataTable();
        myDataTable.Columns.Add(new DataColumn("Name", typeof(string)));
        myDataTable.Columns.Add(new DataColumn("Description", typeof(int)));

        foreach (var person in _persons)
        {
            var myRow = myDataTable.NewRow();
            myRow[1] = person.Name;
            myRow[2] = person.Age;
            myDataTable.Rows.Add(myRow);
        }
        dataSet.Tables.Add(myDataTable);
        dataSet.AcceptChanges();

        return myDataTable.Rows.Count;
    }

    //Below methods are not implemented because they are useless in our scenario

    public DataTable[] FillSchema(DataSet dataSet, SchemaType schemaType)
    {
        throw new NotImplementedException();
    }

    public IDataParameter[] GetFillParameters()
    {
        throw new NotImplementedException();
    }

    public int Update(DataSet dataSet)
    {
        throw new NotImplementedException();
    }

    public MissingMappingAction MissingMappingAction
    {
        get { throw new NotImplementedException(); }
        set { throw new NotImplementedException(); }
    }

    public MissingSchemaAction MissingSchemaAction
    {
        get { throw new NotImplementedException(); }
        set { throw new NotImplementedException(); }
    }

    public ITableMappingCollection TableMappings
    {
        get { throw new NotImplementedException(); }
    }

    public IDbCommand SelectCommand
    {
        get { throw new NotImplementedException(); }
        set { throw new NotImplementedException(); }
    }

    public IDbCommand InsertCommand
    {
        get { throw new NotImplementedException(); }
        set { throw new NotImplementedException(); }
    }

    public IDbCommand UpdateCommand
    {
        get { throw new NotImplementedException(); }
        set { throw new NotImplementedException(); }
    }

    public IDbCommand DeleteCommand
    {
        get { throw new NotImplementedException(); }
        set { throw new NotImplementedException(); }
    }
}

Awesome. We have finished our implementation.

Adpater Pattern Real World Example

If you yourself are writing a library, or a framework, and you want to ensure that it’s useable by future classes that may not even have been written yet, and so you cannot be certain what their interface will be, you can add support for an Adapter as part of your interface for your code, and this will make it easier for other future applications to use your code.

This idea is used within the. NET Framework Library itself, you will find if you look at ADO. NET in the System.Data namespace using a tool such as Reflector, that there is a class called the IDataAdapter, and that IDataAdapter has a number of derived types, including a concrete class called DataAdapter, also you’ll find the DbDataAdapter, Odbc, OleDb, and SqlDataAdapters. Each of these implements at its core the IDataAdapter interface.

Where To Apply Adapter Pattern?

You should consider using the Adapter Pattern whenever you want to use an existing class’s functionality, but its interface is not the one that you require.

Another scenario, if you’re trying to create reusable code, and you don’t want to tie it too tightly to a particular implementation, you should use some kind of an Adapter interface as what you’re code depends on, so that future clients could implement their own version of that Adapter and still make use of your code.

You’ll also find the Adapter Pattern useful if there are several existing implementations of code that you want to be able to use, and it’s impractical to adapt each of their interfaces by sub-classing everyone. By implementing the Adapter Pattern, and just writing an Adapter for each of these subclasses, for instance, maybe you have a Data Access class, one for SQL Server, another for Oracle, another for DB2, you would be able to create a Data Adapter, and then create just the Adapter classes for each of those different implementations rather than trying to change those implementations directly to expose the interface that you require.

Summary

The Adapter Pattern is used to wrap a needed class with one that implements a required interface.

Also,

If you write your Client classes so that they depend on Adapters, it allows you to future-proof these classes so they can be made to work with future versions of code that does not use interfaces that you foresaw when you wrote your Client Library.

You should remember the Open/Closed Principle, which states that modules should be open to extension, but closed to modification, and by utilizing the Adapter Pattern in your implementations of your code, you allow for your code to better follow the Open/Closed Principle.

References


comments powered by Disqus