Java Map Object,表示为地图的对象

时间:2017-09-28 15:16:32

标签: java

我正在考虑在Java中使用不同的对象表示来提供更大的灵活性。我最大的需求是通过系统动态传递数据,以便在系统中的某个位置使用脚本引擎进行处理。

我的问题是,将对象纯粹表示为Map<String, Object>是否是一个坏主意。例如,如果我要继续使用它,我将来会为自己创造一些严重的问题吗?这样做本身就是一个坏主意,为什么?等...

例如,我可以像这样实现它

public class Foo {
   private final Map<String, Object> data;

   public Foo ()
   {
      this.data = new HashMap(); 
   }

   public String getProperty1() { return this.data.get("property1"); }
   public int getProperty2() { return this.data.get("property2"); }
   public Object getDynamicData(String key) { return this.data.get(key); }

   //Use builder to set properties in data and create the Foo object...
}

我在玩这个游戏时还有其他一些事情,例如使用另一个名为FooFields的对象,该对象包含已知属性和键的类型,使查找更好一些。但是我已经看到了一般性的一些问题。

  • 肯定会增加复杂性与POJO
  • 将对象转换为UI图层或数据库图层更加困难
  • 尽管使用FooFields构建它以给我编译时错误检查仍然存在一些我可以获得运行时错误的情况

另一种解决方案可能是

public class Foo {
   private final String property1;
   private final int property2;
   private final Map<String, Object> dynamicData;

   public Foo (String property1, int property2, Map<String, Object> data)
   {
      this.property1 = property1;
      this.property2 = property2;
      this.dynamicData = data;
   }

   public String getProperty1() { return this.property1; }
   public int getProperty2() { return this.property2; }
   public Object getDynamicData(String key) 
   { 
       return this.dynamicData.get(key); 
   }
}

1 个答案:

答案 0 :(得分:1)

  

我最需要的是在系统中的某个点上动态地通过系统传递数据以使用脚本引擎进行处理……我的问题是,单纯地将对象表示为{{1} }例如,如果我要继续前进,我将来会为自己创造一些严重的问题吗?这样做本质上是一个坏主意,为什么?

我认为这是解决您的问题的完美法律方法,而且对于信息处理问题而言,这比许多其他问题要多得多。将模型信息集映射为它们的信息集。

  • 信息的意思是“有意义的数据”(即“形式”在脑海中)。一个具有有意义名称的键的单个值(在映射中)实际上是一条信息。

  • (开放)集合是不同元素(例如键值对)的集合,仅此而已。

首先,商务人士将信息集视为字面上的信息集-通常在电子表格,纸质表格或屏幕中。他们不认为它们是具有“行为”的活呼吸对象,应该以某种方式“与信息紧密地定位在一起”。 (如果您确实认为自己的业务公理或不变量覆盖了您认为值得在有状态对象中实现的信息项,请填充靴子,但是,将相同的不变性同样容易应用于通过地图的无状态对象方法中,而且更容易命名。)

为单个信息项(例如“ customerForename”或“ productId”或“ lineItemQuantity”)查找良好的,基于域名的长期有效名称比查找其长期名称和合同要容易得多商业“实体”。例如,假设您一开始就将信息集命名为“客户”,但现在输入的信息集中既包含客户信息,也包含一些订单信息。您是否应该将信息分解为单独的“客户”和“订单”对象,以期有一天分离能获得回报。从理论上讲,Order具有LineItem对象的列表,但LineItems不应同时包含“ productId”和“ productTitle”以便在其他上下文中可重用,但是我的信息集没有“ productTitle”,因此也许它们应该是可选的吗?我该如何验证?等等等等。...现在,我的信息集分布在三个DTO(也许在不同的包中),只是为了惯用地捕获此单个信息集。建模练习永远无法完全适应不断变化的需求,变得越来越耦合,并且变得越来越难维护-收获很小。

或者,您可以只将Customer和Orders信息填充到与以前仅用于Customer信息的地图相同的地图中-再次使用精心挑选的长期键名来指定地图中保存的新信息。他们在一起-为什么不把他们在一起。如果您需要将它们拆分为单独的地图,请执行此操作。映射(真正的集合)易于拆分,合并,增长和缩小,而不会对它们代表的内容产生精神分裂症-因为与POJO / DTO不同,它们不会对应该包含的内容做出艰难的选择,这会使代码难以生存使用它们。不再要求信息集中的字段应该是不间断的更改,但是尝试删除POJO构造函数的第三个参数,而不会在整个生产和测试代码中引起编译问题。

此外,如果您要从一个信息集转换为另一信息集,请尝试在同一地图中累积数据。毕竟,您不是只是在旧信息的基础上添加新信息吗?如果以后需要分离,可以。在过去,客户保留纸质申请表,并在其后附加任何内部信件/决定(带回形针),而不是替换它。这意味着可以在仅引用其关注的地图中数据的流水线中逐步应用处理规则,并且该地图会慢慢积累更多信息。这样一来,可以轻松地细分和添加(和测试)规则,而不必携带不必要的额外信息,也不必在信息集增长或收缩时随处打破合同。

这不是面向对象的-但这并不是避免它的唯一原因。然而,它是许多其他语言(OO语言或其他语言)中的惯用方法。 Javascript的对象只是映射。 Groovy,Ruby和Python都看到了本机map / hash / dictionary类型结构的普遍使用。 Clojure有意识地将地图(列表和集合,具有统一的“ seq”抽象)包含为通过函数传递的所有信息的无处不在的集合,这是出于这些良好的理由和其他原因。的确,所有这些都是动态语言,而谦虚的java.util.Map可能很难用于此类模式,因为通常需要进行类转换(除非您提前知道地图将始终包含的内容),并且没有optional ()等。更重要的是,您不能再简单地使用IDE自动完成功能来告诉您类中有哪些可用信息(赞!)。尽管如此,回报还是存在的。

这些要点中的大多数都是Clojure社区中精通的主题,但我希望Java社区中也有更多关于这些主题的讨论:)