Follow treslines by email clicking Here!

Tuesday, August 11, 2015

Router Design Pattern - Route messages, Objects or whatever you want!

Hi there!

Today i'm gonna show you the router pattern i wrote myself in action. The Router design pattern is a very useful programming design pattern whenever you need to be able to send objects or messages between instances over the application.

The example i will provide is a nice way to show it how it could looks like. You can always come back here, take it, adapt it and use it in your applictions as you may need. So be sure you bookmark it or join the group here on the right side of this post subscribing it. If you use it anywhere, please come back and give me a feedback. It would be nice to have more real use cases.

First of all, let's take a look at the UML diagram of it. After that we will take the analogy for our example.

The UML Diagram of the Router Pattern


Pay close attention, because once you understand that, everything will become clear and simple to understand. That's the reason I'm putting always the UML first. That way you'll get an eye for it with the time.





The example

In our example we will see, how we could create clients that listen to server responses. The servers are lazy instantiated and are created on demand.

Response and Request Interfaces

Those interfaces defines a contract to be used to design which type the client will use to send requests and receive responses. Let's take a closer look to it:
public interface Response < T >  {
  T getResponse();
  void setResponse(T value);
}

public interface Request < T >  {
  T getRequest();
  void setRequest(T value);
}

Client and Server Interfaces

Those interfaces defines a contract to be used while implementing concrete clients and servers. It uses either the Response or the Request depending on the what you are implementing:
public interface Client {
   < T extends Response < ? >  >  void onServerResponse(T response);
}

public interface Server {
  < T extends Request < ? > > void onClientRequest(T request, Client client);
}

Routable interface and Router class

The routable defines the contract for the router. The router itself is designed as a singleton and can be accessed and used everywhere in the application sending and receiving messages or objects. In this implementation the servers are lazy implemented and created on demand. For sure you may adapt it to your needs. Feel free to do it and give me feedback of the usage of it in your applications.
public interface Routable {
  public  < T extends Client >  void registerClient(T clientImpl);
  public void registerServer(Class < ? extends Server >  serverImpl);
  public  < T extends Request < ? >  >  void routeClientToServer(Class < ? extends Client >  clientImpl, Class < ? extends Server >  serverImpl, T request);
  public void removeClient(Class < ? >  serverClass);
  public void removeAllClients();
  public void removeServer(Class < ? >  clientClass);
  public void removeAllServers();
  public boolean isRegistered(Class < ? >  clazz);
}


public class Router implements Routable {

  private Map < String, Client >  clients = new HashMap < String, Client > ();
  // using sets to avoid duplicates
  public Set < Class < ? extends Client >  >  clientSet = new HashSet < Class < ? extends Client >  > ();
  public Set < Class < ? extends Server >  >  serverSet = new HashSet < Class < ? extends Server >  > ();
  private static final Router ROUTER = new Router();

  private Router() {
    // singleton - can be accessed anywhere in the application
  }

  public static Router turnOn() {
    return ROUTER;
  }

  public  < T extends Request < ? >  >  void routeClientToServer(Class < ? extends Client >  clientImpl, Class < ? extends Server >  serverImpl, T request) {
    doNotAllowNullValue(clientImpl);
    doNotAllowNullValue(serverImpl);
    doNotAllowNullValue(request);
    doNotAllowUnregisteredNullValue(isRegistered(clientImpl));
    // just to ensure that the server implementation exits already
    doNotAllowUnregisteredNullValue(isRegistered(serverImpl));
    // as we now know that the server implementation exists,
    // we just create a lazy instance over reflection on demand
    try {
      serverImpl.newInstance().onClientRequest(request, clients.get(clientImpl.getName()));
    } catch (InstantiationException e) {
      // we shall never run into this situation, except if the user does NOT define
      // a default constructor in any of the concrete implementation of Server as per
      // convention.
      e.printStackTrace();
    } catch (IllegalAccessException e) {
      e.printStackTrace();
    }
  }

  public void removeServer(Class < ? >  serverClass) {
    serverSet.remove(serverClass);
  }

  public void removeAllServers() {
    serverSet.clear();
  }

  public void removeClient(Class < ? >  clientclass) {
    clients.remove(clientclass.getName());
    clientSet.remove(clientclass);
  }

  public void removeAllClients() {
    clients.clear();
  }

