Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/design-patterns/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 如何避免switch语句?_Java_Design Patterns - Fatal编程技术网

Java 如何避免switch语句?

Java 如何避免switch语句?,java,design-patterns,Java,Design Patterns,我正在努力学习java中的客户机/服务器 到现在为止,我已经掌握了基本知识。 这里介绍如何接受和服务众多客户 public class Server { ServerSocket serverSocket; public void startServer() throws IOException { serverSocket= new ServerSocket(2000); while (true){ Socket s=

我正在努力学习java中的客户机/服务器 到现在为止,我已经掌握了基本知识。 这里介绍如何接受和服务众多客户

public class Server {
    ServerSocket serverSocket;

    public void startServer() throws IOException {
        serverSocket= new ServerSocket(2000);
        while (true){
            Socket s= serverSocket.accept();
            new ClientRequestUploadFile(s).start(); //here is the first option.
        }
    }
}

现在假设我有太多类型的选项,客户端可以请求。 代码如下:

public void startServer() throws IOException {
        serverSocket= new ServerSocket(2000);
        while (true){
            Socket s= serverSocket.accept();
            DataInputStream clientStream= new DataInputStream(s.getInputStream());
            String requestName=clientStream.readUTF();
            switch (requestName){
                case "ClientRequestUploadFile": new ClientRequestUploadFileHandler(s).start();break;
                case "clientRequestCalculator": new clientRequestCalculatorHandler(s).start();break;
                case "clientRequestDownloadFile": new clientRequestDownloadFileHandler(s).start();break;
            }
        }
    }

如果有100个选项,有没有办法避免切换语句(可能是设计模式)?
请记住,将来可能会出现新选项。

这似乎是一个类似命令模式的示例

基本上,您需要一种将给定命令(在本例中为
字符串
)映射到执行适当行为的方法

最简单的方法是创建如下命令界面:

interface Command {
    void execute();
}
然后,您可以创建一个
映射
,它保存您的命令,并将每个传入的
字符串
映射到某个帮助器类中,该类实现
命令
,并在看到该命令时执行您希望执行的操作。然后你会使用它,比如:

commandMap.get(requestName).execute();
然而,这将需要在程序启动时进行一些动态设置,以使用命令字符串和命令对象构建映射。这是一种非常动态的设置映射的方法,这可能是一件好事,也可能是坏事,这取决于命令集更改的频率

如果您的命令更为静态,那么设置它的一种更为优雅的方法是使用
enum
来定义各种命令及其行为。下面是一个相当简单和通用的示例,说明了如何做到这一点:

public class CommandPatternExample {
    public static void main(String[] args) throws Exception {
        CommandEnum.valueOf("A").execute(); // run command "A"
        CommandEnum.valueOf("B").execute(); // run command "B"
        CommandEnum.valueOf("C").execute(); // IllegalArgumentException
    }

    interface Command {
        void execute();
    }

    enum CommandEnum implements Command {
        A {
            @Override
            public void execute() {
                System.out.println("Running command A");
            }
        },
        B {
            @Override
            public void execute() {
                System.out.println("Running command B");
            }
        };
    }
}

正如注释中所指出的,在代码中的某个地方没有办法避免使用命令到辅助对象的映射。最重要的是不要将它放在您的业务逻辑中,这会使方法很难阅读,而是放在它自己的类中。

因此,您有一个很大的ish
开关
块,它最终会
start()
生成一些代码。推荐的方法是使用现有的接口,这样就可以是
可运行的
(包含一个
void
方法,没有参数,就像您的
start()

如果您将整个块重构为一个方法,它将有两个输入:
Socket
requestName
——因此它的签名如下所示:

Runnable getRequestCommand(Socket s, String request)
它将包含您的
开关
块,并返回如下内容

if ("ClientRequestUploadFile".equals(request)) {
    return new ClientRequestUploadFileHandler(s);
} 
// etc
再次使用预先存在的接口,这是一个
双功能(它需要请求字符串和套接字作为输入,并返回可运行的处理程序)

现在,您可以拆分每个
案例
,并创建这样一个函数:

BiFunction<Socket, String, Runnable> upload = (s, req) -> {
    return "ClientRequestUploadFile".equals(req) 
        ? new ClientRequestUploadFileHandler(s)
        : null;
}
现在,您的
开关实际上也启动了创建的
Runnable
,因此您也可以
如果(requestHandler!=null){requestHandler.run();}
,而不将其返回给调用方。作为一行,这是
handlers.stream().map(h->h.apply(s,request)).findFirst(Objects::nonNull).ifPresent(Runnable::run)

无论如何,现在您必须在原始类中创建所有的
BiFunction
s,但是您可以将它们外部化,例如,到
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;
            }
        }
    }
}
现在您得到的代码比以前多得多,但处理本身已从接受套接字的类中移除,因此更好的单一责任;这允许您通过向
enum
添加值来添加功能,而无需触摸原始代码

