简体   繁体   English

创建可扩展属性类(OOP)

[英]Creating an extensible properties class (OOP)

I have an application which supports multiple types and versions of some devices. 我有一个应用程序,它支持某些设备的多种类型和版本。 It can connect to these devices and retrieve various information. 它可以连接到这些设备并检索各种信息。

Depending on the type of the device, I have (among other things) a class which can contain various properties. 根据设备的类型,我有(除其他外)一个可以包含各种属性的类。 Some properties are common to all devices, some are unique to a particular device. 某些属性对所有设备都是通用的,有些属性对于特定设备是唯一的。

This data is serialized to xml. 此数据序列化为xml。

What would be a preferred way to implement a class which would support future properties in future versions of these devices, as well as be backwards compatible with previous application versions? 实现一个类的首选方法是什么,它将支持这些设备的未来版本中的未来属性,以及向后兼容以前的应用程序版本?

I can think of several ways, but I find none of them great: 我可以想到几种方法,但我发现它们都不是很好:

  • Use a collection of name-value pairs: 使用一组名称 - 值对:
    • pros : good backward compatibility (both xml and previous versions of my app) and extensibility, 专业人士 :良好的向后兼容性(我的应用程序的xml和以前版本)和可扩展性,
    • cons : no type safety, no intellisense, requires implementation of custom xml serialization (to handle different value objects) 缺点 :没有类型安全,没有智能感知,需要实现自定义xml序列化(处理不同的value对象)
  • Create derived properties class for each new device: 为每个新设备创建派生属性类:
    • pros : type safety 专业人士 :类型安全
    • cons : have to use XmlInclude or custom serialization to deserialize derived classes, no backward compatibility with previous xml schema (although by implementing custom serialization I could skip unknown properties?), requires casting for accessing properties in derived classes. 缺点 :必须使用XmlInclude或自定义序列化来反序列化派生类,不与以前的xml架构向后兼容(虽然通过实现自定义序列化我可以跳过未知属性?),需要强制转换才能访问派生类中的属性。
  • Another way to do it? 另一种方法呢?

I am using C#, by the way. 顺便说一句,我正在使用C#。

类似于PropertyBag的东西怎么样?

If you're not limited to interoperability with an external schema, then you should use Runtime Serialization and the SoapFormatter. 如果您不仅限于与外部模式的互操作性,那么您应该使用运行时序列化和SoapFormatter。 The pattern for runtime serialization permits derived classes to specify which of their properties need to be serialized and what to do with them when deserialized. 运行时序列化的模式允许派生类指定需要序列化哪些属性以及在反序列化时如何处理它们。

The XML Serializer requires XmlInclude because, in effect, it needs to define the schema to use. XML Serializer需要XmlInclude,因为实际上它需要定义要使用的模式。

I like name/value sets for this sort of thing. 我喜欢这类东西的名称/价值集。

Many of your cons can be dealt with -- consider a base class that acts as a general name/value set with no-op methods for validating incoming name/value pairs. 您可以处理许多缺点 - 考虑一个基类,它充当通用名称/值集,使用无操作方法验证传入的名称/值对。 For known sets of names (ie keys), you can create derived classes that implement validation methods. 对于已知的名称集(即键),您可以创建实现验证方法的派生类。

For example, Printer may have a known key "PrintsColor" that can only be "true" or "false". 例如,Printer可能具有已知的键“PrintsColor”,其只能是“true”或“false”。 If someone tries to load PrintsColor = "CMYK", your Printer class would throw an exception. 如果有人试图加载PrintsColor =“CMYK”,您的Printer类将抛出异常。

Depending on what you're doing, you can go a few different ways in terms of making the validation more convenient -- utility methods in the base class (eg checkForValidBoolean()) or a base class that accepts name/type information in its constructor for cleaner code in your derived classes, and perhaps a mostly automated XML serialization. 根据您正在做的事情,您可以采用几种不同的方式使验证更方便 - 基类中的实用程序方法(例如checkForValidBoolean())或在其构造函数中接受名称/类型信息的基类在派生类中更清晰的代码,也许是一个大多数自动化的XML序列化。

