You don't really need a pattern yet. Just implement the service in a clean fashion, so that somewhere there is an Image FlikrApi.GetImage(Url)
method.
// client code
Image image = flickApi.GetImage(url);
When you come to implement your second service, then you will have some requirements as to how to decide which function to call based on the Url. You can then decide how to do it - for two services it might be as simple as switching on the top-level domain name. So you have a switch which calls one or another method on one or another object.
readonly FlickrApi flikrApi = new FlickrApi();
readonly WhateverApi whateverApi = new WhateverApi(); // third party
Image GetImage (Url uri) {
switch (url.TopLevelDomain()) {
case "flickr.com":
return flikrApi.GetImage(url);
break;
case "whatever.com":
return whateverApi.GetWhateverImage(url);
break;
default:
throw new UnhandledUriException(uri);
}
// client code
Image image = GetImage(uri);
Learn to count - one, two, many. When you hit many, then think about refactoring to a pattern. It might be that you don't get to implement more than two services, as you're working on a framework for them, rather than doing something useful.
If you do have a requirement for more dynamic services, and can select based on the top-level domain only, then I'd probably have a map - a Dictionary<string, Func<Url,Image>>
populated with the tlds and service delegates.
readonly Dictionary<string, Func<Url,Image>> apis = new ...;
ImageApi () {
apis["flickr.com"] = new FlickrApi().GetImage;
apis["whatever.com"] = new WhateverApi().GetWhateverImage;
apis["zzze.com"] = (uri) => Zoobers.GetWhateverImage(new ZooberCreds(), uri.ToString());
}
static Image GetImage (Url uri) {
string tld = urli.TopLevelDomain();
if (!imageApis.ContainsKey(tld)) throw new UnhandledUriException(uri);
return imageApis[tld](uri);
}
// client code unchanged
In a language without closures/delegates you'd define an interface and use those, but C# is better than that, and using the built-in function type lets you use any suitable function rather than having to create a class just to conform to an interface. It's not quite the strategy pattern as there is no structural relationship between a context and a strategy - in the strategy pattern, there is a context which has-a strategy, one strategy only, and that strategy can change. Here we're selecting between strategies based on a simple condition.
If you have more complicated requirements on deciding which service to use, then you might end up iterating over a list of interfaces to IImageApi
, where the interface includes a bool HandlesUrl(Url)
method to ask the service whether it recognises the Url. In that case, instead of using delegates to talk to any third-party code, you would have to use a wrapper.
interface IImageApi {
bool HandlesUri(Url);
Image GetImage(Url);
}
readonly List<IImageApi> apis = new ...;
ImageApi () {
apis.Add(new FlickrApi()); // you wrote this, so it can implement the interface
apis.Add(new WhateverApiAdapter()); // third party requires adapter
apis.Add(new ZoobersApiAdapter()); // ditto
// or you can use something like MEF to populate the list
}
static Image GetImage (Url uri) {
foreach (var api in apis)
if (api.HandlesUri(uri))
return api.GetImage(uri);
throw new UnhandledUriException(uri);
}
// client code unchanged
Do the simplest thing first, the second-most simplest thing second and the most complicated thing if you have to.