I'm try to design "Network Connection" for easily use to retrieve data from server. However, I facing a design problem.
My NetworkUtil
Class which I'm going to use was designed as
class NetworkUtil
public NetworkUtil(URL, resultType); // resultType might be XML or RAW
public setListener(listener); // listener will notice when result has arrive
public addPostData(key, value);
public connect(); // connect start new thread, so result will shown in listener
interface NetworkUtilListener1
public onNetworkFail();
public onNetworkSuccess(URL, resultType, xml, raw);
interface NetworkUtilListener2
public onNetworkFail();
public onNetworkSuccessRAW(URL, resultType, raw);
public onNetworkSuccessXML(URL, resultType, xml);
Once result arrive, I will check resultType and use the result from that parameter. However in 2 example shown above (NetworkUtilListener1
and NetworkUtilListener2
), I consider the problem for future use when more resultType
is coming such as JSON, image or even my custom type, so my team can easily use it.
NetworkUtilListener1
will have long unused parameter like
onNetworkSuccess(URL, resultType, raw, xml, json, image);
which isn't a good design as I thought.
NetworkUtilListener2
will force concrete class who use it to have a lot of empty method since most of the time we prefer only 1 or 2 type of result on each project.
onNetworkSuccessRAW(URL, resultType, raw);
onNetworkSuccessXML(URL, resultType, xml);
onNetworkSuccessJSON(URL, resultType, json);
onNetworkSuccessImage(URL, resultType, image);
Anyone could give me some help on redesign this class structure or recommend me the design pattern I need to focus on. so I can have better NetworkListener
.
This sounds like a solution for the classical visitor pattern:
Let's start by overloading the NetworkListener methods:
interface NetworkListener {
void onSuccess(XMLResult xml);
void onSuccess(JSONResult json);
}
Then, as suggested, let's have several implementation of results. Everyone of them capable of notifying a listener by invoking the appropriate overloaded method.
interface Result {
void notify(NetworkListener listener);
}
class XMLResult implements Result {
@Override
public void notify(NetworkListener listener) {
listener.onSuccess(this);
}
}
class JSONResult implements Result {
@Override
public void notify(NetworkListener listener) {
listener.onSuccess(this);
}
}
Now, let's see how a sample network listener implementation would look like:
class SampleListner implements NetworkListener{
@Override
public void onSuccess(XMLResult xml) {
// handle here
}
@Override
public void onSuccess(JSONResult json) {
// handle here
}
}
And your notification code would look somewhat like:
Result result = null;
for(NetworkListener listener: listeners){
result.notify(listener);
}
Since the pattern relies heavily on method overloading you can add a catch-all method that receives a Result object, that will guarantee that if any new implementations of Result are created, you still get a default handling for them if you do not add further overloading methods.
Instead of receiving a type and the result, make use of polymorphism:
public interface Result { ... }
public class XmlResult implements Result { ... }
and in the future you could add as many as you need.
public class JSonResult implements Result { ... }
Finally, you will have this interface
interface NetworkUtilListener1
public onNetworkFail();
public onNetworkSuccess(URL, result);
I don't think NetworkUtil should worry about format type. Just let the NetworkListener register itself to NetworkUtil and have NetworkUtil notify the listener on success. Let the NetworkListener worry about type because they are the only handling the result. Should be something like
class NetworUtil{
registerListener( listener){ mylisteners.push( listener); }
notifyListener(){ for(listener: mylisteners){ listener.onSuccess( myresult ); }
}
class Listener1{
Listener1(){ registerWithNetworkUtil(); }
void onSuccess(myresult){
if(myresult.isXML){ parseXML(myresult); }
else if(myresult.isJSON()){ parseJSON(myresult); }
else if(myresult.isXXYYZZ()){ parseXXYYZZ(myresult); }
}
}
note you might want to have some default parsers handy so new listeners can utilize them.