JBPM学设计模式之组合模式

模式简介

        组合模式,将对象组合成树形结构以表示“部分与整体”的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。

        合成模式属于对象的结构模式,合成模式将对象组织到树形的结构中,可以用来描述整体与部分的关系。合成模式可以是客户端将单纯元素和复合元素同等的看待。

        树形结构在各种类型的语言中发挥了巨大的作用,一个基于集成类型的等级结构就是一个树结构;同样一个基于合成的对象的结构也是一个树结构。在编程中我们一般遇到的树结构都是连通的有方向的树形结构。

        有向树结构可以分为三种,从上到下、从下到上、双向的。这三种图中,树的节点和他们之间的关系都是一样的,但是连接他们的关系的方向却是不一样的。

        在由上到下的树图(如下图1-1)中,每一个树枝节点都有箭头指向它的所有子节点,从而客户可以要求每个树枝节点都给出自己所有的子节点,而一个节点却并不知道它的父节点。在这样的树结构中信息可以按照箭头自上向下传播。

跟JBPM学设计模式之组合模式

1-1

        在一个由下向上的树图(如下图1-2)中,每个节点的箭头都指向它的父节点,但是一个父节点却不知道其子节点。信息可以按照箭头所指的方向自下向上传播。

跟JBPM学设计模式之组合模式

1-2

        在一个双向的树图(如下图1-3)中,每一个节点都同时知道它的父节点和子节点。信息可以按照箭头的方向向两个方向传播。

跟JBPM学设计模式之组合模式

1-3

        一个树结构是由两种节点组成的,树枝节点和树叶节点。前者可以包括子节点,后者不能有子节点。所以可以说树枝节点是承载树叶节点的容器。

        组合模式的结构如下图1-4中所示,在图中我们可以看到其涉及到三个角色:

        抽象构件角色(Compnent):这是一个抽象角色,它给参加组合的对象规定共有的接口和默认行为。

        树叶构件角色(Leaf):代表参加组合的树叶对象,树叶对象没有子对象,规定了参加组合的原始对象的行为。

        树枝构件角色(Composite):代表参加组合的有子对象的对象,给出了树枝构件对象的行为.

跟JBPM学设计模式之组合模式

图 1-4

        合成模式可以不提供父对象的管理方法,但是必须提供诸如添加、删除、获取子对象的的管理方法;所以根据所实现的接口的是否提供相应的管理方法分为两种形式,分别称为安全式和透明式。虽然这是模式的实现问题,但是却影响到模式结构的细节。

        透明式组合模式(如下图1-5),在Component里声明所有用来管理子类对象的方法。这样所有的构件类都具有相同的接口。从接口层次看来,树枝对象和树叶对象是没有区别的,客户可以同等的对待所有的对象。但是其缺点就是不够安全,因为树叶节点是不可能有子对象的,因此其管理子对象的方法是没有意义的,但是编译时期不会出错,而只会在运行时期才会出错。

跟JBPM学设计模式之组合模式

1-5

        安全式组合模式(如下图1-6),在Composite里声明所有管理子类对象的方法。这样的做法是安全的做法,因为树叶节点本来根本就没有管理子类对象的方法,因此,如果对树叶对象使用这些方法,程序就会在编译器出错,而不是等到运行时才出错。

跟JBPM学设计模式之组合模式

1-6

JBPM中的组合模式

         JBPM中的活动节点模型具有透明组合模式的特征。我们知道JBPM中的节点有复合类型,也就是可以承载子节点;虽然从业务上来说,只有group类型的节点才能承载子节点,但是从JBPMActivityImpl的模型设计上来看,任何类型的节点都有ActivityImpl承载,自然任何类型的节点都可以承载子节点。具体的结构图如下1-7所示

跟JBPM学设计模式之组合模式

1-7

        抽象构件角色:这里由ActivityCompositeElement共同完成抽象构件角色。前者向客户提供节点相关的业务功能接口,后者提供树叶构件需要具有的管理子类对象的方法接口。

public interface Activity {

  /** the short display name given to this element. */
  String getName();

  /** the list of outgoing transitions.
   * Caution: the actual member is returned.  No copy is made. 
*/
  List<? extends Transition> getOutgoingTransitions();

  /** the default outgoing transition. */
  Transition getDefaultOutgoingTransition();

  /** the first leaving transition with the given name or null of no
   * such leaving transition exists. If the multiple transitions have 
   * the given transition name, the first (in order of {
@link #getOutgoingTransitions()})
   * will be returned.
   *  
   * 
@param transitionName is the name of the transition to take.  A null value will 
   * match the first unnamed transition. 
*/
  Transition getOutgoingTransition(String transitionName);

  /** indicates if a leaving transition with the given transitionName exists. 
   * A null value matches an unnamed transition. 
*/
  boolean hasOutgoingTransition(String transitionName);

  /** indicates if this activity has leaving transitions */
  boolean hasOutgoingTransitions();

  /** the leaving transitions, keyed by transition name.  If a transition with 
   * the same name occurs mutltiple times, the first one is returned.
   * Leaving transitions with a null value for their name are not included 
   * in the map.
   * Beware: the actual member is returned.  No copy is made.  In fact, the 
   * returned map is maintained as a cache.  So updates to the map will 
   * influence subsequent retrievals of outgoing transitions by name. 
*/
  Map<String, ? extends Transition> getOutgoingTransitionsMap();
  
  /** searches for the given transitionName in this activity and then up the 
   * parent chain. Returns null if no such transition is found. 
*/
  Transition findOutgoingTransition(String transitionName);

  /** the list of arriving transitions.
   * Beware: the actual member is returned.  No copy is made. 
*/
  List<? extends Transition> getIncomingTransitions();

  /** indicates if this activity has arriving transitions */
  boolean hasIncomingTransitions();
  
  /** retrieve the parent activity in the composite activity structure.  This is 
   * different from {
@link ObservableElement#getParent()} in that it is restricted 
   * to the parent activities.  It doesn't take into account the process definition. 
*/ 
  Activity getParentActivity();
  
  /** indicates if this processDefinition has activities. */
  boolean hasActivities();

  /** the list of direct composite activities.  Recursively contained 
   * activities are not included in the list. 
   * Beware: the actual member is returned.  No copy is made. 
*/
  List<? extends Activity> getActivities();

  /** indicates if an activity with the given name exists directly in 
   * this element.  Only the direct contained activities are 
   * searched.  No recursive search is made. 
*/
  boolean hasActivity(String activityName);

  /** the first composite activity with the given name or null of no
   * such activity exists. Only the direct contained activities are 
   * searched.  No recursive search is made. 
*/
  Activity getActivity(String activityName);

  /** searches for the given activity in this element recursively, 
   * including this activity and all child activities.  The search 
   * is done depth-first. A null value for activityName matches a activity 
   * without a name. 
*/
  Activity findActivity(String activityName);

  /** the composite activities, keyed by activity name.  If an activity 
   * with the same name occurs mutltiple times, the first in the list
   * is included in the map. Activities with a null value for their name 
   * are not included in the map. 
   * Beware: the actual member is returned.  No copy is made. In fact, the 
   * returned map is maintained as a cache.  So updates to the map will 
   * influence subsequent retrievals of activities by name.
*/
  Map<String, ? extends Activity> getActivitiesMap();

  /** the type of this activity which corresponds to the xml tag */
  String getType();
}

相关文章:

  • 2021-07-25
  • 2021-11-22
  • 2021-12-10
  • 2021-09-30
  • 2021-09-14
猜你喜欢
  • 2021-05-25
  • 2021-12-04
  • 2021-10-24
  • 2021-09-29
相关资源
相似解决方案