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.
dynamic
variables, and not bothering with all these interfaces?