Jas*_*son 5 .net c# design-patterns casting
注意
这是一个相当大的问题,所以请耐心等待,如果不清楚我会提前道歉.为了使这个问题易于管理,并尽量减少混淆,我省略了复制和粘贴类的一些属性.
CONTEXT
我正在编写一个联网应用程序 - 一种"远程桌面"应用程序,以便普通技术人员可以帮助他的朋友或邻居解决计算机问题.我意识到像TeamViewer这样的免费和更高级的软件存在,但这个问题并没有讨论创建这个软件的实用性.
共同条款
该客户端是技术人员的书呆子,助手-的控制器.该服务器是"受害者",一遇险.该客户端通常是启动命令服务器之一.
信息
该软件不仅仅是实时查看/控制应用程序.我想要额外的模块,例如文件浏览器模块和聊天模块(因此他们可以在不需要使用额外的即时消息软件的情况下进行通信).
最初,我通过UDP发送和接收消息的方法是手动且低效的.(我使用Lidgren库进行UDP网络连接,这就是为什么我的数据包不显示低级字节,如标题消息大小.)
Original Packet Structure:
Byte Index Type Purpose/Description
------------------------------------------------------
0 byte The intended destination module (e.g. 1 -> Chat module)
1 byte The command for this module to perform (e.g. 0 -> NewChatMessage)
2 byte Encryption/compression flag
3 byte Packet priority flag (e.g. Cancel packets should be processed first)
4-X ? Command-specific arguments of variable type (e.g. For this example, the argument would be the actual chat message text)
Run Code Online (Sandbox Code Playgroud)
一旦我发出100多条消息,这种方法就变得难以包裹.来源变得神秘,修改是一件苦差事.即使将"目标模块"和"命令"编码为枚举(仍然是间接数字),仍然很难开发应用程序.在接收这些数据包时,解析特定于命令的参数将成为嵌套在一个非常长的if-else梯形图中的一个可笑的长开关梯形图.
我后来决定对我的数据包结构采用序列化.我现在可以将每个命令设计为一个类,然后将其序列化为一个紧凑的字节数组(或字符串),然后将其反序列化为其原始类,并将参数作为类属性读取.它似乎更容易,我愿意为更大的序列化数据结构支付带宽成本.
Revised Packet Structure:
Byte Index Type Purpose/Description
------------------------------------------------------
0-X String Serialized class
Run Code Online (Sandbox Code Playgroud)
问题
但这个问题是设计之一.我没有问题序列化和反序列化我的类.问题在于我如何设计我的命令/数据包类.我发现自己被困在做这个.我的课程设计如下:
数据包基类
/// <summary>
/// The fundamental unit of network communication which can be transmitted and received from client to server. Contains the destination module and module-specific command.
/// </summary>
public class Packet
{
/// <summary>
/// Specifies the module this packet is forwarded to.
/// </summary>
public Module DestinationModule { get; set; }
/// <summary>
/// Specifies whether this packet is encrypted or compressed.
/// </summary>
public EncryptionCompressionFlag EncryptedOrCompressed { get; set; }
/// <summary>
/// Specifies the packet's priority.
/// </summary>
public PacketPriority Priority { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
欢迎模块数据包库(更多相关信息如下)
/// <summary>
/// Base packet structure for packets specific to this module. Adds a ModuleCommand property from the base Packet class.
/// </summary>
public class WelcomeModulePacket : Packet
{
/// <summary>
/// Specifies the command number this particular packet is instructing the module to execute.
/// </summary>
public ModuleCommand Command { get; set; }
}
/// <summary>
/// Specifies the commands for this module.
/// </summary>
public enum ModuleCommand : byte
{
/// <summary>
/// The user has just clicked the Connect button (on form's GUI) and is sending this connect packet.
/// </summary>
ClientRequestingConnectionPacket = 0,
}
Run Code Online (Sandbox Code Playgroud)
'WelcomeModulePacket'类的说明:
所以,因为我有两个以上的模块,有一个单一的枚举"CommandModule"列举每对命令的所有模块,将是一个很长的枚举和相当混乱.这就是为什么我为每个模块都有一个不同的 'ModuleCommand'枚举.
连接包
/// <summary>
/// The user has just clicked "Connect" on the Welcome form of the client. The client is now requesting a connection to the server.
/// </summary>
public class ClientRequestingConnectionPacket : WelcomeModulePacket
{
public string Username { get; set; }
public string Password { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
所以,你可以看到我的类不仅有两层继承,还有三层.
ClientRequestingConnectionPacket : WelcomeModulePacket : Packet
Run Code Online (Sandbox Code Playgroud)
当我将特定数据包(如"ClientRequestingConnectionPacket")存储到不太具体的数据结构中时,此设计的问题就显而易见了.例如,将"ClientRequestingConnectionPacket"排入通用PriorityQueue(用于优先级排序,请记住"Packet"类如何具有"PacketPriority"属性).当我将此数据包出列时,它将作为数据包而不是ClientRequestingConnectionPacket出列.在这种情况下,因为在那里孤单只有一个包,我明明知道数据包的原始类型是ClientRequestingConnectionPacket,但是当我存储数百包在排队,我没有任何办法知道原来的特定类型把它扔回去.
所以我做了一些奇怪的补充,比如添加这个属性:
Type UltimateType { get; set; }
Run Code Online (Sandbox Code Playgroud)
到'Packet'基类.但我的意思是,这种"解决方案"看起来真的是强制性和原始性的,我不相信它是一个真正的解决方案.
那么,根据上面其他StackOverflow问题的链接,我是否犯了这个错误?我如何解决它?这称为什么?我应该如何设计我的申请?
修订问题:Packet类的适当设计模式是什么?
您可以按照 FishBasketGordo 建议使用GetType,也可以使用以下命令测试类型is
if (packet is ClientRequestingConnectionPacket)
{ /* do something */ }
else if (packet is SomeOtherPacket)
....
Run Code Online (Sandbox Code Playgroud)