(1) When setting up a TransferMoneyContext to transfer money from
account A to account B, why would you inject the logic for doing this
into the account A role, and then ask account A to do it for you? (Or
account B, for that matter?)
It seems to me that in this simple example, the TransferMoneyContext
could orchestrate the transfer itself - in which case it would just be
a 'controller'. The only difference I can see is in how the
participants are named. The TransferMoneyContext knows about 'self',
'source_account', 'sink_account' and 'amount'. If the code runs inside
account A then it sees 'context', 'self', 'context.sink_account' and
'context.amount' respectively.
So I'm sure I'm missing some of the bigger picture here. Can you point
me to a larger example which shows the benefits of injecting the
procedural code into the object(s) themselves rather than talking to
them from outside?
(2) At the end of the presentation, the point is made that an Account
isn't really an object at all, but it's a role. However all the code
shown so far certainly *looks* like Account is an object (such as
"@balance += amount" for adding to the balance)
Does this statement invalidate the previous code examples? If so, what
should an Account role *really* look like in code? Does the
TransferMoneyContext still call Account.find(source_id) to find the
relevant account role? Is this object likely to be stateless?
(3) ISTM that composing stateless objects is little more than function
composition. Is there any truth in that, and are there any materials
which talk about DCI from a view of functional programming?
Thanks,
Brian.
After watching http://architects.dzone.com/videos/dci-architecture-coplien
- which was very interesting by the way - I'm going to ask a couple of
naive questions if that's OK.
(1) When setting up a TransferMoneyContext to transfer money from
account A to account B, why would you inject the logic for doing this
into the account A role, and then ask account A to do it for you? (Or
account B, for that matter?)
It seems to me that in this simple example, the TransferMoneyContext
could orchestrate the transfer itself - in which case it would just be
a 'controller'. The only difference I can see is in how the
participants are named. The TransferMoneyContext knows about 'self',
'source_account', 'sink_account' and 'amount'. If the code runs inside
account A then it sees 'context', 'self', 'context.sink_account' and
'context.amount' respectively.
So I'm sure I'm missing some of the bigger picture here. Can you point
me to a larger example which shows the benefits of injecting the
procedural code into the object(s) themselves rather than talking to
them from outside?
(2) At the end of the presentation, the point is made that an Account
isn't really an object at all, but it's a role. However all the code
shown so far certainly *looks* like Account is an object (such as
"@balance += amount" for adding to the balance)
Does this statement invalidate the previous code examples? If so, what
should an Account role *really* look like in code?
Does the TransferMoneyContext still call Account.find(source_id) to find the
relevant account role? Is this object likely to be stateless?
(3) ISTM that composing stateless objects is little more than function
composition. Is there any truth in that, and are there any materials
which talk about DCI from a view of functional programming?
On Feb 9, 2010, at 5:59 , candlerb wrote:
+++++
So I'm sure I'm missing some of the bigger picture here. Can you point
me to a larger example which shows the benefits of injecting the
procedural code into the object(s) themselves rather than talking to
them from outside?
Trygve, how about your planning example?
This planning example illustrates the combination of MVC with DCA and is my best example for illustrating the combination of these two paradigms.
Trygve Reenskaug������ mailto: try...@ifi.uio.no
Morgedalsvn. 5A ������� http://heim.ifi.uio.no/~trygver
N-0378 Oslo�������������� Tel: (+47) 22 49 57 27
Norway
Thank you. I note the method "reselectObjectsForRoles" is not defined,
and I imagine this comes from BB1Context which is from an earlier part
of the book.
However I can identify what I think are some critical pieces of DCI
code. One is this:
Frontloader>>frontloadFrom: startWeek
AllActivities do: [:act | act earlyStart: nil].
[ Context reselectObjectsForRoles.
Activity notNil
] whileTrue:
[ Activity earlyStart: startWeek.
Predecessors do:
[ :pred |
(pred earlyFinish > Activity earlyStart)
In the PDF, AllActivities, Context, Activity and Predecessors are
underlined to highlight that these are role names (which might not be
clear from a regular listing)
If I understand rightly, after role selection "Activity" and
"Predecessors" must be related objects (the latter being the
predecessors of the former). In reality an individual activity object
doesn't have any notion of its predecessors. Rather, this information
is pulled out from the system's set of activities and dependencies to
fill the Predecessors role. This is also an example of how the roles
in a context may be changing during execution of a single method.
Now, the code for picking an object for the Activity role is clear
(lines 342-348), and so are AllActivities (349-350) and FrontLoader
(351). The Context is presumably inherited. But I can't at the moment
see how the Predecessors role is filled, in such a way that it aligns
with the selected Activity. Have I missed something, or is it possible
that a few lines of listing are missing? I was expecting to see
something like
BB4bFrontloadCtx>>Predecessors
data predecessorsOf Activity
(except in valid Smalltalk of course :-)
Regards,
Brian.
Now, the code for picking an object for the Activity role is clear (lines 342-348), and so are AllActivities (349-350) and FrontLoader (351). The Context is presumably inherited. But I can't at the moment see how the Predecessors role is filled, in such a way that it aligns with the selected Activity. Have I missed something, or is it possible that a few lines of listing are missing? I was expecting to see something like BB4bFrontloadCtx>>Predecessors data predecessorsOf Activity (except in valid Smalltalk of course :-)
CheersRegards, Brian.
Trygve Reenskaug mailto: try...@ifi.uio.no
Morgedalsvn. 5A http://heim.ifi.uio.no/~trygver
N-0378 Oslo Tel: (+47) 22 49 57 27
Norway
Thank you. So if I understand this correctly,
BB4bFrontloadCtx>>Activity
is called once by reselectObjectsForRoles, and is memoized in roleMap,
so that references to Activity within the context get the value last
chosen.
Does the call "self at: #Activity" resolve by looking up #Activity as
a key in the roleMap object? If so, I think I'm getting there :-)
In that case it translates very literally to the following Ruby. Ruby
lets me define the subscripting 'operator' x[y] as a method, which
I've chosen instead of x.at(y)
class Context
def [](key)
@role_map[key]
end
def reselect_objects_for_roles
@role_map = {}
self.class::ROLE_NAMES.each do |rnam|
@role_map[rnam] = send(rnam) rescue nil
end
end
def context
self
end
end
class FrontloaderCtx < Context
ROLE_NAMES =
[:context, :activity, :predecessors, :all_activities, :front_loader]
def initialize(data)
@data = data
end
def activity
@data.all_activities.detect { |act|
act.early_start.nil? && !@data.precessors_of(act).detect { |
pred| pred.early_start.nil? }
}
end
def all_activities
@data.all_activities
end
def predecessors
@data.predecessors_of(self[:activity])
end
def front_loader
@data
end
end
The bits I'm missing now are executeInContext and the inherited
roleStructure method. I'm guessing that it allows a subset of this
roleMap to be given to the role objects, so that their communication
with other objects is on a "need to know" basis. Is that more or less
right?
A few observations from the above code to check my understanding and
my translation.
(1) It looks like there is a hidden dependency here: the 'activity'
role must be filled before the 'predecessors' role, otherwise the
'predecessors' role-selection method will fail. That is, the ordering
of roleNames is critical to correct behaviour.
(2) It seems slightly odd to me that 'data' (the instance of the data
model) is just an instance variable of the FrontloaderCtx, whereas the
other objects are roles in the roleMap. This means that there is a
design decision to be made, for each participating object, whether to
make it a role or not.
(3) Furthermore, the frontLoader role is actually played by 'data' -
lines 356-357. I guess you need *some* object to play the role of the
front loader, and maybe it's convenient for the model data to do this,
rather than instantiating a fresh object.
The references to other role objects are injected dynamically in line
332, but it looks like the frontLoader role methods are injected
statically in line 237. Is that true? Was that done just because of
implementation limitations? I'd have thought you would want to avoid
polluting the model with methods from the front loader algorithm if at
all possible.
Thanks again for your patience,
Brian.
On Feb 16, 9:47 am, Trygve Reenskaug <tryg...@ifi.uio.no> wrote:You are right in assuming that /reselectObjectsForRoles /is is in the superclass: 1. *BB1Context>>reselectObjectsForRoles* 2. | messName | 3. roleMap := IdentityDictionary new. 4. self class roleNames 5. do: [:rNam | 6. messName := rNam asString asSymbol. 7. self roleMap 8. at: messName 9. put: (self perform: messName ifNotUnderstood: [nil])].Thank you. So if I understand this correctly, BB4bFrontloadCtx>>Activity is called once by reselectObjectsForRoles, and is memoized in roleMap, so that references to Activity within the context get the value last chosen.
Does the call "self at: #Activity" resolve by looking up #Activity as a key in the roleMap object? If so, I think I'm getting there :-)
A few observations from the above code to check my understanding and my translation. (1) It looks like there is a hidden dependency here: the 'activity' role must be filled before the 'predecessors' role, otherwise the 'predecessors' role-selection method will fail. That is, the ordering of roleNames is critical to correct behaviour.
(2) It seems slightly odd to me that 'data' (the instance of the data model) is just an instance variable of the FrontloaderCtx, whereas the other objects are roles in the roleMap. This means that there is a design decision to be made, for each participating object, whether to make it a role or not.
(3) Furthermore, the frontLoader role is actually played by 'data' - lines 356-357. I guess you need *some* object to play the role of the front loader, and maybe it's convenient for the model data to do this, rather than instantiating a fresh object.
The references to other role objects are injected dynamically in line 332, but it looks like the frontLoader role methods are injected statically in line 237. Is that true? Was that done just because of implementation limitations? I'd have thought you would want to avoid polluting the model with methods from the front loader algorithm if at all possible.
Thanks again for your patience, Brian.
> See my posting of 20 January. Or, if you're a domain expert in banking, try it out yourself!
I searched for this post, but only saw ...Source role code. I would
love to see an example of Account as Context.
> > Does the TransferMoneyContext still call Account.find(source_id) to find the
> > relevant account role? Is this object likely to be stateless?
>
> Yes and yes!
So is Account an alias here for a Context object? If so, wouldn't it
be called something like "WorkWithBalanceContext" (the use case from
the TransferMoneyContext perspective)? How do you keep it in domain
terms at this level (which seems intuitively to want to relate to an
account from the customer perspective)?
Sean