6

I have a situation where I have an external library. In short, I need to be able to adapt my type to theirs, and theirs to mine. The library has a collection like so:

interface IExternalCollection
{
    void Add(IExternal item);
    IExternal Get(int index);
}

Now, let me start off by explaining the most straightforward solution. I have a regular adapter for this collection, and regular adapters for IExternal and IInternal:

interface IInternalCollection
{
    void Add(IInternal item);
    IInternal Get(int index);
}

class InternalCollectionAdapter : IInternalCollection
{
    private IExternalCollection collection;

    public InternalCollectionAdapter(IExternalCollection collection)
    {
        this.collection = collection;
    }

    public void Add(IInternal item)
    {
        collection.Add(new ExternalAdapter(item));
    }

    public IInternal Get(int index)
    {
        return new InternalAdapter(collection.Get(index));
    }
}

This solution requires 2 adapters for the same type, and my ExternalAdapter adapts InternalAdapter (there is no internal concrete type). What's worse is, my internal interface only has 2 methods, but the external interface has many (15-ish). In my code, I only need those 2, which is why I designed my interface to only have those 2, but now I need to cluster my interface with all 15 in order to be able to create the ExternalAdapter.

So I figured, surely there is a better way. I read up on the 2WayAdapter, and it looks like this:

class TwoWayAdapter : External, IInternal
{
    public void Show()
    {
        base.Display();
    }     
}

This allow me to write a CollectionAdapter like this:

class InternalCollectionAdapter : IInternalCollection
{
    private IExternalCollection collection;

    public InternalCollectionAdapter(IExternalCollection collection)
    {
        this.collection = collection;
    }

    public void Add(IInternal item)
    {
        collection.Add((IExternal)item);
    }

    public IInternal Get(int index)
    {
        return (TwoWayAdapter)collection.Get(index);
    }
}

And call it like so:

collection.Add(new TwoWayAdapter()); 
collection.Get(0);

This works, and I only need 1 adapter for the type, and I can design its API as I please.

However, this external library creates its own objects of type External. So when I call Get on the external collection, it might return a pure newed up instance of External, making my cast to TwoWayAdapter fail.

My last solution is to allow access to the adaptee, like this:

class InternalAdapter : IInternal
{
    private IExternal adaptee;
    public Object Adaptee { get { return adaptee; } }

    public InternalAdapter(IExternal adaptee)
    {
        this.adaptee = adaptee;
    }

    public void Show()
    {
        adaptee.Display();
    }
}

This will change my CollectionAdapter to this:

class InternalCollectionAdapter : IInternalCollection
{
    private IExternalCollection collection;

    public InternalCollectionAdapter(IExternalCollection collection)
    {
        this.collection = collection;
    }

    public void Add(IInternal item)
    {
        collection.Add((IExternal)item.Adaptee);
    }

    public IInternal Get(int index)
    {
        return new InternalAdapter(collection.Get(index));
    }
}

As you can see, this requires my internal interface to have a Object Adaptee { get; } property, which smells really really bad.

I can only imagine that this situation has come up many times, but my efforts on the internet has been in vain.

I would like a solution where I can keep my API clean of all the external methods I don't need, and preferably one where the Adaptee stays hidden.

EDIT

After pondering a bit more on this issue, I've come up with a solution which seems to live up to my requirements, i.e clean API that doesn't include stuff I don't need and doesn't expose the Adaptee.

Is is a combination of my previous iterations, and goes as follews:

  • To adapt their type to mine, I use a regular adapter.

    class InternalAdapter : IInternal
    {
        private IExternal adaptee;
    
        public InternalAdapter(IExternal adaptee)
        {
            this.adaptee = adaptee;
        }
    
        public void Show()
        {
            adaptee.Display();
        }
    }
    
  • To adapt this type back to theirs I create a TwoWayAdapter.

    class TwoWayAdapter : External, IInternal
    {
        public void Show()
        {
            base.Display();
        }     
    }
    
  • Then my CollectorAdapter looks like this:

    class InternalCollectionAdapter : IInternalCollection
    {
        private IExternalCollection collection;
    
        public InternalCollectionAdapter(IExternalCollection collection)
        {
            this.collection = collection;
        }
    
        public void Add(IInternal item)
        {
            collection.Add((IExternal)item);
        }
    
        public IInternal Get(int index)
        {
            return new InternalAdapter(collection.Get(index));
        }
    }
    

Now you might notice that this solution has its problems of its own, namely, I cannot get one of their types, and then convert it to mine, convert it back, and add it to their collection.

This solution works in my case in this project only because get retrieves a new instance, and add is only used with a new instance (newed up by me), i.e the same object is not getted and added.

The reason I have made this an edit and not an answer is that, this doesn't solve the more general problem I propose in this question.

Just to clarify what I'm asking for: I would love if you know of any articles, blogs or books that solve this issue with a technique or pattern. I would also appreciate if someone could reference what they have done in previous situations.

11
  • 1
    Have you considered just using dynamic variables, and not bothering with all these interfaces? Jun 10, 2016 at 15:26
  • Can you make it clear as to why you need to adapt the types to one another? Maybe there is some other solution.
    – Snoop
    Jun 10, 2016 at 16:28
  • @RobertHarvey, Could you provide an example? Link or answer to how that would look in my example is both fine. Jun 10, 2016 at 18:23
  • @StevieV, Well, I need to adapt the external type so that my Library is not dependent upon it. I need to adapt my type to theirs because they need to do some work with it, and they obviously is independent of mine. Jun 10, 2016 at 18:25
  • 3
    Without some sort of workflow (i.e. what you actually do with your/their objects) it's hard to provide advice. Maybe your proposals are sound; maybe they're not. For instance, why do you need to adapt collections of items? Why not simply items (i.e. let your internal object have a constructor using an external thingy, and a method to convert to an external) ? To me what you're proposing sounds like you constantly switch from external to internal and vice versa. Is it the case? Aren't you overengineering this whole thing?
    – user44761
    Jun 13, 2016 at 8:27

1 Answer 1

0

I've recently become a huge fan of functional programming styles. Here's a demo of how I would start working on this, note I didn't compile or even put the code into visual studio. If you carry this idea forward I think you only need two more methods to go the other way! If I had more time I would try to take it to using Generics as well which would reduce the coding 50%.

public static class XAdapters{
   public static IExternalCollection AsExternalCollection(this InternalCollection intneralCollection) where Q:new(){
        var externalCollection = new ExternalCollection();
        inputCollection.ToList().ForEach(input=>{
           //create output item is an extenion method too
           output.Add(input.CreateNewOutputItem());
       }
       return externalCollection;
   }

  public static IExternalCollection Add(this IExternalCollection externalCollection, InternalCollectionItem item){

    var x =  item.CreateNewExternalItem();
    externalCollection.Add(x);
    return externalCollection;
   }

}

To use the code above:

var externalCollection = InternalCollection.AsExternalCollection.Add(someInternalItem);

Here's the factory converters:

public static extneralItem createNewExtneralItem(this intneralItem internal)          {
     var extern = new externalItem();
     extern.property = internal.property; //etc...
     return extern;
}
2
  • An interesting method of doing adapters. Looking at your factory, would external and internal need to have the same API, to do a mapping like that? Jun 20, 2016 at 6:26
  • The example above is more than "doing adapters" it's a functional approach which shows how extension methods "do things for you". In this case it's to adapt one thing to the other. They do not need to have the same API. Of course we are guessing a bit because we can't see the total relationships between the two item types. This is typically referred to as Mapping. Mapping tries to convert one object type to another. Jun 20, 2016 at 18:55

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.