实现业务数据类型
业务数据类型可以描述为实体对象 (即 knower),该对象基本上负责在问题域中抽象化一些清晰且可明确定义的信息。因此,业务数据类型主要由数据定义,而不是由行为定义,就像控制对象 (即 doer) 一样。由于大多数业务数据类型的规范均基于其属性,因此该业务信息的实现通常侧重于类的属性。
此外,由于业务数据类型是实体对象,并且与控制对象相比通常是轻量化的,因此可在三层 (客户端、服务器和数据库) 体系结构中的应用程序和服务器之间有效地传输信息。
初始化业务属性
业务数据类型属性的初始化是通过隐式代码生成的“初始化”方法完成的。不会为模型化业务数据类型生成任何构造函数。而是为每个模型化构造函数创建工厂,并在工厂内生成实例。然后,将调用具有与工厂匹配的签名的初始化方法。
这些初始化方法在第一次代码生成时为空,并且必须手动实现,原因在于代码生成器当前无法对应该或不应该初始化哪些属性做出任何假设。默认情况下,实现这些方法后,将保留其代码以供后续使用。要模拟 Java 构造函数链接,每个初始化方法在执行其他任何操作之前都应始终调用其父项初始化方法。
业务属性访问器
属性始终作为具有两个访问器的私有字段生成:getter 和 setter 方法,例如 JavaBean 特性。getter 作为无自变量方法生成,该方法返回属性的值。
setter 作为单参数方法生成,该方法将属性设置为自变量的值。根据属性是否受约束,setter 是在带有或不带有 wt.util.WTPropertyVetoException 抛出子句的情况下生成的。此异常扩展了 java.beans.PropertyVetoException 并允许自定义消息。
请注意,这些方法的主体会标记为 "preserve=no"。这会指示代码生成器覆盖方法中的代码。可以通过将此标志设置为“是”来保留 getter 和 setter,但是通常不建议这样做。另一方面,在 Windchill 选项卡上的 "GenerateAccessors" 特性设置为 "False" 的情况下,可以指示代码生成器不为该属性生成 getter 和 setter 方法。
覆盖访问器方法
通常,被覆盖的访问器会执行以下操作:
属性验证 (如示例所示)
迟缓属性初始化
计算的属性访问
以下是一种覆盖访问器方法的示例:
验证业务属性
验证业务数据类型属性在两个不同级别进行处理。最简单且完全代码生成的级别将作为由其 setter 调用的受约束属性的验证方法来实现。用于否决属性设置的机制是可以从 setter 中抛出的 wt.util.WTPropertyVetoException。如果属性未受约束,则会生成 setter,而不会抛出异常的功能。
用于指定字符串或数字属性限制的特性为 LowerLimit 和 UpperLimit。在这两种情况下,所提供的值都将视为文字或常量。这意味着,验证方法是用 "if" 语句生成的代码,这些语句使用 Java 小于或大于运算符分别针对 LowerLimit 或 UpperLimit 特性中的指定值进行测试。以下示例说明了这两个特性的使用以及自动生成的验证代码:
验证一个或多个属性的其他更常规级别可覆盖和实现从 wt.properties WTObject 继承的 "checkAttributes" 方法。在对象最初存储在数据库中之前以及每次对其进行修改之前,都会调用此方法。在这种情况下,抛出的异常是 wt.fc.InvalidAttributeException,而不是 wt.util.WTPropertyVetoException。
实现 checkAttribute 方法
以下示例中所示的 checkAttributes 方法是在本部分前面的覆盖访问器方法示例中所示的 setStatus 方法的替代方法。如果将状况设置为 2,其可以确保提供关闭备注。
checkAttributes 方法由 PersistenceManager 调用,以确保对象状态正确后再将其存储在数据库中。以下是实现 checkAttribute 方法的示例:
业务属性集合
所有关联级别的结构化属性 (即非第一类) 都与存储结构属性的第一类对象放置在同一数据库表中。集合的属性必须是对象可映射的,并且无论基数如何,都将始终以平面结构形式存在于数据库表中。表格的平面度是由以下事实导致:假定三层体系结构中的数据库层是关系数据库,而不是面向对象的数据库。
但是,如果顶层结构化属性的基数是 0..1 或 0..*,并且未将其标记为必需内容,则其在数据库中无效。如果此顶级结构化属性集合了任何其他结构化属性,则必须小心,原因在于嵌套的结构化属性始终有效。
业务属性持久化
如果业务属性被标记为“持久化”,则将其保留为一类对象的数据库表中的一列。否则,其将仅被视为内存中属性,且不会映射到数据库表中的列。
业务属性派生
衍生属性严格生成为 getter 和 setter,而没有模型化派生属性的任何此类命名字段。通常,对衍生属性进行建模的目的是使其在访问时充当计算值。衍生属性的一个副作用是既不可序列化,也不可外部化。这些属性可以是类主体中的实现详细信息,但 Windchill 生成的外部化方法不会将属性识别为现有属性。也就是说,用于读取和写入对象数据的外部化方法是根据对象模型 (而不是实现) 中的已知内容生成的。
如果具有派生属性的对象正在通过跨线传输或在外部存储/检索,则其中的派生属性不存在。在实现过程中,如果在对象已序列化或外部化后处理需要衍生属性,除非调用访问器以重新衍生属性,否则可能会产生错误的结果。
实现业务服务
业务服务可作为一组基于业务服务设计模式的抽象,共同提供问题域的基本行为和处理。业务服务中的主要类别充当对业务对象执行处理的控制对象 (即 doer)。此外,cookie 由业务服务维护,其中保存了每个对象的状态和关键信息。
Cookie 类已集合到由业务服务管理的接口中。例如,PersistInfo 是一个 cookie 类,已集合到 Persistable 接口。任何实现 Persistable 接口的类也将集合 PersistInfo,并且 PersistenceManager 业务服务将管理 PersistInfo 的属性,例如 createStamp 和 modifyStamp。
初始化业务服务
业务服务的设计旨在仅在服务器中作为单态执行。启动服务器时,将读取 wt.properties 文件,以确定将自动启动哪些服务。这些指定的服务将进行构造和静态初始化,服务事件将进行注册,然后服务将启动。wt.properties 文件中 wt.services.service 条目所指定的顺序可控制服务启动的顺序。如果服务启动时需要管理访问权限,则必须创建新的 SessionContext,并将当前用户设置为管理员。在服务启动处理结束时重置会话上下文也同样重要。为确保始终重置会话上下文,应在 "finally" 子句中完成此操作。
业务服务操作
业务服务操作可通过本地或远程方式进行调用。客户端仅进行远程调用。本地调用可以来自客户端或服务器。
业务服务的 Helper 抽象仅包含可在本地调用的方法。这些方法通常用于访问服务的 cookie 信息。协助处理的其他类型的方法也可以在 Helper 中实现。如果 Service 定型为 "RemoteInterface",则 Service 抽象包含可以在服务器上本地调用并可以从客户端远程调用的方法。使用帮助程序中 "service" 的此类模式化和聚合时,所有公共方法均可供使用。业务服务应确保所有外部可用的操作均通过 Helper 或 Service 类来公开。所有其他操作应仅在内部可用,但其他服务使用的实用程序类上的方法可能除外。
通常,在服务器上执行的业务服务操作必须执行数据库操作。要维护数据库完整性、服务独立性和其他服务之间的松散联合,应通过使用事务处理块来保护数据库更改。示例代码的以下部分用作如何实现事务处理块的指南:
否决业务服务事件
当业务服务收到系统中发生的事件的通知时,可以选择否决该事件。这是通过订阅为监听程序并实现 notifyVetoableEvent 方法来完成的。监听程序必须处理事件的数据并确定否决是否适用。如果是这样,则监听程序将抛出适当的异常。否则,监听程序将执行所需的任何处理,并允许事件继续。
业务服务规则
如果适用,业务服务应实现规则以强制执行其操作的一致性和正确性。业务规则的两种基本形式是访问控制和问题域完整性。
访问控制可确定哪些用户可以按照何种方式采取何种行动。在业务服务中显示实现访问控制的强制。例如,在锁定服务中,当请求锁定某个对象时,该服务可保证尝试放置该锁定的主体具有对该对象的修改权限,以便可以将其锁定保留在数据库中,如下所示:
问题域完整性的强制与访问控制稍有不同。在业务服务中维护正确的问题域行为始终应该视为目标,特别是可能必须满足一些特殊要求。例如,在版本控制中时,如果对象已检入,则可再次将其检出。但是,如果对象已检出,则除非存在某些共享和合并规则,否则不能再次将其检出。
业务服务通信
业务服务可以直接通过同步 Java 调用或通过事件相互通信。与另一服务的直接点对点通信是在 Windchill 的所有服务中实现访问控制的方式。这是最基本的通信方法。
基于事件的通信是在服务之间进行通信的更为复杂的方法。服务可在启动时注册其事件并向其他服务的事件订阅监听程序。激发服务事件时,会通知所有已订阅监听程序事件的具体值。事件通知和响应是同步的。因此,这等效于对服务的方法调用,只是引起事件的服务不了解将通知哪些其他服务。如果其中一个已通知服务否决了该事件,则不会通知尚未通知的其他服务。
这对您有帮助吗?