Reputation: 3736
I have a class ABC that can do operation a(), b() and c() (operation == method), which are all closely related. I also have simimar classes DE and FGH. In the future, it is possible new classes are added with additional operations. Classes never share operations (for example, there is no class ADF)
My program should be able to perform all these operations, and the timing is "kind of random", meaning I have no estimate beforehand of which operation will be executed when/how many times.
I tackled this program by creating a wrapper class "OperationInvoker". My program can only access this invoker, and it has one method for each operation. The advantage of this is encapsulation: if new operations are created or existing one or edited, onlt the wrapper class needs editing.
My question is whether a "huge" wrapper is the best pattern to solve this?
I looked into the observer pattern for this by I think this is not a suitable case because the classes ABC, DE, FGH ... never have any mutual interests. Like, if operation A needs to be executed, all registered classes will receive event A and only 1 class (namely, ABC) will be actually interested in this event. all other registered classes will discard the event. This seems like overkill/overhead with all the registering and observing.
The delegation pattern also suffers the same problem: i don't have many classes doing the same operation in a different way, so there is no point in having a flexible delegation system.
Are there other, better patterns, or is this just a simple case of having a delegating wrapper class?
EDIT: someone asked what exactly ABC does. Well, I have a WSDL file that describes SOAP web services. I use a tool to convert this file to java classes. This means I cannot control the content of these classes as they are auto generated. So I wrote a class WebServiceInvoker which acts as a wrapper around these web services. This wrapper class has 3 methods: do web service A, do web service B, and do web service C.
As the program expands, more and more wsdl files enter the picture. Of course none of the web services do the same thing. The only reason why they are not all bundled together in one big wsdl file is because some web services just don't belong logically next to other web services. So I wind up with the following classes:
It is possible in the future more wsdl's (and thus wrapper classes) appear. The reason why is there an additional wrapper around all the other wrappers is because every service needs to be called with a "session id". So every wrapper needs to know this information. So the superwrapper holds this information, and can call all web services. This also works very well as an api: the program who uses the web services knows nothing of the services, it just has 1 public method available for each web service through the superwrapper. The program of course is aware of what each web service returns (a list, a string, nothing...).
Upvotes: 4
Views: 1130
Reputation: 65821
It looks like you have a definite architecture on one side that you are trying not to leak out into other components. The natural method of avoiding leaks like this is to add one more degree of separation.
Essentially, you can still keep one huge wrapper if you like but you need to consider what you wish to expose and only expose that. It looks like all you want to expose is a list of known operations so how about something like:
// Your setup - trimmed down.
interface ABC {
public void a();
public void b();
public void c();
}
interface DE {
public void d();
public void e();
}
interface FGH {
public void f();
public void g();
public void h();
}
// Your current wrapper.
class AllOps {
public ABC getABC() {
return null;
}
public DE getDE() {
return null;
}
public FGH getFGH() {
return null;
}
}
// One further degree of separation.
enum Op {
A {
@Override
void op() {
ops.getABC().a();
}
},
B {
@Override
void op() {
ops.getABC().b();
}
},
C {
@Override
void op() {
ops.getABC().c();
}
},
D {
@Override
void op() {
ops.getDE().d();
}
},
E {
@Override
void op() {
ops.getDE().e();
}
},
F {
@Override
void op() {
ops.getFGH().f();
}
},
G {
@Override
void op() {
ops.getFGH().g();
}
},
H {
@Override
void op() {
ops.getFGH().h();
}
};
// Hide the huge wrapper.
private static AllOps ops = new AllOps();
// Only expose the operations.
abstract void op();
}
public void test() {
Op.A.op();
Op.F.op();
}
Not sure what pattern this would be but if you only expose the enum I think you have achieved your disconnect.
Additional operations can be added as new enums that implement op
in any way you like. They may even not use the huge wrapper.
Upvotes: 1
Reputation: 2408
To allow for easier code maintenance, I suggest altering the wrapper to something like this:
public class Operations {
private ABC abcInst;
private DE deInst;
private FGH fghInst;
protected Operations(ABC abc, DE de, FGH fgh) {
this.abcInst = abc;
this.deInst = de;
this.fghInst = fgh;
}
public ABC getABC() { return abcInst; }
public DE getDE() { return deInst; }
public FGH getFGH() { return fghInst; }
}
This way there is no mainenance needed here (unless a new class is added) and all calls from a caller will only need the additional .getABC().a()
instead of .a()
etc. wich emphasizes on the fact that the operations are performed by different classes but still allows to access all functionality by passing around the Operations
-instance.
Upvotes: 1