Doesn't it make sense for the interaction algorithm code to live in the context?

138 views
Skip to first unread message

Trygve Reenskaug

unread,
Jan 20, 2012, 11:48:36 AM1/20/12
to DCI-object-composition
Once in a while, this question turns up:
"doesn't it make sense for the interaction algorithm code to live in the context?"
Good question. It is really a design decision. It is always possible to centralize the interaction code by rewriting the role methods to become instance methods in the Context. The roles are instance variables in the Context. The method names may have to be changed to avoid name clashes, and any occurrence of 'self' in the code must be replaced by the corresponding role name.

I guess a centralized architecture may be OK in simple cases, but the decentralized (DCI) architecture leads to more sophisticated mental model and code.

It is an interesting exercise to rewrite existing DCI examples with centralized solutions. Below, I have rewritten the three role methods in my Squeak Dijkstra program. ( http://fulloo.info/Examples/SqueakExamples/BB7Dijkstra/BB7Dijkstra.html)

The CurrentIntersection, EastNeighbor and SouthNeightbor  role methods are turned into three context instance methods:
computeDistances  
    " This method computes the final distance for the node named the CurrentIntersection. "
    EastNeighbor  ifNotNil:
        [EastNeighbor recomputeTentativeDistanceEast].
    SouthNeighbor ifNotNil:
        [SouthNeighbor recomputeTentativeDistanceSouth].
    Unvisited remove: CurrentIntersection.
    Unvisited ifNotEmpty: [CurrentContext recur].
recomputeTentativeDistanceEast  
    | oldDistance newDistance |
    oldDistance := (DistanceDict at: EastNeighbor ) distance.
    newDistance := (DistanceDict at: CurrentIntersection) distance
                         + (CurrentIntersection distanceTo: EastNeighbor ).
    newDistance < oldDistance
    ifTrue:
        [DistanceDict
            at: EastNeighbor
            put: (BB7TentativeDistance new
                    distance: newDistance previousNode: CurrentIntersection)].
recomputeTentativeDistanceSouth  
    | oldDistance newDistance |
    oldDistance := (DistanceDict at:
SouthNeighbor ) distance.
    newDistance := (DistanceDict at: CurrentIntersection) distance
                         + (CurrentIntersection distanceTo:
SouthNeighbor).
    newDistance < oldDistance
    ifTrue:
        [DistanceDict
            at:
SouthNeighbor
            put: (BB7TentativeDistance new
                    distance: newDistance previousNode: CurrentIntersection)].
 Compare to the distributed version in
    http://fulloo.info/Examples/SqueakExamples/BB7Dijkstra/BB7Dijkstra.html
" Methodful Role CurrentIntersection " and " Methodful Role EastNeighbor " and " Methodful Role SouthNeighbor "

A difference between procedure orientation and object orientation is that in the former, we ask: "What happens?". In the latter, we ask: "Who does what?". Even in the above extremely simple example, a reader looses the "who" and thereby important locality context that is essential for building a mental model of the algorithm.

--Trygve


On 2012.01.20 14:53, James O. Coplien wrote:


Begin forwarded message:

From: Scrum Foundation <nor...@scrumfoundation.com>
Subject: [Scrum Foundation] Contact request for James O. Coplien
Date: January 20, 2012 2:47:50 GMT+01:00
To: "James O. Coplien" <co...@gertrudandcope.com>
Reply-To: Philippe Huibonhoa <phuib...@gmail.com>

The following message was submitted for you through the online contact form
at scrumfoundation.com by Philippe Huibonhoa (phuib...@gmail.com):

Hi James,

I'm really amazed at how powerful the DCI paradigm is.  Thank you for the incredible amount of work and thought you've invested in it!

I'd really like to use DCI more, specifically in Ruby on Rails.  In fact, I want to write a gem that will help encapsulate and promote DCI usage within Ruby and Rails, but there's a sticking point that I'd like to pick your brain on.

Since the context knows about all the roles, doesn't it make sense for the algorithm code to live in the context?  In my mind, it makes more sense for roles to only be concerned with playing their part and not worry (or know) of the other roles in the context; this way you don't need to give roles access to a global context variable.  The context then would be responsible for ensuring each role does their part to execute the use case.

Here's the bank transfer example, using sort of a rough idea of the interface I'd like my gem to provide for facilitating DCI.  It might do a better job explaining my point, in that the context's execute method orchestrates all the inter-role functionality, thus roles don't need to know the current context.

https://gist.github.com/4628ec0321a86101e459

Thanks for your time!

-Philippe

--
Sent from http://scrumfoundation.com/contact


--

Trygve Reenskaug       mailto: try...@ifi.uio.no

Morgedalsvn. 5A         http://folk.uio.no/trygver/

N-0378 Oslo               Tel: (+47) 22 49 57 27

Norway

Jim Gay

unread,
Jan 28, 2012, 11:01:52 PM1/28/12
to object-co...@googlegroups.com
I've personally been more attracted to the algorithm living in the
context. I find the intentions more obvious and I've not found that I
can't easily see "who does what?"
In fact, I find that I have to think a lot harder about what exactly
is going on when the role methods refer to the context.

Given that the original post was about Ruby, I want to note that using
a "call" method in place of "execute" is more idiomatic. Some people
seem to be thinking that because initial DCI Ruby examples use
"execute" that in needs to be "execute", but Procs, blocks and methods
all respond to "call" and following this leads to many more options
for DCI.


--
Write intention revealing code #=> http://www.clean-ruby.com

Jim Gay
Saturn Flyer LLC
571-403-0338

James Coplien

unread,
Jan 29, 2012, 9:57:42 AM1/29/12
to object-co...@googlegroups.com
I actually got new insights about this during a Lean Arch course in Amsterdam this week. I think that the natural model is for roles to act like actors in a play, interacting with each other to carry out the work of the system.

Most of the time, putting the algorithm in the context is global procedural thinking: an all-knowing algorithm orchestrating conscribed objects to perform low-level operations on the behalf of the procedure. You can of course do that in DCI and of course can do it through generic interfaces, but it's not DCI.

Once in a while, though, I have a hunch that this context-centric algorithm has a place as one of the attendees pointed out last week. The example that gives me pause is the age-old example of drawing a shape on a window, where each object comes from its corresponding class hierarchy. I conjecture that the draw algorithm doesn't easily decompose in a way that fits naturally in either class or in any corresponding roles, and as such it belongs in the Context. One can re-write Dijkstra that way, too. But these are just ideas in my head and, as usual, I think we need some code to flesh it out. I feel strongly that this is the exceptional case, but I want to make room for it if it's there. Jim, do you have some code to share?

rune funch

unread,
Jan 29, 2012, 2:03:54 PM1/29/12
to object-co...@googlegroups.com
Den 29/01/2012 kl. 15.58 skrev James Coplien <jcop...@gmail.com>:

> The example that gives me pause is the age-old example of drawing a shape on a window

What are the roles in the interaction? And what's the UC/scenario?

-Rune

James Coplien

unread,
Jan 29, 2012, 7:42:32 PM1/29/12
to object-co...@googlegroups.com

On Jan 29, 2012, at 11:03 , rune funch wrote:

> What are the roles in the interaction? And what's the UC/scenario?


Yeah, I know. Much more thought needed here — and some code. The roles are the shape and the drawable. The algorithm takes a bit more imagination. I'm thinking on it.

Christopher Dicely

unread,
Feb 10, 2012, 10:47:06 PM2/10/12
to object-co...@googlegroups.com


On Sunday, January 29, 2012 6:57:42 AM UTC-8, Cope wrote:

Most of the time, putting the algorithm in the context is global procedural thinking: an all-knowing algorithm orchestrating conscribed objects to perform low-level operations on the behalf of the procedure. You can of course do that in DCI and of course can do it through generic interfaces, but it's not DCI.


How can it be "global procedural thinking" when you've broken the system up into defined context and the algorithms are attached to specific contexts? And, if it is "not DCI", I wonder if that's a problem with the context-linked algorithms, or a problem with DCI's fidelity to its own stated goals: most of the papers describing the goals of DCI talk about aligning code with user's mental models and often specifically cite use cases as the concrete representation of the users mental model, but attaching the behavior described in use cases to DCI's Roles (which, at least in the examples I've seen, seem to  usually domain objects that are the recipients of actions in the use cases, rather than actors) divorces the code from the model presented in the use case, since it makes the recipient of an action into an actor. A perfect example is the funds transfer example in http://www.artima.com/articles/dci_vision.html where the use case has two accounts which are equally the recipients of actions, but in order to force the behavior into the "Roles belong to domain objects, and some Role must do the action" model, one of those accounts becomes the actor to which the algorithm that is meant to implement the use case is attached. 

