用于避免开启类型的设计模式或接受的解决方案

我正在尝试找到一个好的,干净的设计模式或普遍接受的实现来处理类型的枚举,其中仅在运行时知道单个类型。

我之前已经问过类似的问题,但我仍然不清楚替代实现相比交换机或一系列if-thens有明显的优势。

首先,我将演示一些实现,然后我将问一个问题: 这些实现是否比简单的交换机更好或更优先? 如果是这样,为什么? 如果没有,为什么不呢?

在我的应用程序中,我通过流发送和接收数据。 在运行时,我通过序列化接收数据结构,该结构描述了我的二进制数据中的字段。 这包括字段中的数据类型,即Int32,Bool,Double等。在设计时,我所知道的是数据可能是几种类型中的一种。 我需要从流中读取字段并适当地处理数据。

如果允许启用类型,则解决方案可能如下:

非工作代码:

object ReadDataField(byte [] buff, ref int position, Dictionary fields) { object value; int field = buff[position]; position++; switch(fields[field]) { case typeof(Int32): { value = (Int32)BitConverter.ToInt32(buff, position); position += sizeof(Int32); break; } case typeof(Int16): { value = (Int16)BitConverter.ToInt16(buff, position); position += sizeof(Int16); break; } // Etc... } return value; } 

在我看来,这段代码的优点是简单易读,易于维护。

但是,由于在C#中没有打开类型,我实现了以上内容:

工作守则:

 enum RawDataTypes { Int32, Int16, Double, Single, etc. } object ReadDataField(byte [] buff, ref int position, Dictionary fields) { object value; int field = buff[position]; position++; switch(fields[field]) { case RawDataTypes.Int32: { value = (int)BitConverter.ToInt32(buff, position); position += sizeof(int); break; } case RawDataTypes.Int16: { value = (Int16)BitConverter.ToInt16(buff, position); position += sizeof(Int16); break; } // Etc. } return value; } 

这显然是一种解决方法,但它也很简单,易于维护。

但是,C#中没有几篇详细介绍类型切换的文章。 除了以产生预期结果的方式处理inheritance的困难等之外,我已经看到许多答案已经说过有一种更“更好”的方法,更符合面向对象编程精神

提出的常见解决方案是1)使用多态,或2)使用字典查找。 但实施要么有其自身的挑战。

关于多态性,以下是“如果它工作不会很好”代码的示例:

多态性的非工作实现:

 object ReadDataField(byte [] buff, int position, Dictionary fields) { int field = buff[position]; position++; object value = Activator.CreateInstance(fields[field]); // Here we're trying to use an extension method on the raw data type. value.ReadRawData(buff, ref position); return value; } public static Int32 ReadRawData(this Int32 value, byte[] buff, ref int position) { value = BitConverter.ToInt32(buff, position); position += sizeof(Int32); return value; } public static Int16 ReadRawData(this Int16 value, byte[] buff, ref int position) { value = BitConverter.ToInt16 (buff, position); position += sizeof(Int16 ); return value; } // Additional methods for each type... 

如果您尝试编译上面的代码,您将获得:

‘object’不包含’ReadRawData’的定义,并且最好的扩展方法重载’RawDataFieldExtensions.ReadRawData(short,byte [],ref int)’在blah blah中有一些无效的参数…

您不能将原始数据类型子类化以添加function,因为它们是密封的,因此扩展方法似乎是一种选择。 但是,扩展方法不会从’object’转换为实际类型,即使调用value.GetType()返回基础类型:System.Int32,System.Int16等。使用’dynamic’关键字不帮助,因为您不能在动态类型上使用扩展方法 。

通过将对象本身的实例作为参数传递给具有多态参数的方法, 可以使上述工作起作用:

多态的工作实现:

 object ReadDataField(byte [] buff, int position, Dictionary fields) { int field = buff[position]; position++; dynamic value = Activator.CreateInstance(fields[field]); // Here the object is passed to an overloaded method. value = ReadRawData(value, buff, ref position); return value; } public static Int32 ReadRawData(Int32 value, byte[] buff, ref int position) { value = BitConverter.ToInt32(buff, position); position += sizeof(Int32); return value; } public static Int16 ReadRawData(Int16 value, byte[] buff, ref int position) { value = BitConverter.ToInt16 (buff, position); position += sizeof(Int16 ); return value; } // Additional methods for each type... 

上面的代码工作并且仍然是直截了当的,可维护的,并且可能更多“本着面向对象编程的精神”。

但它真的“更好吗?” 我认为它使维护更加困难,因为它需要更多的搜索来查看已实现的类型。

另一种方法是使用字典查找。 这样的代码可能如下所示:

字典实现:

 delegate object ReadDelegate(byte [] buff, ref int position); static Dictionary readers = new Dictionary { { typeof(Int32), ReadInt32 }, { typeof(Int16), ReadInt16 }, // Etc... }; object ReadDataField(byte [] buff, int position, Dictionary fields) { int field = buff[position]; position++; object value = readers[fields[field]](buff, ref position); return value; } public static object ReadInt32(byte[] buff, ref int position) { Int32 value = BitConverter.ToInt32(buff, position); position += sizeof(Int32); return value; } public static object ReadInt16(byte[] buff, ref int position) { return BitConverter.ToInt16(buff, position); position += sizeof(Int16); return value; } // Additional methods for each type... 

在我看来,字典实现对多态解决方案的一个优点是它列出了可以在一个易于阅读的位置处理的所有类型。 这对可维护性很有用。

但是,鉴于这些例子,是否有更好,更清洁,更容易接受等实施方案,它们具有明显的优势? 使用多态或字典查找的这些实现是否优于使用交换机? 我并没有真正保存任何代码,我不确定我是否已经增加了代码的可维护性。

无论如何,我仍然需要使用自己的方法枚举每个类型。 多态性将条件推迟到语言本身,而不是用开关或if-then显式。 使用字典依赖于内部条件来进行自己的查找。 在一天结束时,有什么区别?

  1. 使用所有实现公共接口的“转换器”(或其他)的静态集合。 然后,您可以遍历该集合,询问每个集合是否处理该类型。 如果他们这样做,那么请他们这样做。 每个转换器只知道它的类型。
  2. 使用相同的静态“集合”转换器,但将它们保存在按类型键入的字典中。 然后从字典中按类型请求转换器并要求它为您转换。

使用策略设计模式:

定义单独的转换器对象(使用通用接口),封装不同的转换算法。 客户端在运行时将转换委托给适当的转换器对象。

这大大减少了实现依赖性。 客户端代码与转换的实现方式无关。

我同意@David Osborne的回答。 即使您使用switch语句实现单个转换器对象,此实现也会封装并从客户端隐藏。