【问题标题】:How to dynamically handle commands in a chat program?如何动态处理聊天程序中的命令?
【发布时间】:2017-08-02 16:47:48
【问题描述】:

我的问题更多的是设计问题。我目前正在用 Java 开发一个经典的服务器-客户端聊天程序。一切都很好,直到我得到命令。我认为用户发送命令会很方便,然后服务器会处理这些命令以更改他们的昵称。问题是我想编写灵活的代码,尤其是面向对象的代码。为了避免无休止的 if/else if 语句来知道输入了什么命令,我相信最好为从超类 Command 继承的每个命令创建一个类。然后我可以通过在所有子类中覆盖的 getCommand() 函数返回特定命令。但这根本不能解决我的问题。服务器仍然需要使用 instanceof 测试返回了什么命令。动态执行此操作的一种方法是从超类 Command 自动向下转换它,然后在服务器类中调用适当的函数。例如:

public void processCommand(CommandNick c) {}
public void processCommand(CommandKick c) {}

但我还没有找到任何合适的方法来做到这一点,即使我找到了,我觉得这里仍然存在设计问题。而且我相信有一种很好且灵活的方法可以做到这一点,但是几天时间还不足以让我弄清楚。有任何想法吗?提前致谢! :)

【问题讨论】:

  • 你能展示一些你最初想法的代码sn-ps吗?

标签: java server command client chat


【解决方案1】:

我假设您的服务器将消息作为带有 Sender 和 String 的对象接收。创建您的 Command 类,并在服务器初始化代码中,使用 String 作为键和您的 AbstractCommand 类作为值创建一个 HashMap<String, AbstractCommand>。你的命令应该扩展这个类。注册所有命令,如下所示:

commandRegistry.put("help", new HelpCommandHandler());

我假设命令是一条带有! 的消息。所以当你收到一条消息时,检查它是否是一个命令:

Message message = (Your Message)
String messageBody = message.getBody();
Sender messageSender = message.getSender();

if(messageBody.startsWith("!")) {
    // Split the message after every space
    String[] commandParts = messageBody.split(" ");
    // The first element is the command base, like: !help
    String baseCommand = commandParts[0];
    // Remove the first character from the base, turns !help into help
    baseCommand = baseCommand.substring(1, baseCommand.length());
    // Creates a new array for the arguments. The length is smaller, because we won't copy the command base
    String[] args = new String[commandParts.length - 1];
    // Copy the elements of the commandParts array from index 1 into args from index 0
    if(args.length > 0) {
        System.arraycopy(commandParts, 1, args, 0, commandParts.length - 1);
    }
    // Your parse method
    processCommand(sender, baseCommand, args);
}

public void processCommand(Sender sender, String base, String[] args) {
    if(commandRegistry.containsKey(base)) {
        commandRegistry.get(base).execute(sender, args);
    } else {
        // Handle unknown command
    }
}

public abstract class AbstractCommand {
    public abstract void execute(Sender sender, String[] args);
}

示例实现。我假设您的服务器是单例服务器,您可以使用 Server.get() 或任何类似方法获取它的对象。

public class HelpCommandHandler extends AbstractCommand { /* !help */
    @Override
    public void execute(Sender sender, String[] args) {
        sender.sendMessage("You asked for help."); // Your code might not work like this.
    }
}

public class ChangeNickCommandHandler extends AbstractCommand { /* !changenick newNick */
    @Override
    public void execute(Sender sender, String[] args) {
        // I assume you have a List with connected players in your Server class
        String username = sender.getUsername(); // Your code might not work like this
        Server server = Server.get(); // Get Server instance
        server.getUsers().get(username).setNickname(args[0]); // Argument 0. Check if it even exists.
    }
}

// Server class. If it isn't singleton, you can make it one like this:
public class Server {
    private static Server self;
    public static Server init(/* Your args you'd use in a constructor */) { self = new Server(); return get(); }
    public static Server get() { return self; }

    private List<User> users = new List<User>();
    private HashMap<String, AbstractCommand> commandRegitry = new HashMap<>();

    // Make construcor private, use init() instead.
    private Server() {
        commandRegistry.put("help", new HelpCommandHandler());
        commandRegistry.put("changenick", new ChangeNickCommandHandler());
    }

    // Getters
    public List<User> getUsers() {
        return users;
    }

    public HashMap<String, AbstractCommand> getRegistry() {
        return commandRegistry;
    }
}

【讨论】:

  • 感谢您的回答!您的方法的问题是 handleCommand() 函数是在 AbstractCommand 类中实现的,而我希望命令由服务器本身处理。例如,用户名由服务器存储。所以如果用户想要更改他们的昵称,该命令需要从服务器调用一个方法..
  • 我可能把它命名为混乱。 if 语句通过检查命令是否以 ! 开头来处理命令。你应该把它放在你从客户那里收到消息的地方。我会将抽象方法重命名为更清晰的名称。
  • 在这种情况下,我将向服务器打开 2 个通道。一种处理普通聊天内容,另一种可以在后台处理服务器端内容。
  • @Brianbcr666Ray 为什么?服务器处理命令内容,通过检查它是否以 ! 开头,如果它确实处​​理它,则像普通消息一样处理。
  • @LórántViktorGerber 我知道你的代码做了什么,它非常接近我的预期,除了 execute() 在 AbstractCommand 类中而不是在服务器类中。因此它将无法将更改应用到服务器。
【解决方案2】:

这是一段伪代码,用于说明您的控制器不需要了解命令处理器(不需要 instanceof)。

abstract class CommandProcessor {
    /* return boolean if this Command processed the request */  
    public static boolean processCommand(String command, User user, Properties chatProperties, Chat chat);
}

/* Handle anything */
public class CommandRemainder extends CommandProcessor {
    @Override
    public static boolean processCommand(String command, User user, Properties chatProperties, Chat chat) {
        chat.appendText("[" + user.getName() + "] " + command);
        return true;
    }
}

/* Handle color changing */
public class CommandColorizer extends CommandProcessor {
    protected static List<String> ALLOWED_COLORS = new ArrayList<>(Arrays.asList("red", "blue", "green"));
    @Override
    public static boolean processCommand(String command, User user, Properties chatProperties, Chat chat) {
        if ("fg:".equals(command.trim().substring(0,3)) {
            String color = command.trim().substring(3).trim();
            if (ALLOWED_COLORS.contains(color)) {
                chat.setForeground(color);
            }
            return true;
        }
        return false;
    }
}

public class ChatController {
    protected Chat chat = new Chat();
    protected User user = getUser();
    protected Properties chatProperties = getChatProperties();
    protected List<CommandProcessor> commandProcessors = getCommandProcessors();

    {
        chat.addChatListener(new ChatListener(){
            @Override
            public void userChatted(String userChatString) {
                for (CommandProcessor processor : commandProcessors) {
                    if (processor.processCommand(userChatString, user, chatProperties, chat)) {
                        break;
                    }
                }
            }
        });
    }
    List<CommandProcessor> getCommandProcessors() {
        List<CommandProcessor> commandProcessors = new ArrayList<>();

        commandProcessors.add(new CommandColorizer());
        commandProcessors.add(new CommandRemainder()); // needs to be last

        return commandProcessors;
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-02-02
    • 2018-05-20
    • 1970-01-01
    • 2021-09-09
    • 2017-11-25
    相关资源
    最近更新 更多