  public boolean isRegistered(Class < ? >  clazz) {
    boolean result = false;
    boolean searchBreak = false;
    Iterator < Class < ? extends Client >  >  iterator = clientSet.iterator();
    while (iterator.hasNext()) {
      Class < ? extends Client >  next = iterator.next();
      // note: we can't use equalsIgnoreCase here
      if (next.getName().equals(clazz.getName())) {
        result = true;
        searchBreak = true;
        break;
      }
    }
    if (!searchBreak) {
      Iterator < Class < ? extends Server >  >  it = serverSet.iterator();
      while (it.hasNext()) {
        Class < ? extends Server >  next = it.next();
        // note: we can't use equalsIgnoreCase here
        if (next.getName().equals(clazz.getName())) {
          result = true;
          searchBreak = true;
          break;
        }
      }
    }
    return result;
  }

  public  < T extends Client >  void registerClient(T clientImpl) {
    doNotAllowNullValue(clientImpl);
    clientSet.add((Class < ? extends Client > ) clientImpl.getClass());
    clients.put(clientImpl.getClass().getName(), clientImpl);
  }

  public void registerServer(Class < ? extends Server >  serverImpl) {
    doNotAllowNullValue(serverImpl);
    serverSet.add(serverImpl);
  }

  private void doNotAllowNullValue(Object toCheck) {
    if (toCheck == null) {
      final String msg = "You can't pass null to this method!";
      throw new NullPointerException(msg);
    }
  }

  private void doNotAllowUnregisteredNullValue(boolean isRegistered) {
    if (!isRegistered) {
      final String msg = "Either the client or the server was not registered in this router. Register it first!";
      throw new IllegalArgumentException(msg);
    }
  }
}

Sample Implementation and Test

Now let's see how a real implementation could looks like and how it works in practise. First of all we are gonna define some responses and requests. Then we will create the clients and servers. Finally we will test it, by running a junit test to show it in action.
//SAMPLE CLIENT RESPONSE
public class ClientResponse implements Response < String >  {
  private String response;
  public String getResponse() {return response;}
  public void setResponse(String value) {response = value;}
}

//SAMPLE SERVER REQUEST
public class ServerRequest implements Request < String >  {
  String request;
  public String getRequest() {return request;}
  public void setRequest(String value) {request = value;}
}

// SAMPLE CLIENT IMPL
public class ClientImpl implements Client {
  public  < T extends Response < ? > > void onServerResponse(T response) {
    System.out.println(response.getResponse());
  }
}

//SAMPLE SERVER IMPL
public class ServerImpl implements Server {
  public < T extends Request < ? > >  void onClientRequest(T request, Client client) {
    // handle request and depending on it create response
    ClientResponse clientResponse = new ClientResponse();
    clientResponse.setResponse("Server is sending a response to client...");
    // route response back to client immediately or whenever you want
    client.onServerResponse(clientResponse);
  }
}

public class RouterTest {
  @Test
  public void testRouter() {
    Router.turnOn().registerClient(new ClientImpl());
    // servers would be only referenced and lazy instantiated later
    Router.turnOn().registerServer(ServerImpl.class);
    System.out.println("Client is sending a request to server...");
    ServerRequest request = new ServerRequest();
    request.setRequest("Client is sending a request to server...");
    Router.turnOn().routeClientToServer(ClientImpl.class, ServerImpl.class, request);
  }
}


That's all! hope you like it! :)

2 comments:

  1. Note that you can also split both isRegistered, for something like isRegistered, isClientRegistered as private and isServerRegistered as private, and routeClientToServer, routeClientToServer and canRouteClientToServer as private, to preserve the SoC...
    It would be more clean and easier to understand the code

    ReplyDelete
    Replies
    1. Hi Luis! nice comment! Thanks! That's true! for sure we could do that. While publishing blog post i tend to reduce the code as much as i can. In fact my code repo has a lot of javadoc and a bunch of such utility methods. Publishing all these comments, however, would worsen the readability of the code and could harm the teaching purpose.

      My main goal here is to disseminate the content and know-how. In the case of isRegister for example i had two methods first, but while using it in the practise, people found much easier to use and understand, if they had only one method to choose for both. (which makes sense for me now) i agree with you that we could split it internally and i think i will do that. The method canRouteClientToServer is also a nice improvement and i think i will put it in the contract of the Routable. Thanks for the feedbacks.

      Delete