views:

40

answers:

2

I am creating a client side swing app that will have data provided by/from one of many data providers(brokers). The data providers however, have varying ways of perfoming same things e.g.

broker1's login method

public boolean doLogin(String username, String password);

broker2's login method

public int login(String username, String password,String sessionId);

For all providers the set of required actions is the same e.g

login, getstatus, sendRequest, getData, logOff
(but they have different params and return types)

I took a look at the adapter pattern but am unfortunately not able to use it well as the required methods have different parameters.

Is the adapter pattern usable in this case? if so how?
If not what would be the best way of doing this?

Thanks.

A: 

My first thought was looking into the facade pattern, which, in my 'Head First Design Patterns' book, is explained in the same chapter as Adapter and compared with a remoted control for home theatre components.

This facade would sit between the client app and the various brokers. So the client wouldn't have to care, which and how many brokers are part of the 'theatre', it just have to 'press the login button' and 'all broker connections are switched on'.

Andreas_D
I believe the OP is already using some kind of façade in his application, and I'm not sure he wants to use all the brokers at once. His problem is more that the different brokers (probably third parties) have different APIs with different method arguments, and he wants to unify all this in a single public API, regardless of the third party used. The "Facade pattern" does not answer how the inner objects are manipulated, but just how they are integrated inside a single concise API, which is not the OP's concern. IMHO
Yanick Rochon
+1  A: 

Patterns are general guidelines (starting point) of best practices. Many developers "adapts" the patterns to their needs; the important thing is, then, if you must use a pattern, use it consistently throughout your whole application.

Now, to answer your question; yes the adapter pattern can very well be used in your situation. A possible solution (in the like) could be:

abstract class BrokerAbstract<T> {
   private int errCode;
   private String errMessage;

   abstract public boolean login(String user, String pass, Map<String,Object> options);
   abstract public int getStatus(Map<String,Object> options);
   abstract public boolean sendRequest(Map<String,Object> options);
   abstract public T getData(Map<String,Object> options);
   abstract public boolean logOff(Map<String,Object> options);

   protected void setError(int code, String message) {
      this.errCode = code;
      this.errMessage = message;
   }

   public int getErrorCode() { return this.errCode; }
   public String getErrorMessage() { return this.errMessage; }
}

Then

class Broker1 extends BrokerAbstract<Object> {
   private OriginalBroker1 original;       

   public boolean login(String user, String pass, Map<String,Object> options) {
      return original.doLogin(user, pass);  // ignore options
   }
   public boolean login(String user, String pass) {
      return login(user, pass, null);  // third parameters will be ignored
   }
   public int getStatus(Map<String,Object> options) { /*...*/ return 0; }
   public boolean sendRequest(Map<String,Object> options) { /*...*/ return false; }
   public Object getData(Map<String,Object> options) { 
      return original.getData();  // OriginalBroker1.getData():Object
   }
   public boolean logOff(Map<String,Object> options) {
      return original.doLogout((boolean) options.get("clearSession"));
   }
   public boolean logoff() {
      HashMap<String,Object> options = new HashMap<String,Object>();
      options.put("clearSession", true);
      return logoff(options);   // proxy to original method
   }
}

Or

class Broker2 extends BrokerAbstract<Integer> {
   private OriginalBroker2 original;       

   public boolean login(String user, String pass, Map<String,Object> options) {
      int code = original.doLogin(user, pass, (String) options.get("sessionId"));
      if (0 != code) {
          setError(code, "Custom error message"); // could use enum here for messages...
          return false;
      } else {
          return true;
      }
   }
   public boolean login(String user, String pass, String sessionId) {
      HashMap<String,Object> options = new HashMap<String,Object>();
      options.put("sessionId", sessionId);
      return login(user, pass, options);
   }
   public int getStatus(Map<String,Object> options) { /*...*/ return 0; }
   public boolean sendRequest(Map<String,Object> options) { /*...*/ return true; }
   public Integer getData(Map<String,Object> options) { 
      return original.getData(options.get("key"));  // OriginalBroker2.getData(key:String):int
   }
   public boolean logOff(Map<String,Object> options) {
      return original.doLogout();
   }
   public boolean logoff() {
      return logoff(null);   // ignore third parameter
   }
}

Of course this is a very general approach. If you know that one method will be receiving strings for all parameters, you could also have a abstract signature like :

abstract public boolean login(String...args);

Then your concrete implementation would be :

abstract class A {
   abstract public boolean login(String...args);    
}
class B extends A {
   public boolean login(String...args) { return this.login(args[0], args[1]); }
   public boolean login(String user, String pass) { return original.login(user,pass); }
}

class C {
   public void login() {
      B b = new B();
      b.login("foo", "secret");
      // or
      b.login(new String[] {"foo", "secret"});
      // or !
      b.login("foo", "secret", "sessionId");  // will ignore third, but otherwise would still work...
   }
}

etc.

Yanick Rochon