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.