Question

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.

Was it helpful?

Solution

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.

OTHER TIPS

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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top