It would seem to align much better with the user's mental model if the algorithm implementing the use case was attached to the Context associated with the use case rather than with an arbitrary Role.


rune funch

unread,
Feb 11, 2012, 12:49:14 AM2/11/12
to object-co...@googlegroups.com
I've asked my self the same question on several occasions but usually asking myself another question changes my view
"if the roles were played by humans, what would they each do?"
In the case of the transfer I believe the Source person would hand the money to the destination person who would store it somewhere. I find it unlikely that a third person would be needed to take the money from the source and hand it to the destination. Wouldn't you agree?

-Rune
--
You received this message because you are subscribed to the Google Groups "object-composition" group.
To view this discussion on the web visit https://groups.google.com/d/msg/object-composition/-/hHGlDRPxQKUJ.
To post to this group, send email to object-co...@googlegroups.com.
To unsubscribe from this group, send email to object-composit...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/object-composition?hl=en.

James Coplien

unread,
Feb 11, 2012, 2:01:53 AM2/11/12
to object-co...@googlegroups.com
On Feb 11, 2012, at 11:47 , Christopher Dicely wrote:

How can it be "global procedural thinking" when you've broken the system up into defined context and the algorithms are attached to specific contexts?

A context is a system. You haven't yet done the allocation of responsibilities to the agents that can carry them out.

