Java – Client/Server design pattern

client-serverdesign-patternsjava

I am new in programming and I want to code a simple Client Server System where the Client sends commands like "SAY house" and the Server detects the keyword "SAY" as an command and returns whatever is after the keyword (in this case "house"). There are many examples how to do this but what I also want to learn with this project is a good design pattern for problems like this. I dont want to implement everything in only 2 classes like Server.java and Client.java.

So I started with a Server class who starts the server on a port and waits for clients. When he get a Request from a client, the Server class delegates the input to a class "RequestHandler". This class does the switch-case part to detect a command like "Say" or "SHUTDOWN" and calls over an interface the corresponding method.

Is this a good pattern? The problem is for example the service "SHUTDOWN". I have to take the Socket Object all along with me to the method who executes the command. Or maybe this pattern isnt good for upgrading the Server with more Functions? Help please for improvements!

Thanks


public class Server {
private int port;
private ServerSocket server;

public Server(int port) throws Exception{
    this.port = port;
    try{
          server = new ServerSocket(port);      
     }catch(Exception e){
          System.out.println("Server kann nicht gestartet werden: "+e.getMessage());
          throw e;
     }
}

public Server(int port){
    this.port = port;
}

public void start(){
    Socket client;
    Thread thread;

    while (true) {
        try {
            client = server.accept();
            // Verbindung eingegangen, Objekt erzeugen und in Thread laufen lassen
            System.out.println("Verbindung von "+client.getInetAddress());
            CommandHandler handler = new CommandHandlerImpl(client,new ControllerImpl());
            thread = new Thread(handler);
            thread.start();
        }
        catch (Exception e) {
          System.out.println("Verbindungsfehler: "+e);
        }
      }
}

}

public class CommandHandlerImpl implements CommandHandler{

private final String regex = " ";
private IController controller;
private Socket client;

public CommandHandlerImpl(Socket client, IController controller){
    this.client = client;
    this.controller = controller;
}

@Override
public String getCommand(String abstractString) throws Exception {
    String result;
    String[] arr = abstractString.split(regex);
    String command = arr[0];

    String s = "";
    if(arr.length > 1){
        s = arr[1];
    }
    switch (command) {
    case "CAPITALIZE":
        result = controller.capitalize(s);
        break;
    case "BYE":
        result = controller.sayBye();
        break;
    case "SHUTDOWN":
        result = controller.shutdown(s);
        break;
    default:
        result="Command nicht gefunden!!!";
    }
    return result;
}

@Override
public void run() {
    String line, res;
    try {
        PrintWriter out = new PrintWriter(client.getOutputStream());
        BufferedReader in = new BufferedReader(new InputStreamReader(client.getInputStream()));

        while (true) {
            line = in.readLine();
            System.out.println(line);
            res = null;
            if (line != null){
                res = getCommand(line);
            }
            if (res == null){
                break;
            }
            out.println(res);
            out.flush();
        }
          System.out.println("Verbindung von "+client.getInetAddress()+" beenden.");
          client.close();
    } catch (IOException e) {
        e.printStackTrace();
    }catch(Exception e){
        e.printStackTrace();
    }
}

}

IController is an interface doing something with the String and returns a String so I can send it back to the client.
Is this good or how should it be for a good design?

Best Answer

This class does the switch-case part to detect a command like "Say" or "SHUTDOWN" and calls over an interface the corresponding method.

Is this a good pattern?

Generally switch cases should not be used unless all the cases are finalized before-hand. If we know there are extension, as in your case, we should not use switch-cases at all.

For your case you can use either Command or Strategy as your need. If your set of operations are pretty much similar in logic, go with Strategy, otherwise Command.

As I read you question, I think Command is more suited for you.

I tried to outline the solution with Command pattern implementation, but it was somewhat hard to understand some of your code fragments. Generally you should be using comments whenever your code is not pretty much reflective for an outsider who reads it. I think you can understand with the source I have mentioned.

Additionally if you want to map a String from client to a desired Command object, use a Java Map. Don't use Switch-cases or if-else ladders which definitely makes it messy. Please raise questions if any. :))