For intellisense -- your derived classes could have basic accessors that are implemented in terms of the key lookup. 对于intellisense - 您的派生类可以具有基于密钥查找实现的基本访问器。 Intellisense would present those accessor names. Intellisense将显示那些访问者名称。

This approach has worked well for me -- there's sort of a short-sightedness to classic OO design, especially for large systems with plugged-in components. 这种方法对我来说效果很好 - 对于经典的OO设计有一种短视,特别是对于带有插入式组件的大型系统。 IMO, the clunkier type checking here is a big of a drag, but the flexibility make it worthwhile. IMO,这里的笨重类型检查是一个很大的拖累,但灵活性使它值得。

I believe that creating derived properties is the best choice. 我相信创建派生属性是最好的选择。

You can design your new classes using xml schema. 您可以使用xml架构设计新类。 And then just generate the class code with xsd.exe . 然后使用xsd.exe生成类代码。

With .net isn't hard to develop a generic class that can serialize and deserialize all types to and from xml. 使用.net并不难开发一个可以将所有类型序列化和反序列化为xml的泛型类。

public static String toXmlString<T>(T value)
{
    XmlSerializer xmlSerializer = new XmlSerializer(typeof(T));
    StringWriter stringWriter = new StringWriter();
    try { xmlSerializer.Serialize(stringWriter, value); }
    catch (Exception e)
    {
        throw(e);
    }
    finally { stringWriter.Dispose(); }
    String xml = stringWriter.ToString();
    stringWriter.Dispose();
    return xml;
}

public static T fromXmlFile<T>(string fileName, Encoding encoding)
{
    Stream stream;
    try { stream = File.OpenRead(fileName); }
    catch (Exception e)
    {

      e.Data.Add("File Name", fileName);
      e.Data.Add("Type", typeof(T).ToString());
      throw(e);
    }

    BufferedStream bufferedStream = new BufferedStream(stream);
    XmlSerializer xmlSerializer = new XmlSerializer(typeof(T));

    TextReader textReader;
    if (encoding == null)
        textReader = new StreamReader(bufferedStream);
    else
        textReader = new StreamReader(bufferedStream, encoding);

    T value;
    try { value = (T)xmlSerializer.Deserialize(textReader); }
    catch (Exception e)
    {
        e.Data.Add("File Name", fileName);
        e.Data.Add("Type", typeof(T).ToString());
        throw(e);
    }
    finally
    {
        textReader.Dispose();
        bufferedStream.Dispose();
    }
    return value;
}

Programatically speaking, this sounds like it might be a job for the Decorator Pattern . 从编程方面来说,这听起来像是装饰模式的工作 Essentially, you have a super class which defines a common interface for all these types of devices. 从本质上讲,您有一个超类,它为所有这些类型的设备定义了一个通用接口。 Then you have decorator classes which have other properties which a device might have. 然后你有装饰器类,它们具有设备可能具有的其他属性。 And, when creating these devices, you can dynamically add these decorations to define new properties for the device. 而且,在创建这些设备时,您可以动态添加这些装饰以定义设备的新属性。 Graphically: 图形:

替代文字

You can look at the Wikipedia page for a more detailed description. 您可以查看Wikipedia页面以获取更详细的描述。 After that, it would just be a matter of doign some serialization to tell the program which decorators to load. 在那之后,只需要一些序列化来告诉程序装载哪些装饰器。

The general idea of what you're trying to accomplish here is precisely what the EAV pattern solves. 你在这里想要完成的一般想法正是EAV模式所解决的问题。 EAV is a pattern most commonly used in database development but the concept is equally valid for applications. EAV是数据库开发中最常用的模式,但该概念对于应用程序同样有效。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM