65.9K
CodeProject 正在变化。 阅读更多。
Home

DataSet - 多态集合

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.70/5 (12投票s)

2013年3月7日

CPOL

4分钟阅读

viewsIcon

30909

downloadIcon

5

一组提供多态数据结构的类。

介绍 

DataSet 是一个简单的类,可以保存任何类型的项目。我最初使用它为客户端层提供数据,仅通过公开数据来封装服务器端的功能。然后,它扩展了 ConfigData,这是一个包装的 DataSet,提供配置数据并强制读取所有项目。

这篇文章描述了我用来支持 DataSet 的类包。单独来看,这没什么,但我意识到我想要发表文章的很多其他主题都需要它,因为它是我传递数据的实际结构。

这可以用来代替 JSON,我打算很快让转换器可靠地工作。

背景 

通常情况下,一个类没有任何功能,只有值。随着时间的推移,我厌倦了编写无数只有访问方法的类。因此,基于我曾在各种工作中使用的设计,我开始构建自己的类。

基类

结构非常简单。一个 DataSet 包含一组 DataItem 对象。每个项目都采用一个名称/值对,并且一旦创建就不可变。由于在使用泛型时遇到了麻烦,我改变了设计,使该项目保存一个 Object,而类本身确保了安全的类型转换,而不是必须在更高级别处理它。

DataSet 中,项目可以作为对象检索,也可以直接访问值;这省去了很多空值检查和方法链。

可以安全地从 DataSetDataItem 调用的“原生”类型都列在枚举 ValueType 中。所有其他类型都被视为 Object,消费者有责任安全地管理它们。

然后有两个包装类。ConfigData 用于从文件加载应用程序的配置,而 SealedDataSet 提供对 DataSet 的只读访问。

传输层

我使用 DataSet 的两个主要用途是通信和持久化。为此,我提供了两个读写数据的类。

DataReader 用于从 BufferedReader 中获取传入的 String 并将其转换为 DataSet。反之,DataWriterDataSet 转换为字符串并将其写入 BufferedWritter。这些字符串的格式简洁明了

从 JavaDoc 中提取的格式示例是

# comments can be added at any point and each
# item is written thus:
# name t value
example {
# Boolean -
   isMale ? TRUE
# DataSet -
   subBlock {
      item - simple text.
   } 
# Date -
   dateWriten @ 2013-02-27 12:00:25.3789Z
# Double -
   weight $ 75.3
# Integer -
   age % 42
# Long -
   big = 123456
# String [without reserved characters] -
   name - William Norman-Walker
# String [with reserved chracters] -
   longText \
\# this will all be read as\
it \@ contains all the special chracters \\ escaped\
\{ so the parser can read them \}.\
all for \$15.00 \@ \-50\% discount
# An array can be a list of any supported objects
 array [
  - each element in an array
  - will appear on a different line
  ! 123
  - data types can be mixed
 ]
} 

它有点像我所知道的其他几种格式;但这就是它的演变方式。对于简单的配置文件,类型字符可以省略,并被视为字符串。扩展格式实际上是多余的,只需在行终止符上使用反斜杠就足以干净地解析它了。

使用代码

那么我如何使用它呢?第一种方式是加载配置选项。我将应用程序的配置存储在数据文件中,然后在启动时将其加载。

/**
 * Standard Entry point.
 * @param args
 */
public static void main (String[] args) {
    DataReader reader = null;
    try {
        reader = new DataReader(new File("demmo.config"));
        ConfigData config = new ConfigData(reader.read());
        App app = new Appendable(config);
        config.close();
    } catch (DataException ex) {
        System.err.print(ex);
    } catch (java.io.IOException ex) {
        System.err.print(ex);
    } finally {
        if (reader != null) {
            try {
                reader.close();
            } catch (IOException ex) {
                System.err.print(ex);
            }
        }
    }
} 

我不太喜欢抛出异常,但在这里我认为这样做是有意义的。考虑一下,ConfigData 的构造函数接受一个文件并处理所有底层问题。

数组

很明显,我需要数组。作为使用 MongoDb 后端的一部分,我发现从 JSON 转换为更安全的 DataSet 存在很多问题,所以我决定迎难而上。

要将数组作为项目添加,首先需要将其放入 ValueArray 中,这样可以确保数据得到正确管理。

数组中的值必须符合普通的值类型,并且它们本身可以是数组,但目前不支持 NULL。这将来可能会改变。

要添加一个数组,代码简洁明了

 
// Add the array:
int[] arr = {1, 2, 3};
data.put("first", new ValueArray(arr));

ValueArray va = new ValueArray();
va.add("X");
va.add(42);
data.put("second", va);
// or add to an exisiting one
data.getArray("first").add("Something");

链式调用

作为添加数组支持的一部分,我引入了链式调用的概念。对结构进行任何添加都将导致返回添加的组件,这允许使用链式命令。链式命令意味着我们可以替换

DataSet data = new DataSet();
data.put("A", 1);
ValueArray vaB = new ValueArray();
vaB.add("One");
vaB.add(2);
vaB.add(9876543210L);
vaB.add(3.4);
vaB.add(false);
ValueArray innerArray = new ValueArray();
innerArray.add(1);
innerArray.add(2);
vaB.add(innerArray);
DataSet innerData = new DataSet();
innerData.put("key","value");
vaB.add(innerData);
vaB.add(new java.util.Date())
data.put("B", vaB);
DataSet subset = new DataSet();
subset.put("D", null)
subset.put("E", "1\\2#3-4\"5{6}7?8$9@a%b\nc")
subset.put("F", "So-long, farewell Adure!")
data.put("C", subset);

用这个

DataSet data = new DataSet()
	.put("A", 1)
	.put("B", new ValueArray()
		.add("One")
		.add(2)
		.add(9876543210L)
		.add(3.4)
		.add(false)
		.add(new ValueArray(1,2))
		.add(new DataSet().put("key","value"))
		.add(new java.util.Date())
	)
	.put("C", new DataSet()
		.put("D", null)
		.put("E", "1\\2#3-4\"5{6}7?8$9@a%b\nc")
		.put("F", "So-long, farewell Adure!")
	);

我知道我更喜欢哪种!

关注点

我在编写的东西中经常使用这些类。这篇文章的目的是将来不必再重新审视这一点。对于那些感兴趣的人,下一篇文章应该是一个使用 DataSet 查找变量名的表达式求值器。

历史 

  • 2013-03 - 提交到 CodeProject 的初始版本。
  • 2013-03-13 - 更新代码,修复了 DataItemtoString() 中的错误。
  • 2013-07-11 - 更新代码,修复了 SealedDataItem 中的 get(key) 中的错误。
  • 2015-03-10 - 添加了对值数组的支持以及必要的重构。
© . All rights reserved.