从 Websocket 服务器中移除 Switch 语句的味道

Removing Switch Statement Smell from a Websocket Server

我不确定这个问题属于这里,因为它是 "debatable",但让我们试一试。

在阅读了很多例子和关于它的问题之后(比如 this one 看起来真的很相似),我仍然不知道我是否应该选择多态更换开关。

这都是关于接收 JSON 消息并根据消息执行例程的 Ratchet WebSocket 服务器 type:

public function onMessage(ConnectionInterface $from, $msg) {
    /*!
    Triggers everytime a message is received by the application.
    Depending on @p $msg type, the application will broadcast @p $msg accordingly.

    @param ConnectionInterface $from 
    This is the socket (client) who sent @p $msg.
    @param string $msg
    A JSON string sent by the client.
    */
    $usermsg = json_decode($msg, true);
    if (isset($usermsg["message"])) {
        $actual_msg = $this->removeHTML($usermsg["message"]);
    }

    switch ($usermsg["type"]) {

        case 'text':
            $this->text($from, $actual_msg, "text");
            break;

        case 'token':
            $this->token($from, $usermsg["im"]);
            break;

        case "ready":
            $this->ready($from);
            break;

        case "action":
            $this->text($from, $actual_msg, "action");
            break;

        case "users":
            $this->whoIsOnline($from);
            break;

        case "listen":
            $this->listen($from);
            break;

        case "end":
            $this->finish($from, $actual_msg);
            break;

        case 'statInit':
            $this->statInit($from);
            break;
    }
}

事实是,$msg 是一个字符串,因为它是 JSON,根本没有为到达的任何消息实例化任何对象。这就是为什么没有 class 层次结构的原因,因为消息不是对象。事实上,除了实际的服务器之外,没有其他 class。

无论如何,这是唯一存在于服务器端的开关(客户端中还有一个开关,但它是 jQuery 所以情况不同),因此添加新功能应该是添加一个案例和方法,没那么难。该项目不会增长太多,但我希望它可以轻松扩展。

我是否应该坚持 OOP 设计并为每条到达的消息创建一个对象并应用多态性?似乎有点不知所措,因为服务器处理聊天消息。

一方面,归根结底,这对您有用并且相对容易维护。完全 OOP 可能只是一种纯粹的练习 and/or 自我。

如果您要将 OOP 方法提升到一个新的水平,如果是我,我会创建一个 SocketMessage class 来接受 JSON 消息字符串构造函数。我还会为每个扩展它的消息类型创建一个 SocketHandler class 和单独的处理程序 classes。您的方法之后的一些粗略代码是:

public function onMessage(ConnectionInterface $from, $json) 
{
    $message = new SocketMessage($json);
    return $message->dispatch();
}

dispatch() 调用将是 SocketMessage class 的一部分,并将根据消息类型确定将其定向到哪个处理程序。它会知道哪些处理程序可用,因为您首先会在基础 SocketHandler class.

中注册它们
// Somewhere that you initialize code
SocketHandler::register('text', Handlers\TextHandler::class);
SocketHandler::register('token', Handlers\TokenHandler::class);
SocketHandler::register('ready', Handlers\ReadyHandler::class);

注册处理程序只会将它们放入 class 中的静态数组中,以便稍后访问。在 SocketMessage::dispatch() 函数中,你可能会做这样的事情:

class SocketMessage
{
    ...
    public function __construct($json)
    {
        // Parse raw message data
        $this->message = @json_decode($json, true);
        if (empty($this->message)) {
           throw new Exception('SocketMessage expects a valid JSON string.');
        }
    }
    ...
    public function dispatch()
    {
        $type = $this->message['type'];
        $handler = SocketHandler::get($type);

        return $handler->handle($this->message);
    }
    ...
} 

我真的不确定所有这些我要去哪里。我的主要观点是你可以让它更面向对象,这将导致它更灵活和可扩展,但也会带来更多的可维护性困难。这实际上是一个偏好和项目范围的问题(即它是一个由很多人维护的大型项目,还是您在业余时间玩的一个爱好脚本?)