Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#

Structural Design Pattern (Part 3)

5.00/5 (1 vote)
13 Oct 2011LGPL34 min read 11.9K  
Structural Design Pattern (Part 3)

This is in continuation to the previous post. Please note that this post will be a long one, so if you have time, keep reading.

Let me start off with the first pattern I picked up to talk about in Structural patterns category.

Adapter Pattern

What

It states that “Translating one interface of a component or a class to a compatible interface”, it may not look simple as you read it. What it means is that many times in the real world, what happens is one component won't fit into another component appropriately. We often tend to overlook the compatibility issues and thus encounter problems while fitting them together to make it work.

If they are newly designed components, then it’s not a major problem for us to worry, because both of them will be designed as perfectly as specified to get coupled, but if one of the components is existing or generic and the other is already designed or modification needed, then it's a problem for us.

Why

As said above, there are many places which we see in our real life examples wherein many components won't fit into the existing components at all.

Assume there is a legacy driver component which your project would be interested in using. But its interface won't match with yours, and neither can you change in your project to make it work due to design issues or cost of change is huge/complex.

Let's assume the workaround way wherein we either did not know about this pattern or did not want to use it. What would happen is, to fit component A and B together at position Y, due to incompatibility issues you either would change component A or B and make them fit together, but did not realize that either of these components is already broken due to this change or you have violated the design which it was built on.

Next to fit B and C components at X position again due to the same reasons, you would change B (assumed) and fit them all together, but if this chaining continues a lot more like this, think about the complexity you have added to the software module. After a while, the whole changes made look so ugly to yourself or to the other developers looking at these components, also it's very difficult to understand because what your design says is quite different from what your implementation says. And to fix a bug or add feature to a component gets quite tedious. Overall the complexity in maintaining this code part gets really complex in the long run.

How

As stated above, to make 2 components to co-work together perfectly, we are in need of an interface which is compatible with both of the components.

So let's look at the code I wrote which simulates this pattern usage, of course it’s very much simplistic in nature.

C#
public interface IOldTarget
    {
        void Add(float a, float b);
    }
    public class OldTarget : IOldTarget
    {
        public void Add(float a, float b)
        {
            Console.WriteLine("Adding 2 float values " + (a + b));
            //more code….
        }
    }

As you can see from the above code, I hereby have an Old target component which I am supposed to work with. As said, I also have a new target component which is being developed but its interface won't fit with the Old target, let's look at it:

C#
public interface INewTarget
    {
        void Add(int a, int b);
    }
    public class NewTarget : INewTarget
    {
        public void Add(int a, int b)
        {
            Console.WriteLine("Adding integet values "+(a + b));
            //More code goes here..
        }
    }

So now it's pretty clear to you that the old and new targets won't directly fit at all. So what we need to do to solve this incompatibility is to bring in Mr. Adapter. So let's invite him and see what good he can do:

C#
public class TargetAdapter : IOldTarget
    {
        INewTarget newTarget;
        public TargetAdapter(INewTarget target)
        {
            this.newTarget = target;
        }
        public void Add(float a, float b)
        {
            newTarget.Add((int)a, (int)b);
        }
    }

So what Mr. Adapter is doing here is, he is acting as a compatible interface between those 2 components. Now all of these components can work happily together in system. Let's see the usage code part of this adapter guy:

C#
static void Main(string[] args)
        {
            int a = 5 , b = 5;
            float c = 5.2f, d = 5.2f;
            INewTarget newTarget = new NewTarget();
            IOldTarget oldTarget = new OldTarget();
            newTarget.Add(a, b);
               oldTarget.Add(c, d);
            oldTarget = new TargetAdapter(newTarget);
            oldTarget.Add(c, d);
               Console.ReadLine();
        }

The advantage here is, if by some reason one of the component interface gets changed or added, then the other one need not worry much, just the interfacing guy has to be aware of it. So the overall cost of change in the system is very much minimized by using this pattern.

Where

Let's look at a couple of real world examples where we do come across such pattern usage:

  • A micro SD card surely won't fit into the laptops SD card slot. Instead of changing either of those components, we make use of some adapter component as shown in the above picture.
  • There are many electrical components whose pins are in different shapes, but there are common slots in the walls where you can’t plug any shaped pins. So we make use of adapter plugs there too as shown in the picture.
  • To fit 2 different sized water pipes together, we make use of Adapter component.

Well, that’s all I can talk about this pattern. I am sorry if it’s too lengthy, but I wanted to make every point clear.

Look out for my next post in continuation to this.

Thanks.

P.S.: Your valuable comments, votes are well appreciated.

Filed under: C#, CodeProject, dotnet
Tagged: .NET, blog, C#, codeproject, dotnet, tips

License

This article, along with any associated source code and files, is licensed under The GNU Lesser General Public License (LGPLv3)