出于学习目的,我正在编写基于套接字的客户端/服务器应用程序。我设计了一个自定义协议,以确保我可以正确处理我的数据包。今天在检查代码的一些旧部分时,我意识到我创建数据包的方式包含很多冗余代码。
我有不同类型的数据包,例如ImagePacket,MessagePacket等。所有类型的创建仅在次要内容上有所不同,例如标题和分隔符创建是相同的。
为了改善这一点,我提出了这样的解决方案(简化):
abstract class Packet
{
public Packet(object o)
{
MemoryStream memoryStream = new MemoryStream();
AddHeader(ref memoryStream);
AddData(ref memoryStream, obj);
AddDelimiter(ref memoryStream);
_packetBytes = memoryStream.ToArray();
memoryStream.Close();
}
protected abstract void AddData(ref MemoryStream ms, object obj);
AddData方法实现为抽象方法并在具体类中重写,而AddHeader和AddDelimiter在抽象类Packet本身中定义。
这很好用,我没有像以前那么多的重复代码,但我不满意 将对象传递给AddData,因为我不清楚我不能给ImagePacket构造函数一个字符串。
// correct
Packet myMsgPacket = new MessagePacket("hello world");
Packet myImagePacket = new ImagePacket(image);
// wrong, but will be compiled :(
Packet myChaosPacket = new ImagePacket("muaha you're doomed");
如果我必须对正确传递的数据类型进行检查,那么我最终会再次使用大量的愚蠢代码。 如何实现重复代码的减少,还可以解决上面提到的问题?
答案 0 :(得分:3)
这听起来像你需要使用工厂模式
Packet MyPacket = MyPacketFactory.CreatePacket(Data)
然后 MyFactory.CreatePacket
将返回IPacket
更新:根据下面的评论,我应该更清楚了。您的工厂可以有许多重载的CreatePacket()方法,它们采用不同的数据类型......
IPacket CreatePacket(Image Data) {}
IPacket CreatePacket(String Data) {}
IPacket CreatePacket(Exception Data) {}
如果您有多种类型的数据包只包含字符串(例如消息数据包,状态数据包或类似数据包)您可以创建一个枚举,指示需要哪个字符串数据包...
IPacket CreatePacket(String Data, StringPacketTypesEnum PacketType) {}
在您的数据包工厂内部,您可以拥有处理所有重复代码的常用功能 - 例如AddDelimiter()
- 这将保留您的代码DRY
答案 1 :(得分:0)
Ruby on Rails使用Validations来解决这个问题。
如果你的addData()函数在输入上调用了validate(),你只需要覆盖每个子类中的validate()。
答案 2 :(得分:0)
我会转到返回界面的工厂模式。
除此之外,您不需要使用ref作为参考类型。
答案 3 :(得分:0)
我认为你目前的解决方案还可以。您只需要将传递给ImagePacket
构造函数的数据类型缩小到Image
类型。例如:
class ImagePacket : Packet{
public ImagePacket(Image img) : base(img){}
}
这将检查数据是否是处于复制时的图像。当然,如果你真的必须传递object
类型,这可能不起作用。