我应该在这里使用哪种设计模式

时间:2010-12-19 03:40:39

标签: c# design-patterns

出于学习目的,我正在编写基于套接字的客户端/服务器应用程序。我设计了一个自定义协议,以确保我可以正确处理我的数据包。今天在检查代码的一些旧部分时,我意识到我创建数据包的方式包含很多冗余代码。

我有不同类型的数据包,例如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");

如果我必须对正确传递的数据类型进行检查,那么我最终会再次使用大量的愚蠢代码。 如何实现重复代码的减少,还可以解决上面提到的问题?

4 个答案:

答案 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类型,这可能不起作用。