从与具有软实时性能约束(有时是怪物脂肪类)的研发人员一起工作的经验来看,我建议不要使用 OR 映射器。在这种情况下,您最好处理“接触金属”并直接使用 JDBC 结果集。这是我对具有软实时约束和每个包大量数据项的应用程序的建议。更重要的是,如果需要持久化的不同类(不是类实例,而是类定义)的数量很大,并且您的规范中也有内存限制,您还需要避免像 Hibernate 这样的 ORM。
回到你原来的问题:
您似乎遇到的是一个典型的问题:1)将多个数据项映射到一个 OO 模型中,以及 2)这样的多个数据项没有表现出一种好的分组或隔离方式(并且任何分组尝试都倾向于感觉不对。 ) 有时域模型不适合这种聚合,并且想出一种人为的方式这样做通常会导致不能满足所有设计要求和愿望的妥协。
更糟糕的是,OO 模型通常要求/期望您将类中存在的所有项目作为类的字段。这样的类通常没有行为,所以它只是一个struct
-like 构造,又名data envelope
or data shuttle
。
但这种情况引出了以下问题:
您的应用程序是否需要一次读取/写入所有 40、50 多个数据项?
*是否必须始终存在所有数据项?*
我不知道您的问题域的具体情况,但总的来说,我发现我们很少需要一次处理所有数据项。这是关系模型大放异彩的地方,因为您不必一次查询表中的所有行。您只提取您需要的那些作为相关表格/视图的投影。
在我们可能有大量数据项的情况下,但平均而言,通过网络传递的数据项数量少于最大值,您最好使用属性模式。
而不是定义一个包含所有项目的怪物信封类:
// java pseudocode
class envelope
{
field1, field2, field3... field_n;
...
setFields(m1,m2,m3,...m_n){field1=m1; .... };
...
}
定义字典(例如基于地图):
// java pseudocode
public enum EnvelopeField {field1, field2, field3,... field_n);
interface Envelope //package visible
{
// typical map-based read fields.
Object get(EnvelopeField field);
boolean isEmpty();
// new methods similar to existing ones in java.lang.Map, but
// more semantically aligned with envelopes and fields.
Iterator<EnvelopeField> fields();
boolean hasField(EnvelopeField field);
}
// a "marker" interface
// code that only needs to read envelopes must operate on
// these interfaces.
public interface ReadOnlyEnvelope extends Envelope {}
// the read-write version of envelope, notice that
// it inherits from Envelope, but not from ReadOnlyEnvelope.
// this is done to make it difficult (but not impossible
// unfortunately) to "cast-up" a read only envelope into a
// mutable one.
public interface MutableEnvelope extends Envelope
{
Object put(EnvelopeField field);
// to "cast-down" or "narrow" into a read only version type that
// cannot directly be "cast-up" back into a mutable.
ReadOnlyEnvelope readOnly();
}
// the standard interface for map-based envelopes.
public interface MapBasedEnvelope extends
Map<EnvelopeField,java.lang.Object>
MutableEnvelope
{
}
// package visible, not public
class EnvelopeImpl extends HashMap<EnvelopeField,java.lang.Object>
implements MapBasedEnvelope, ReadOnlyEnvelope
{
// get, put, isEmpty are automatically inherited from HashMap
...
public Iterator<EnvelopeField> fields(){ return this.keySet().iterator(); }
public boolean hasField(EnvelopeField field){ return this.containsKey(field); }
// the typecast is redundant, but it makes the intention obvious in code.
public ReadOnlyEnvelope readOnly(){ return (ReadOnlyEnvelope)this; }
}
public class final EnvelopeFactory
{
static public MapBasedEnvelope new(){ return new EnvelopeImpl(); }
}
无需设置read-only
内部标志。您需要做的就是将信封实例向下转换为Envelope
实例(仅提供 getter)。
期望读取的代码应该在只读信封上运行,而期望更改字段的代码应该在可变信封上运行。实际实例的创建将在工厂中划分。
也就是说,您使用编译器通过建立一些代码约定、管理在何处以及如何使用哪些接口的规则来强制事物为只读(或允许事物是可变的)。
您可以将代码分层为需要编写的部分,与只需要阅读的代码分开。一旦完成,简单的代码审查(甚至 grep)就可以识别出使用错误接口的代码。)
问题:
非公开父接口:
Envelope
未声明为公共接口,以防止错误/恶意代码将只读信封转换为基本信封,然后再转换为可变信封。预期的流程是从可变到只读的 - 它不是双向的。
这里的问题是扩展Envelope
仅限于包含它的包。这是否是一个问题将取决于特定的域和预期用途。
工厂:
问题是工厂可能(并且很可能会)非常复杂。再次,野兽的本性。
验证:
这种方法引入的另一个问题是,现在您必须担心期望字段 X 存在的代码。拥有原始的怪物信封类可以让您部分摆脱这种担忧,因为至少在语法上,所有字段都在那里......
...无论字段是否设置,这是我提议的新模型仍然存在的另一件事。
因此,如果您有希望看到字段 X 的客户端代码,则客户端代码必须在该字段不存在时抛出某种类型的异常(或以某种方式向计算机或读取合理的默认值)。在这种情况下,您将不得不
识别现场存在的模式。期望字段 X 存在的客户端可能与期望其他字段存在的客户端分开(分层)分组。
关联自定义验证器(只读信封接口的代理),它们根据某些规则(以编程方式提供的规则、解释器或规则引擎)抛出异常或计算缺失字段的默认值。
缺乏打字:
这可能值得商榷,但过去使用静态类型的人可能会因为采用松散类型的基于映射的方法而失去静态类型的好处而感到不安。与此相反的论点是,大多数 Web 都采用松散类型的方法,即使在 Java 端(JSTL、EL)也是如此。
抛开问题不谈,在任何给定时间,可能字段的最大数量和平均字段数量越少,这种方法的性能将是最有效的。它增加了额外的代码复杂性,但这就是野兽的本性。
这种复杂性不会消失,要么会出现在你的类模型中,要么会出现在你的验证代码中。不过,序列化和沿线传输效率更高,特别是在您期望大量单独数据传输的情况下。
希望能帮助到你。