3

Do forked nodes have to be be joined in the end? And can outgoing fork nodes have guards?

Basically what I'm trying to do is return the change to the customer and continue with the car wash at the same time.

But, maybe there's a better way to do it?

UML State Diagram

Crave
  • 59
  • 4
  • This is a very interesting question. When you ask if “have to be joined”, do you mean that it’s required by UML? Or that it’s required to have a useful model? Could you also provide a lore complete picture of your diagram so that we can see how each forked branch ends? (the part displayed together with your narrative lead to think there’s a problem) – Christophe Mar 28 '20 at 09:27

3 Answers3

1

Do forked nodes have to be be joined in the end?

For me no, this is not mandatory, but the group will end when all the flows and linked elements will end too

can outgoing fork nodes have guards?

You mean outgoing flows, the norm says a fork segment must not have Guards or Trigger

Basically what I'm trying to do is return the change to the customer and continue with the car wash at the same time.

Yes they can be done in parallel, for the wash machine point of view all is done when it return the change and the wash is done too

bruno
  • 32,421
  • 7
  • 25
  • 37
  • Alright, so I need to get rid of the guard that says Return Change [Deposit > Required]. Do you happen to know any alternative ways to do this in parallel? It seems like forking isn't a very good option for this and I can't specifically mention anything in the outgoing fork node for returning the change. – Crave Mar 27 '20 at 16:25
  • @Don for the transition on the right you need to add a decision to return or not the money, and you need the fork before – bruno Mar 27 '20 at 16:25
  • Note I said wrongly *transition*, I was meaning *flow* of course – bruno Mar 27 '20 at 18:48
1

Do forked nodes have to be be joined in the end?

It depends. If you want to continue with your split flow then yes. If you do not join them you just have two loose ends continuing independently until each of them terminates the one or other way.

And can outgoing fork nodes have guards?

P. 360 of UML 2.5 states

  • fork_segment_guards

A fork segment must not have Guards or Triggers.

inv: (source.oclIsKindOf(Pseudostate) and source.oclAsType(Pseudostate).kind = PseudostateKind::fork) implies (guard = null and trigger->isEmpty())

(I was wrong with that and fixed this after bruno noticed me about it.)

maybe there's a better way to do it?

Sure. There are many ways to describe things. But except for the superfluous guard that seems to be ok.

Community
  • 1
  • 1
qwerty_so
  • 35,448
  • 8
  • 62
  • 86
  • Please do not say the outgoing flows of a fork can have guard when the norm say explicitly the contrary. Also it is not mandatory for the flows outgoing from a fork to reach a join (note the OP says "Do forked nodes **have to be** be joined in the end?") – bruno Mar 27 '20 at 18:46
  • @bruno Hmmm. The OCL seems to be more clear: inv: (source.oclIsKindOf(Pseudostate) and source.oclAsType(Pseudostate).kind = PseudostateKind::fork) implies (guard = null and trigger->isEmpty()) so you are obviously right. – qwerty_so Mar 27 '20 at 21:05
  • the norm, always the norm :-) – bruno Mar 27 '20 at 21:06
  • Sure it is. In this case the textual description was more a bit abnormal ;-) – qwerty_so Mar 27 '20 at 21:10
0

In short

It is not written black on white, but if you fork you will have some constraints that will sooner or later require you to join in practice. There is only one valid construct that let you avoid the join in practice.

In more details

It is an indirect consequence of the concurrent flow of tokens and the rules for ending the flow.

According to UML 2.5 specifications, a flow-final has the following behavior:

A FlowFinalNode is a FinalNode that terminates a flow. All tokens accepted by a FlowFinalNode are destroyed. This has no effect on other flows in the Activity.

You can use it to absorb the tokens on one of the concurrent flows, without impact on the other branches.

Similarly, the activity-final obeys the following principle:

An ActivityFinalNode is a FinalNode that stops all flows in an Activity. A token reaching an ActivityFinalNode owned by an Activity terminates the execution of that Activity.

But there is no rule that defines the speed of each flows, nor that each flow will perform all the expected activities if you do not have a join. This is the problem of the missing synchronisation.

Imagine that you have two forked flows that remain independent: each flow will then have to end somehow. If you have no join:

If an Activity owns more than one ActivityFinalNode, then the first one to accept a token (if any) terminates the execution of the Activity, including the execution of any other ActivityFinalNodes.

So the first flow that reaches the activity-final will interrupt the ongoing activity on the other flows, leaving the performance of the other flows incomplete.

The only way to ensure that both branches would end correctly without interruption, would therefore to have a flow-final on each branch.

If one of the flow cycles you have the problem of the multiplication of tokens du to the concurrent flows, that are reinjected in the circuit (which correspond to concurrent execution).

The only other way to avoid a join node, is to use an implicit join:

An ExecutableNode shall not execute until all incoming ControlFlows (if any) are offering tokens. That is, there is an implicit join on the incoming Control Flows. Specific kinds of ExecutableNodes may have additional prerequisites that must be satisfied before the node can execute.

But implicit or explicit, it's a join ;-)

Christophe
  • 68,716
  • 7
  • 72
  • 138
  • _you will have some constraints_ is not correct. You can end both paths independend of each other by hitting a flow final or an action without outgoing flow. Absolutely legal and valid. – qwerty_so Mar 27 '20 at 21:13
  • @qwerty_so what I say is not about legality but about practical meaning. The fact that the only way to have a safe result is to have a flow-final (and not an activity-final) on both sides, limits considerably the usefulness of an unjoined fork. And OP seem to use this construct in a loop. Which means that the final flow for the normal case might interrupt the forked one, or that token reinjection causes several execution of the yellow activity to occur at the same time. Legal perhaps, useful, I doubt. – Christophe Mar 27 '20 at 21:23
  • Usually I also join a fork. But we can not speculate about what he intends. And there are enough reasons why you don't need/want a join. The constraints you mention happen often but they are not enforced in no way... – qwerty_so Mar 27 '20 at 22:10
  • @qwerty_so good. So we agree: in practice most of the time the join is needed when modeling something useful, even if in theory it’s not required. Byw, OP doesn’t ask about enforcement or the specs. So practical aspects matter and I just would like to make sure that OP gets the full picture. What OP tries to achieve is wrong without a join (it’s not speculation: the part of the graph+ narrative makes this clear). – Christophe Mar 27 '20 at 22:35