Итак, у вас есть блок big-i sh switch
, который в итоге start()
s представляет собой некоторый фрагмент кода. Рекомендованным способом является использование существующих интерфейсов, так что это будет Runnable
(содержащий метод void
без параметров, как ваш start()
).
Если вы реорганизуете весь блок в метод, он будет иметь два входа: Socket
и requestName
- поэтому его подпись будет выглядеть так:
Runnable getRequestCommand(Socket s, String request)
, который будет содержать ваш блок switch
и будет возвращать что-то вроде
if ("ClientRequestUploadFile".equals(request)) {
return new ClientRequestUploadFileHandler(s);
}
// etc
Снова используя ранее существующие интерфейсы, это BiFunction<Socket, String, Runnable>
(требует ввода строки запроса и сокета и возвращает работоспособный обработчик).
Теперь вы можете разделить каждого отдельного пользователя case
и создайте такую функцию:
BiFunction<Socket, String, Runnable> upload = (s, req) -> {
return "ClientRequestUploadFile".equals(req)
? new ClientRequestUploadFileHandler(s)
: null;
}
Если вы сделаете то же самое для других случаев и сохраните их в Collection<BiFunction<Socket, String, Runnable>>
(давайте назовем это handlers
), ваш getRequestCommand()
метод выглядит выше как
Runnable requestHandler = null;
for (BiFunction<Socket, String, Runnable> handler : handlers) {
requestHandler = handler.apply(s, request);
if (requestHandler != null) { break; } // found the match
}
return requestHandler;
Теперь ваш switch
фактически также запускает созданный Runnable
, так что вы также можете if (requestHandler != null) { requestHandler.run(); }
здесь и не возвращать его вызывающей стороне. Как одна строка, это handlers.stream().map(h -> h.apply(s, request)).findFirst(Objects::nonNull).ifPresent(Runnable::run)
.
В любом случае, теперь вы застряли в создании всех BiFunction<>
в исходном классе, но вы можете их экстернализовать, например. на enum
.
enum RequestHandler {
FILE_UPLOAD("ClientRequestUploadFile", ClientRequestUploadFileHandler::new),
CALCULATE("clientRequestCalculator", ClientRequestCalculatorHandler::new),
// ...
;
// the String that needs to match to execute this handler
private String request;
// creates the runnable if the request string matches
private Function<Socket, Runnable> createRunnable;
private RequestHandler(String r, Function<Socket, Runnable> f) {
request = r; createRunnable = f;
}
// and this is your handler method
static void runOnRequestMatch(Socket socket, String request) {
for (RequestHandler handler : values()) {
Runnable requestHandler = request.equals(handler.request)
? handler.createRunnable.apply(socket)
: null;
if (requestHandler != null) {
requestHandler.run();
break;
}
}
}
}
И в вашем клиентском коде вы получите
// ...
Socket s= serverSocket.accept();
DataInputStream clientStream= new DataInputStream(s.getInputStream());
String requestName=clientStream.readUTF();
RequestHandler.runOnRequestMatch(s, requestName);
Теперь вы получили гораздо больше кода, чем раньше, но обработка сам удаляется из класса, принимающего сокет, поэтому лучше единоличной ответственности; это позволяет добавить функциональность, добавив значение к enum
, не затрагивая исходный код.
Более простой версией было бы создание коллекции функций в методе простым выполнением
* 1048. *