在类中处理几种方法的最佳方法是什么?

时间:2012-07-10 04:20:20

标签: c# oop design-patterns

我在C#中创建一个服务器,我有一个处理数组中所有登录用户的单例。该数组是一个名为UserSession的类的数组。

此UserSession类有一个在单独的线程上运行的方法,该线程处理该用户的传入数据包。

好吧,请考虑以下代码:

class UserSession
{
    public UserSession(TcpClient client)
    {
        var thread = new Thread(new ParameterizedThreadStart(HandleComm);
        thread.Start((object)client);
    }

    public void HandleComm(object tcpClient)
    {
        TcpClient client = (TcpClient)tcpClient;
        NetworkStream stream = client.GetStream();
        while(1 == 1)
        {
            byte[] buffer = new byte[4096];
            stream.Read(buffer, 0, buffer.Length);
            int testShort = Convert.ToInt32(buffer);
            switch((ActionEnum)testShort)
            {
                case ActionEnum.Something:
                    // here is my problem!
                    // do some more parsing of the message
                    this.DoSomething(SomethingStruct argument); // argument taken from the byte array
                    break;
                case ActionEnum.AnotherSomething:
                    // the same as before
                    break;
                // and this goes on and on
            }
        }
    }
}

处理所有这些单独的枚举的最佳方法是什么,而不会在该类上重复超过80种方法? (ActionEnum是一个包含当前用户特定操作的枚举)。

我序列化了那个缓冲区,我只是快速编写代码让你有个主意。

2 个答案:

答案 0 :(得分:2)

所以:你基本上试图这样做,所以将数字代码转换为方法的调用。

有些事情是您无法获得的,例如自动为您完成此转换。如果您已经传输了方法的名称,则可以使用反射来查找方法,然后调用它。因此,您必须做一些手动工作来建立映射。

您必须决定的是将数字映射到方法的最佳方法。

一种方法是你现在使用的那种方式。这里的映射发生在调度周期内(获取号,翻译,调用)。问题是映射代码掩盖了调度代码。还涉及相当数量的样板代码。

您可以使用Command模式和哈希映射的组合,如下所示:

在设置过程中:

  1. 为您的命令创建一个通用接口(或使用Closure,因为 你正在使用C#)
  2. 创建实现接口pr的对象实例。要映射的方法
  3. 将其添加到hashmap。
  4. 在调度循环中:

    1. 获取数字
    2. 在哈希表中查找号码
    3. 如果找到,则调用映射对象,否则失败
    4. 对于更灵活的方法,请将数字视为必须处理的消息,并使用模式链责任。

      命令模式http://en.wikipedia.org/wiki/Command_pattern

      责任链http://en.wikipedia.org/wiki/Chain-of-responsibility_pattern

答案 1 :(得分:1)

一如既往,有很多可能的答案。我在构建客户端/服务器游戏时遇到了这个问题。我开始时有几十个Enums用于操作/事件/消息,然后意识到当你进行大量的操作时它不是很可持续。

所以这就是我粗略地用伪代码做的事情。

    NetworkClass 
    {
       RegisterChannel(objUsingChannel,typeOfChannel, callbackForChannel, expectedReturnType){/*...*/};

       PushChannelMsg(channelID, dataToSend)

       ReceiveMessageFromNetwork(msg){  Read data from network, which also contains channelID, and send it to any matching channelID}  
       //If there is no channel registered for a data that is received,  just ignore it
    }

EnemyTurretObject
{
   //Register the rotation channel and listen for rotation changes
   NetworkClass.RegisterChannel(this, channels.TurretRotated + this.ID, callback=HandleTurretRotated, doubleReturnType)

   HandleTurretRotated(double rot)
   {  rotate the turret  }
}

FriendlyTurretObject
{
   //Register two channels that we'll send data across
   NetworkClass.RegisterChannel(this, channels.TurretFired + this.ID, callback=null, MissleFiredData)
   NetworkClass.RegisterChannel(this, channels.TurretRotated + this.ID, callback=null, doubleDataType)

   FireMissle()
    {
       NetworkClass.PushChannelMsg(channels.TurretFired + this.ID, new MissleFiredData(x,y)) 
    }

    RotateTurret()
    {
      NetworkClass.PushChannelMsg(channels.TurretRotated + this.ID, newTurretRotationValue) 
    }

}

我基本上避免了大量的Enums,并进行了更广泛的设置,以便每个对象负责它自己的数据,通道等。这是一种更灵活的方法,改变一个枚举不会破坏一切。网络类甚至不需要知道正在发送的数据中的内容,它现在只是一个管道。