And, if it is "not DCI", I wonder if that's a problem with the context-linked algorithms, or a problem with DCI's fidelity to its own stated goals: most of the papers describing the goals of DCI talk about aligning code with user's mental models and often specifically cite use cases as the concrete representation of the users mental model, but attaching the behavior described in use cases to DCI's Roles (which, at least in the examples I've seen, seem to  usually domain objects that are the recipients of actions in the use cases, rather than actors) divorces the code from the model presented in the use case, since it makes the recipient of an action into an actor.

Use cases are described in terms of actors. DCI is pretty faithful to the use case model, and there is an abundance of evidence that roles capture well the mental models. Most folks who have done any kind of serious requirements work (Jacobsson, Wirfs-Brock) come to the same conclusion.

Those who have immersed themselves in the kind of imperative, control-based programming paradigms of course can develop mental models that are this global form. It's just that their evolution sucks over time because they tend not to encapsulate change well under complex interacting requirements.

A perfect example is the funds transfer example in http://www.artima.com/articles/dci_vision.html where the use case has two accounts which are equally the recipients of actions, but in order to force the behavior into the "Roles belong to domain objects, and some Role must do the action" model, one of those accounts becomes the actor to which the algorithm that is meant to implement the use case is attached. 

You are thinking statically. You need to think dynamically in terms of the patterns of evolution of the domain.

It would seem to align much better with the user's mental model if the algorithm implementing the use case was attached to the Context associated with the use case rather than with an arbitrary Role.

I can well guess that it aligns well with your mental model. See above. Then also see Rune's reply.

Trygve Reenskaug

unread,
Feb 11, 2012, 3:11:28 AM2/11/12
to object-co...@googlegroups.com
Rune's metaphor made me think of a group of users and programmers together evolving the user mental model using CRC cards. "everybody, all together, from early on" [Lean Architecture book].  The result is likely to be a distributed object model as illustrated in the attached DCI-distribution.gif. The reason for distribution  is that there is one CRC card for each role (CRC  =  Candidate role, Responsibility, Collaborators as defined by Rebecca Wirfs-Brock).

A similar algorithm could be implemented using the Mediator pattern. This is a centralized solution as illustrated in the attached mediatorCentralization.gif . There is no reason to use the apparatus of the DCI Context; the Mediator pattern can be implemented as a regular object. No problem. There is no right and wrong here, just a more or less effective user mental model. If you, through discussions with the users find that their mental model is centralized, you know how to implement it.  If the result is distributed, DCI gives you the means to implement this more sophisticated model. (We believe that object models are more powerful mental models that are easier to internalize, but that is another story).
-Trygve
mediatorCentralization.gif
DCI-distribution.gif
Reply all
Reply to author
Forward
0 new messages