一个更简单的版本是在方法中创建functions集合,只需执行以下操作

Collection<BiFunction<Socket,String,Runnable>> createMappings() {
    return Arrays.asList(
        createMapping("ClientRequestUploadFile", ClientRequestUploadFileHandler::new),
        createMapping("clientRequestCalculator", ClientRequestCalculatorHandler::new),
    );
}
private BiFunction<Socket,String,Runnable> createmapping(String req, Function<Socket, Runnable> create) {
    return (s, r) -> req.equals(r) ? create.apply(s) : null;
}
Collection createMappings(){
返回数组.asList(
createMapping(“ClientRequestUploadFile”,ClientRequestUploadFileHandler::new),
createMapping(“clientRequestCalculator”,ClientRequestCalculatorHandler::new),
);
}
专用双功能createmapping(字符串请求、函数创建){
return(s,r)->req.equals(r)→create.apply(s):null;
}

使用命令模式。使用字符串作为键从映射中查找适当的命令,并执行()它。我认为您无法避免的是,测试每种情况,独立于开关情况或for循环。你可以试着用一种聪明的方式来制作东西(例如,检查你的字符串是否以“ClientRequest”开头,以避免在字符串是其他内容时测试你的所有3个案例)。@azurefrog你能给我举个例子吗?:)创建一个包含请求名和命令创建的
enum
,并迭代所有值,直到找到匹配项为止。@daniu您所说的命令创建是什么意思?很清楚,谢谢您,还有一个问题,在我的情况下,这样做可以吗?{@Override public void execute(){new ClientRequestUploadFileHandler.start();}当然可以,但是您需要修改命令接口,使其execute方法接受套接字作为参数,以便它在helper方法中可见。此外,请记住,枚举的名称需要与命令字符串匹配,因此类似于
ClientRequestUploadFile{@Override public void execute(socket s){new ClientRequestUploadFileHandler(s).start();}}
你是一名艺术家,我现在在项目中工作,我是一名应届毕业生,我可以在一些项目中听取你的意见吗,而不是大型项目近50个班。(这个项目可以帮助你找到一份工作),我会做所有的事情,你只要给我一张便条plz:)
// ...
Socket s= serverSocket.accept();
DataInputStream clientStream= new DataInputStream(s.getInputStream());
String requestName=clientStream.readUTF();
RequestHandler.runOnRequestMatch(s, requestName);
Collection<BiFunction<Socket,String,Runnable>> createMappings() {
    return Arrays.asList(
        createMapping("ClientRequestUploadFile", ClientRequestUploadFileHandler::new),
        createMapping("clientRequestCalculator", ClientRequestCalculatorHandler::new),
    );
}
private BiFunction<Socket,String,Runnable> createmapping(String req, Function<Socket, Runnable> create) {
    return (s, r) -> req.equals(r) ? create.apply(s) : null;
}