One object, many roles... identifier naming

53 views
Skip to first unread message

Ant Kutschera

unread,
Oct 3, 2011, 2:24:19 PM10/3/11
to object-composition
Hi all,

In the context of identifying roles, I have started to convert my code
into the new syntax for Java.

@James: I am still waiting for you to provide hints on how to write
the use case, or even write it if you feel so inclined ;-)

I have a question about how you would write code for the following
case, where in a context, an object plays several roles. The question
is basically "do I create an identifier for each role, or one for all
roles"?

Take my Till example. There is a basket object, which is a list of
products (and related to the physical thing people put stuff into when
carrying it to the checkout), and I have a Till object which contains
data like the till number, cash balance, error messages, etc. Both are
domain objects which have no behaviour.

I have the (by now well known) roles "SalesLog" and "Printer".

I want to do the role binding in my context constructor. I can think
of two ways to do it:

----- Choice 1: identifier per role -----

//class attributes:
private Till printer;
private Till salesOrderDatabase;
private Till salesLog;
private Basket basket;

public SalesContext(Till till, Basket basket){
bind(till, Printer.class);
bind(till, SalesOrderDatabase.class);
bind(till, SalesLog.class);

this.printer = basket;
this.salesOrderDatabase = basket;
this.salesLog = basket;
this.basket = basket;
}

Here, I have created one identifier per role, because it makes the
following code from the execution method more easy to read:

printer.preparePrint(basket);

salesLog.createAccountingRecords();

But all those identifiers point to the same object - the original
till, with injected methods. So it's a little weird...

----- Choice 2: one identifier per object -----

//class attributes:
private Till till;
private Basket basket;

public SalesContext(Till till, Basket basket){
bind(till, Printer.class);
bind(till, SalesOrderDatabase.class);
bind(till, SalesLog.class);

this.till = till;
this.basket = basket;
}

Here, I have created one identifier per object, rather than per role.
But now I am not sure if the following code is as easy to read:

till.printReceipt(basket);

till.createAccountingRecords();

----------

I feel a little like the first listing is better, but perhaps only
because I come from the service oriented world.

In the OO world I would have created a SalesLog class and a Printer
class. I would have used them to compose a Till, and the Till class
and all its associated classes would have been a massive composition.
In DCI, I have taken the behaviours out of such objects and put them
into roles in the relevant contexts. As such, in the OO world, I
would be expecting to call "printer.printReceipt(basket)", rather than
"till.printReceipt(basket)". Or perhaps, I would probably do this:
"till.getPrinter().printReceipt(basket)", which is almost a
combination of 1 & 2...

On the other hand, if I close my eyes and think of a modern till which
contains a hard disk where accounting records can be written, and a
printer is built in, then I can easily say "in the context of selling
stuff, the till is a printer and it is a sales log". I have passed
the "is a" test.

So with that image in my mind, it makes sense to call
"till.printReceipt(basket)", because the till contains a printer and
it can print stuff, like the contents of the basket.

Which of those two do you prefer, or is there a different way to do
it?

In the second example, is the identifier "till" a good one? Or would
"superDuperHighlyFunctionalKickAssModernTill" be better?

Thanks for your thoughts,
Ant

Ant Kutschera

unread,
Oct 4, 2011, 12:22:57 PM10/4/11
to object-composition
No one want to comment?

rune funch

unread,
Oct 4, 2011, 12:31:54 PM10/4/11
to object-co...@googlegroups.com
I don't see anything wrong in having the same object playing multiple
roles at the same time. Always having the same object playing multiple
roles does feel like a design flaw to me

-Rune

> --
> You received this message because you are subscribed to the Google Groups "object-composition" group.
> 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.
>

Ant Kutschera

unread,
Oct 4, 2011, 1:51:16 PM10/4/11
to object-composition
I've read that four times and it seems to contradict itself.

Cut to the chase: which is better, listing 1 or 2?

rune funch

unread,
Oct 4, 2011, 2:04:10 PM10/4/11
to object-co...@googlegroups.com
Difference is the first word of the second sentence "always". I don't
like either the first for the reason I just wrote the second for
reasons described elsewhere which leads me to think of the till and
basket being in the center of the solution

Mvh
Rune

Ant Kutschera

unread,
Oct 4, 2011, 2:20:42 PM10/4/11
to object-composition
Aha, yes, I agree - the till playing all those roles does feel like I
have a god-object...

One solution would be for you to propose the use case and help me
identify the roles and role players based on the use case. You have
the user story ;-)

I can't be blamed if no one else can come up with a better design
after all.

On Oct 4, 8:04 pm, rune funch <funchsolt...@gmail.com> wrote:
> Difference is the first word of the second sentence "always". I don't
> like either the first for the reason I just wrote the second for
> reasons described elsewhere which leads me to think of the till and
> basket being in the center of the solution
>
> Mvh
> Rune
>

Ant Kutschera

unread,
Oct 4, 2011, 2:33:35 PM10/4/11
to object-composition
PS. there is a good reason I keep bringing up this Till. It isn't
because I am building one for a customer. It is a purely academic
exercise (well, as academic as a non-academic doing an academic
exercise can be bothered to make it).

The reason I am so interested in this Till is that if DCI makes it
hard to design the system in the first place, when compared to OO or
SOA, then DCI might have a problem. And telling people that "they are
thinking in classes not objects" won't cut it. People need to be able
to design and implement their mental models without butchering those
models to make them DCI conform. I am willing to think that it is
just me who is having problems, but the fact that no one else is
really helping me to come up with good reasons why certain objects are
role players and certain "things" are good roles is currently
suggesting to me that they too are having problems with designing
something the correct way in DCI. Sound harsh? Then I challenge you
to come up with a suitably justified design :-)

rune funch

unread,
Oct 4, 2011, 2:38:29 PM10/4/11
to object-co...@googlegroups.com
Den 04/10/2011 kl. 20.33 skrev Ant Kutschera <ant.ku...@gmail.com>:

> suggesting to me that they too are having problems

or not motivated enough to invest time in it:)

Ant Kutschera

unread,
Oct 4, 2011, 3:13:03 PM10/4/11
to object-composition
On Oct 4, 8:38 pm, rune funch <funchsolt...@gmail.com> wrote:
> or not motivated enough to invest time in it:)

Not motivated in showing how great DCI is? I am surprised.

OK..

One beer token* to the first person who provides a use case which
James signs off as acceptable.
And another one to the first person to provide a good design based on
that use case, with justifications about role players and roles.

* Tea / Coffee / Cake vouchers are also available if desired. That
might also work out better for me, because beer costs a lot up in
Scandinavia ;-)

James O. Coplien

unread,
Oct 5, 2011, 4:19:39 AM10/5/11
to object-co...@googlegroups.com

On Oct 4, 2011, at 6:22 , Ant Kutschera wrote:

>> @James: I am still waiting for you to provide hints on how to write
>> the use case, or even write it if you feel so inclined ;-)


After you posted your initial code, there were some follow-on comments that I thought might move you to change your code. I was waiting for that to settle down before jumping in to do a use case.

Wenig, Stefan

unread,
Oct 5, 2011, 7:14:15 AM10/5/11
to object-co...@googlegroups.com
Reentrancy problems maybe, like an object getting a callback while it's in an inconvenient state (we had this discussion a few weeks ago)

It's by far not as likely in this scenario, because
a) role methods can't mess up the object's internal state, and
b) role player methods can, but they will probably (definitely?) complete before control goes back to a role method.

But still, conflicts are not beyond imagination.

class Role1
{
A()
{
oldValue = this.RolePlayerProperty1;
this.RolePlayerProperty1 = newValue;

Role2.B(); // written assuming that Role2 is another object
this.RolePlayerProperty1 = oldValue;
}
}

class Role2
{
B ()
{
this.RolePlayerProperty1 = someOtherValue;
}
}

Yes, I know, it's far-fetched. Another trap could be a role player method like BeginXxx() that exits in an inconsistent state, to be paired by an EndXxxx() method.

Stefan


On Di, Okt 04, 2011 at 18:31:54, rune funch wrote:
> Subject: Re: One object, many roles... identifier naming

Ant Kutschera

unread,
Oct 5, 2011, 7:23:08 AM10/5/11
to object-composition
I gave up on the code, having decided it would be better to do the
redesign first. I can only think of Rune's suggestion to make a role
for Product - is that what you are talking about?

One thing I did manage to do was to convince myself that a Printer
(role) "is a" (special type of) Till. Although when I write that now,
it sounds wrong again. It's more like a till is a special type of
printer! Doh...

So as you see, I really do need some help.

Mikey B

unread,
Oct 5, 2011, 7:54:59 AM10/5/11
to object-co...@googlegroups.com

Is ToPrint not a system operation (context) which uses two data objects of what needs printing and the printerspool?

Daatobjects
Printerspool
Document

Context
Print

?

Ant Kutschera

unread,
Oct 5, 2011, 9:11:00 AM10/5/11
to object-composition
What's a role method compared to a role player method!? And do you
mean they can only change state using an objects normal methods?

Ant Kutschera

unread,
Oct 5, 2011, 9:14:51 AM10/5/11
to object-composition
How did you find those objects (they aren't in the user story) and
what are the roles and why?

On Oct 5, 1:54 pm, Mikey B <mikey...@gmail.com> wrote:
> Is ToPrint not a system operation (context) which uses two data objects of
> what needs printing and the printerspool?
>
> Daatobjects
> Printerspool
> Document
>
> Context
> Print
>
> ?

James O. Coplien

unread,
Oct 5, 2011, 9:25:29 AM10/5/11
to object-co...@googlegroups.com
Roles don't have state. oldValue is state. This is not a role. QED.

:-)

Wenig, Stefan

unread,
Oct 6, 2011, 4:00:29 AM10/6/11
to object-co...@googlegroups.com
Sorry, ambiguous pseudocode. oldValue is supposed to be a local variable in a role method.

Gesendet von meinem HTC

----- Ursprüngliche Nachricht -----
Von: James O. Coplien <jcop...@gmail.com>
Gesendet: Mittwoch, 5. Oktober 2011 15:25
An: object-co...@googlegroups.com <object-co...@googlegroups.com>
Betreff: Re: One object, many roles... identifier naming

:-)

--

James O. Coplien

unread,
Oct 6, 2011, 9:18:07 AM10/6/11
to object-co...@googlegroups.com
The object should be responsible for its own re-entrancy — not the role that the object is playing. If it is, then there is no problem.

Hopefully you are using methods to set things like RolePlayerProperty1. Anyone who directly accesses member data of a subclass or superclass is a doofus.

rune funch

unread,
Oct 6, 2011, 9:22:11 AM10/6/11
to object-co...@googlegroups.com
A property in C# is by definition a field with a getter and/or setter
method. Since Stefan is from C# I guess we can assume property to have
the C# semantics

Mvh
Rune

Wenig, Stefan

unread,
Oct 6, 2011, 1:50:44 PM10/6/11
to object-co...@googlegroups.com
Right, but that discussion is just diverting from the point I'm trying to make. Picking up the reentrancy discussion Jim started a while ago, I tried to find a scenario where it would be a problem if an object plays two roles, but the role's coders/reviewers didn't consider that situation. Just like a reviewer would not see a reentrancy caused by a callback.

Like I said, it's much less likely for roles than for objects, but it seems to be there.

> -----Original Message-----
> From: object-co...@googlegroups.com [mailto:object-
> compo...@googlegroups.com] On Behalf Of rune funch
> Sent: Thursday, October 06, 2011 3:22 PM
> To: object-co...@googlegroups.com
> Subject: Re: AW: Re: One object, many roles... identifier naming
>
> A property in C# is by definition a field with a getter and/or setter
> method. Since Stefan is from C# I guess we can assume property to have
> the C# semantics
>
> Mvh
> Rune
>
> Den 06/10/2011 kl. 15.19 skrev "James O. Coplien" <jcop...@gmail.com>:
>

> > The object should be responsible for its own re-entrancy - not the


> role that the object is playing. If it is, then there is no problem.
> >
> > Hopefully you are using methods to set things like
> RolePlayerProperty1. Anyone who directly accesses member data of a
> subclass or superclass is a doofus.
> >
> >
> > On Oct 6, 2011, at 10:00 , Wenig, Stefan wrote:
> >
> >> Sorry, ambiguous pseudocode. oldValue is supposed to be a local
> variable in a role method.
> >>
> >> Gesendet von meinem HTC
> >>
> >> ----- Ursprüngliche Nachricht -----
> >> Von: James O. Coplien <jcop...@gmail.com>
> >> Gesendet: Mittwoch, 5. Oktober 2011 15:25
> >> An: object-co...@googlegroups.com <object-

> compo...@googlegroups.com>


> >> Betreff: Re: One object, many roles... identifier naming
> >>
> >>
> >> Roles don't have state. oldValue is state. This is not a role. QED.
> >>
> >> :-)
> >>
> >>
> >> On Oct 5, 2011, at 1:14 , Wenig, Stefan wrote:
> >>
> >>> class Role1
> >>> {
> >>> A()
> >>> {
> >>> oldValue = this.RolePlayerProperty1;
> >>> this.RolePlayerProperty1 = newValue;
> >>>
> >>> Role2.B(); // written assuming that Role2 is another object
> >>> this.RolePlayerProperty1 = oldValue;
> >>> }
> >>> }
> >>
> >> --
> >> You received this message because you are subscribed to the Google
> Groups "object-composition" group.
> >> To post to this group, send email to object-

> compo...@googlegroups.com.


> >> To unsubscribe from this group, send email to object-

> composition...@googlegroups.com.


> >> For more options, visit this group at
> http://groups.google.com/group/object-composition?hl=en.
> >>
> >> --
> >> You received this message because you are subscribed to the Google
> Groups "object-composition" group.
> >> To post to this group, send email to object-

> compo...@googlegroups.com.


> >> To unsubscribe from this group, send email to object-

> composition...@googlegroups.com.


> >> For more options, visit this group at
> http://groups.google.com/group/object-composition?hl=en.
> >>
> >
> > --
> > You received this message because you are subscribed to the Google
> Groups "object-composition" group.
> > To post to this group, send email to object-

> compo...@googlegroups.com.


> > To unsubscribe from this group, send email to object-

> composition...@googlegroups.com.


> > For more options, visit this group at
> http://groups.google.com/group/object-composition?hl=en.
> >
>
> --
> You received this message because you are subscribed to the Google
> Groups "object-composition" group.
> To post to this group, send email to object-

> compo...@googlegroups.com.


> To unsubscribe from this group, send email to object-

> composition...@googlegroups.com.

Ant Kutschera

unread,
Oct 6, 2011, 3:46:25 PM10/6/11
to object-composition
On Oct 6, 7:50 pm, "Wenig, Stefan" <stefan.we...@rubicon.eu> wrote:
> Right, but that discussion is just diverting from the point I'm trying to make.

And that seems to be diverting from my post ;-)

Wenig, Stefan

unread,
Oct 7, 2011, 4:34:50 AM10/7/11
to object-co...@googlegroups.com
Maybe, but while I thought it was worthwhile challenging Rune's assumption that this is a harmless scenario, I don't see the point of discussing field vs. method access here.
It's hard to keep up with even a single thread in this group, so I didn't comment on your original question. I just looked at it a bit though.

I think the problem you're touching here as two parts, and they're both about the SRP.
- on the data side: Why would I create such a large object? I don't see a reason. I think you're separating your system into several classes in your head, just to create a single class in your implementation. Maybe that's really SOA thinking, it makes more sense as a service façade than within an OO system. DCI deals with individual objects, not with entire systems. The context can act as a façade, but that shouldn't show in the context's inner workings.
- on the role side: I can bind a single object to several roles, but usually that's just an indicator that that object has a problem. If I do, it will usually work fine, but I think there's a risk if the various roles affect each other through some object state that's shared between this roles (like that reentrancy scenario)

Stefan

> -----Original Message-----
> From: object-co...@googlegroups.com [mailto:object-

rune funch

unread,
Oct 7, 2011, 4:48:59 AM10/7/11
to object-co...@googlegroups.com
Den 07/10/2011 kl. 10.34 skrev "Wenig, Stefan" <stefan...@rubicon.eu>:

> I thought it was worthwhile challenging Rune's assumption that this is a harmless scenario

Since this commentator is not connected (in writing) to anything I
don't know what you're referring to unfortunately. I guess it's my
comment on an object playing multiple roles at the same time. If
that's the case then I never said I believed it to be harmless only
that I could see scenarios where it makes a lot of sense. May I
suggest that we all keep the text we're commenting on together with
the comment.
In general I don't think the interaction should be concerned with what
object plays what role and if the binding has a sensible scenario
where it picks the same object to play multiple roles then it's by
definition makes sense. Eg. If I have a VCS and I wish to compare line
for line who edited that line in the history of the file. I'm only
interested in two devs work on the LHS of the screen I have all lines
devA have ever edited all other lines are blank and on the RHS I have
the same for devB. If the same object couldn't play two roles at the
same time I'm not matching the mental model of the two views being of
the same file. The use case how ever let me see any two views. Two
different files same dev, two different files and devs and same file
same dev are all valid variations of the use case.

-Rune

James O. Coplien

unread,
Oct 7, 2011, 5:07:26 AM10/7/11
to object-co...@googlegroups.com

On Oct 7, 2011, at 10:48 , rune funch wrote:

> In general I don't think the interaction should be concerned with what object plays what role

This has limited truth as a statement in isolation; the second half of the sentence makes it true.

Just a couple of observations.

First, more precisely: "the interaction should not be concerned with the class of the object that plays a given role." That isn't object thinking.

Second, no object should, in general, be concerned about how many roles it is playing. That really complicates the relationship between what-the-system-is and what-the-system-does. Again, this would be a good research topic, to show what kinds of separations of concerns should be enforced in a class's role extension interface.

Third, no role, in general, should be concerned whether its object is playing another role. There can be corner cases where this is true but I consider it to be bad design: creating entities unnecessarily. And, in any case, the Context needs to deal with this issue more broadly.

Fourth, whether it makes sense for an object to play multiple roles within a given Context is the job of the Context, and in particular of the rebind method.


> and if the binding has a sensible scenario where it picks the same object to play multiple roles then it's by definition makes sense.

Exactly, exactly.

This is strongly analogous to a situation in procedural design. Good procedures are designed to work independent of the context from which they are called. The call graph of a procedural program is not a tree. It is a directed graph, potentially cyclic. The same is true in some degree with role / object bindings. A role is a name for an object, and there's nothing wrong with an object having many names — just as in "real life."

Ant Kutschera

unread,
Oct 7, 2011, 7:21:40 AM10/7/11
to object-composition


On Oct 7, 11:07 am, "James O. Coplien" <jcopl...@gmail.com> wrote:
> A role is a name for an object, and there's nothing wrong with an object having many names — just as in "real life."

ok, so listing 2 is ok. I've slightly modified the syntax of how I
bind roles. it now looks as follows which is very satisfying. it
does role assignment and assigns the object to a field in the same
step. note the first parameter passed to the bind method is simply
returned by the method, so that is is the same object.

this.printer = bind(till, Printer.class);

I think my question on this thread is answered, thanks to all for
participating.

rune funch

unread,
Oct 7, 2011, 8:09:47 AM10/7/11
to object-co...@googlegroups.com


2011/10/7 Ant Kutschera <ant.ku...@gmail.com>



On Oct 7, 11:07 am, "James O. Coplien" <jcopl...@gmail.com> wrote:
> A role is a name for an object, and there's nothing wrong with an object having many names — just as in "real life."

ok, so listing 2 is ok.  

I still find both listings to be smelly.


Den 04/10/2011 kl. 18.23 skrev Ant Kutschera <ant.ku...@gmail.com>:

>>   //class attributes:
>>     private Till till;
>>     private Basket basket;
>>
>>     public SalesContext(Till till, Basket basket){
>>         bind(till, Printer.class);
>>         bind(till, SalesOrderDatabase.class);
>>         bind(till, SalesLog.class);
>>
>>         this.till = till;
>>         this.basket = basket;
>>     }
If it's always the same object you assign to multiple roles you either
don't have multiple roles or you have a (semi)god object. Why? Roles
and their Interactions communicate the connection between objects.
Always assigning the same object to the same set of roles ryles out
that there's ever going to be any inter-object communication along
that edge of the network graph.


>> Here, I have created one identifier per object, rather than per role.
>> But now I am not sure if the following code is as easy to read:
>>
>>     till.printReceipt(basket);
>>
>>     till.createAccountingRecords();

Roles are not another place to store your procedures. In this code not
once do you reference a role name but only the name of an object
you've injected methods into from "roles" or am I missing something? 


if your code looked something like:

 public SalesContext<X,Y,Z>(X printer, Y salesOrderDatabase, Z salesLog){
         this.printer = bind(printer
, Printer.class);
         this.salesOrederDatabase = bind(salesOrderDatabase, SalesOrderDatabase.class);
         this.salesLog = bind(salesLog, SalesLog.class);
}

and you once in a while but not always had code like:
   new SalesContext(till,till,till);

Then I said it's ok but to me the key is that you do expect objects to communicate before you try to capture that communication after all what's there to capture if there's nothing to capture?

-Rune

Ant Kutschera

unread,
Oct 7, 2011, 12:37:47 PM10/7/11
to object-composition
On Oct 7, 2:09 pm, rune funch <funchsolt...@gmail.com> wrote:
> I still find both listings to be smelly.
> If it's always the same object you assign to multiple roles you either
> don't have multiple roles or you have a (semi)god object. Why? Roles
> and their Interactions communicate the connection between objects.
> Always assigning the same object to the same set of roles ryles out
> that there's ever going to be any inter-object communication along
> that edge of the network graph.

In the old world, before the till was built, it was Henry (or what was
his name again?) who interacted with things like the printer and sales
log (a book behind him).

There was plenty of interaction, just between the human and the other
objects, not between the objects.

It's not my fault that the process worked like that - blame Henry...

> Roles are not another place to store your procedures. In this code not
> once do you reference a role name but only the name of an object
> you've injected methods into from "roles" or am I missing something?

Yes, I guess the point is, calling "till.printReceipt()" is wrong - it
should be "printer.printReceipt()".

So basically listing 1 was the better one, but can be improved. Lets
morph it into listing 3:

//context class attributes:
private Till printer;
private Till salesOrderDatabase;
private Till salesLog;
private Basket basket;

public SalesContext(Till till, Basket basket){
this.printer = bind(till, Printer.class);
this.salesOrderDatabase = bind(till,
SalesOrderDatabase.class);
this.salesLog = bind(till, SalesLog.class);
this.basket = basket; //not playing a role, but thats ok, not
everything needs to play a role
}

The in the execution, I can call things like this:

printer.preparePrint(basket); //printing a receipt needs data from
the till and the basket!

salesLog.createAccountingRecords();

> and you once in a while but not always had code like:
>    new SalesContext(till,till,till);

Nah, see my constructor above - no need to pass the same object three
times just because its playing three roles.

> Then I said it's ok but to me the key is that you do expect objects to
> communicate before you try to capture that communication after all what's
> there to capture if there's nothing to capture?

Maybe that is the problem. The only interaction between things in the
old world, was the interactions between Henry and the tools around
him. We are replacing him as the "worker" with the new till. As
such, the interaction is now between the till and the tools around it
(printer, sales log, etc). And let's face it, most business software
is like this. Does that mean DCI is not good for implementing
business software? I can hardly believe that...

I notice you are good at pointing out you don't like the code. Anyone
can do that. But only the best can then go on to show how it should
be done ;-)

Trygve Reenskaug

unread,
Oct 7, 2011, 2:24:34 PM10/7/11
to object-co...@googlegroups.com
Is a real till cast in plastic as one block? Or does it consist of several, interworking component parts? Is it best represented as a single object or as a composite aggregation of its parts. The parts can be modeled as roles? The Printer role can be mapped to a real printer or to a device that writes the same data to the customer's i-phone.

I fail to see why this is difficult. I also fail to see how it can be possible to choose the best model without knowing exactly the purpose of the modeling. May be a Fortran program is an  optimal solution. May be DCI is better. Who knows?
--

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

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

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

Norway

Ant Kutschera

unread,
Oct 7, 2011, 4:01:48 PM10/7/11
to object-composition
On Oct 7, 8:24 pm, Trygve Reenskaug <tryg...@ifi.uio.no> wrote:
> Is a real till cast in plastic as one block? Or does it consist of
> several, interworking component parts? Is it best represented as a
> single object or as a composite aggregation of its parts. The parts can
> be modeled as roles? The Printer role can be mapped to a real printer or
> to a device that writes the same data to the customer's i-phone.

I think that a till can indeed be modelled by its parts. And some of
those parts are indeed roles.

> I fail to see why this is difficult.

I don't think it is. When I first asked about identifying roles, it
was because I was looking in the wrong place (actors on a use case
diagram). Once I worked out that I should also be looking in
collaborations, I found the roles easily.

But the feedback I had here was that my design wasn't good. Yet no
one offered anything better. Rune had an alternative with a role for
the product, but didn't suggest any other roles.

One thing I did was to list all possible objects which came out of the
use cases / user stories.
I created a data model. In the data model, I only had "till",
"basket", "product" and "customer", so those were ideal candidates for
the "data" in DCI.
So I asked myself where the behaviour belonged. Certainly not in
those objects.

The till interacts with other objects like the printer, sales log,
etc. Since those objects have no state*, I decided to make them
roles. Is that the way to identify roles? I'm not convinced it is.
A role name is something drawn on a collaboration-use diagram. In a
collaboration-use diagram, I might have for example the till and the
printer (they collaborate to create a receipt). The role name would
be printer. The behaviour is "printing". But I don't have a domain
object called printer, because I have no state. So the non-existant
printer can't place the role of "printer". The printer must *be* the
role.

The final part of the puzzle was to consider which objects play those
roles. The "is a" test helped me there. But it was not as
satisfactory as I had hoped.

"Is a printer a till"? That's hard to say. But scala has nice
syntax:

thePrinter = new Till() with Printer;

That sounds great. That line adds the trait (behaviour) printer, to
my till. It is casting my till into the printer role.

So I am happy with my code. I see some advantages too, compared to
restricted OO. Imagine more contexts which use the printer: during a
stock take, the lines contain quantity rather than price; during a
product search, the printer prints the location. In restricted OO all
that behaviour would go into either a printer class, or the product
class. If you want to figure out which code is used because of a
certain user action, because they tell you about a bug, you won't know
where to start looking. However, in DCI, I start looking in the roles
for the context. It's easy.

Of course I can see a disadvantage with DCI too - what if I have to
print stuff the same way in two different contexts? The solution
appears to be "habits" (helper classes, for those who don't know the
term "habit"). But that doesn't seem so far off restricted OO. And
there is also the problem in DCI which I described a few days ago,
where I need specialised roles, depending upon specialisations of
data.

* While a "Sales log" has persistent state, it is not state which I am
interested in modelling in the system. By that, I mean that I don't
need it in am MVC model.


> I also fail to see how it can be
> possible to choose the best model without knowing exactly the purpose of
> the modeling. May be a Fortran program is an  optimal solution. May be
> DCI is better. Who knows?

I don't :-) But it would be nice to work out the questions to ask in
order to find out when DCI is good, and when it isn't. Advocates of
restricted OO try to program everything in OO. I wonder if in the
future, DCI fans will try the same thing? Can DCI be applied to as
many scenarios as restricted OO is? (I think yes).

Mikey B

unread,
Oct 8, 2011, 8:44:10 AM10/8/11
to object-co...@googlegroups.com

Hello ant,

Id like to give it a stab at helping you. Though be warned i maybe wrong.

You managed to write down all your objects. Lets try and get them on a stage. Can you think of these objects as people? What are they doing?

Hopefully your see receipt can not be a person. It can be what a person says, or on a persons t-shirt. This is because it is a representation of something. Hopefully the bill object. :)

You can imagine the printer as someone writing down on paper what is being sent to it (or said to it).

Has the customer asked you to "program" a printer? I don't think so from ur posts, so all you need to know is that it receives the receipt.

Mike brown

> --
> You received this message because you are subscribed to the Google Groups "object-composition" group.

> To post to this group, send email to object-co...@googlegroups.com.
> To unsubscribe from this group, send email to object-composit...@googlegroups.com.

rune funch

unread,
Oct 8, 2011, 11:49:07 AM10/8/11
to object-co...@googlegroups.com
I'm confused about the system. Is this a system that runs in a
physical till or is it an online book store?

Mvh
Rune

Mikey B

unread,
Oct 8, 2011, 3:06:56 PM10/8/11
to object-co...@googlegroups.com


On Oct 8, 2011 4:49 PM, "rune funch" <funchs...@gmail.com> wrote:
>
> I'm confused about the system. Is this a system that runs in a
> physical till or is it an online book store?
>
> Mvh
> Rune
>

Why does that matter? Restful web ~= mvc+dci

Mike brown


> Den 07/10/2011 kl. 18.37 skrev Ant Kutschera <ant.ku...@gmail.com>:
>
> > I notice you are good at pointing out you don't like the code.  Anyone
> > can do that.  But only the best can then go on to show how it should
> > be done ;-)
>

> --
> You received this message because you are subscribed to the Google Groups "object-composition" group.

> To post to this group, send email to object-co...@googlegroups.com.
> To unsubscribe from this group, send email to object-composit...@googlegroups.com.

Ant Kutschera

unread,
Oct 8, 2011, 4:54:44 PM10/8/11
to object-composition
On Oct 8, 5:49 pm, rune funch <funchsolt...@gmail.com> wrote:
> I'm confused about the system. Is this a system that runs in a
> physical till or is it an online book store?

You're not the first to ask.

My ficitious client, Nozama Ltd. (strange name hey?) has awarded me a
contract to fit all their stores (over 200) with new tills. The
contract includes purchasing hardware, building the bits together,
putting the software in, installation and a 20 year maintenance
contract (if this weren't ficitious, I'd be sitting by the pool
sipping a cocktail instead of writing this post!).

My hardware team have recommended we use cheap off the shelf PCs
running linux. They are a little ahead of the software development
team, who are still working on the architecture and design. They are
evalutating DCI, as they hear its potentially the best way to reduce
test and maintenance costs.

As such, the hardware will consist of a physical printer, a monitor, a
keyboard, a scanning device and an electronic cash drawer. Oh, and
the client's payment partner will supply card readers for reading e-
payment cards (maestro, visa, mc, etc.). These card readers validate
the PIN, call the clearing centre, get an authorisation ID and make it
available to any software on the PC via a low level call to a shared
library. They can provide .NET or Java libraries (APIs) for
communicating with the card reader.

Like the user story showed, this is about replacing whatever systems
they currently have in place (whether manual or already automated),
and building the system from scratch.

All make sense? Any other questions?

Ant Kutschera

unread,
Oct 8, 2011, 5:50:21 PM10/8/11
to object-composition
On Oct 8, 2:44 pm, Mikey B <mikey...@gmail.com> wrote:
> Id like to give it a stab at helping you.

Thanks :-)

> Has the customer asked you to "program" a printer? I don't think so from ur
> posts, so all you need to know is that it receives the receipt.

The physical printer is called by a shared library (DLL, or SO) and
has a low level API. An abstraction layer above that, the printer
supplier has a library which lets you send "print jobs" to the
printer.

A print job is basically a list or strings and each string is printed
on a line.

These are my current problems:

1) all roles are played by the till, so that I can pass the "is a"
test, albeit modified to be the "with" test. Eg. does this phrase
make sense: "printer = till with printer" - yes, so the role printer
is applicable to the till.

2) there is no interaction, per se. However, when I call a role
method, like "printReceipt()" I pass the basket to that method,
because it needs to print each product on the receipt. Is that
"interaction"? I guess so.

3) Many of my roles never need to call "self" to access data from the
role player (till). That seems really weird. And makes me wonder if
a better role player for "Printer" and "Sales Log" would be the
basket, because it has all the information required to create a
receipt or sales records, namely the list of products. But a printer
"is NOT a" basket. Nor does it make sense in my mental model that
printer is a specialisation of the basket. Nor does "basket with
printer" really make sense, because a basket cannot be hooked up to a
printer.

Here is the latest code:

-------------

package
ch.maxant.till.dci_wrappers.server_sell_items_in_basket_context;

import java.math.BigDecimal;

import ch.maxant.dci.util.BusinessException;
import ch.maxant.till.client.model.Basket;
import ch.maxant.till.client.model.Basket.PaymentType;
import ch.maxant.till.client.model.PrintJob;
import ch.maxant.till.client.model.Result;
import ch.maxant.till.common.data.SalesOrder;
import ch.maxant.till.dci_wrappers.ContextWithLogger;
import ch.maxant.till.dci_wrappers.Till;

/** this is the sales context in which a sale is completed */
public class SellItemsInBasketContext extends
ContextWithLogger<Result> {

private final Basket basket;
private final Till till;
private final Till salesLog;
private final Till salesOrderDatabase;
private final Till printer;
private final Till paymentPartner;

public SellItemsInBasketContext(Till till, Basket basket) {
this.till = till;
this.basket = basket;
this.printer = bind(till, Printer.class);
this.salesOrderDatabase = bind(till, SalesOrderDatabase.class);
this.salesLog = bind(till, SalesLog.class);
this.paymentPartner = bind(till, PaymentPartner.class);
}

@Override
protected Result doExecute() throws BusinessException {

// ***********
// USE CASE STEPS
// ***********
PrintJob printJob = null;
if(basket.isEmpty()){ // STEP 1
till.getErrorInformation().setError("Nothing to sell!");
}else{
if(!till.hasEnoughPaper()){ // STEP 2
till.getErrorInformation().setError("Not enough paper!");
}else{
String authID = null;
if(PaymentType.electronic.equals(basket.getPaymentType())){ //
STEP 3
authID =
paymentPartner.callRoleMethod(PaymentPartner.class).getEPaymentAuthID(basket); //
STEP 3.1
if(authID == null){
// STEP 3.2
till.getErrorInformation().setError("E-payment failed. See card
reader.");
}
}

SalesOrder so =
salesOrderDatabase.callRoleMethod(SalesOrderDatabase.class).createSalesOrder(basket,
authID); // STEP 4
salesLog.callRoleMethod(SalesLog.class).createRecords(basket,
authID, so.getSalesRef().toString(), so.getTimestamp()); // STEP 5
printJob =
printer.callRoleMethod(Printer.class).prepareReceipt(authID,
so.getSalesRef(), basket); // STEP 6
updateTillsBalance(); // STEP 7
till.reset(); // STEP 8
basket.reset(); // STEP 8 - although in the users mental model, it
is emptied as each book is scanned...

getLog().info("Sale completed.");
}
}

return new Result(till, basket, printJob);
}

private void updateTillsBalance() {
//till cash balance adjustment
BigDecimal cashBalance = till.getCashBalance();
if(basket.getPaymentType().equals(PaymentType.cash)){
cashBalance =
cashBalance.add(basket.getTotalDiscountedPriceIncludingCustomerDiscount());
}
till.setCashBalance(cashBalance);
}

}


-------------

package
ch.maxant.till.dci_wrappers.server_sell_items_in_basket_context;

import java.math.BigDecimal;

import javax.annotation.Resource;
import javax.persistence.EntityManager;

import ch.maxant.dci.util.Role;
import ch.maxant.till.client.model.Basket;
import ch.maxant.till.common.data.Product;
import ch.maxant.till.common.data.SalesOrder;
import ch.maxant.till.common.data.SalesOrderItem;
import ch.maxant.till.dci_wrappers.Till;

/*package*/ class SalesOrderDatabase extends Role<Till,
SellItemsInBasketContext> {

@Resource(name="em")
private EntityManager em;

/** creates the sales order in the auditing database */
SalesOrder createSalesOrder(Basket basket, String authID) {
em.getTransaction().begin();

Long customerNumber = null;
BigDecimal customerDiscount = null;
if(basket.getCustomer() != null){
customerNumber = basket.getCustomer().getNumber();
customerDiscount = basket.getCustomer().getLoyaltyDiscount();
}
SalesOrder so = new SalesOrder();
so.setAuthID(authID);
so.setCustomerNumber(customerNumber);
so.setCustomerDiscount(customerDiscount);
so.setTillNumber(self.getTillNumber());
so.setPaymentType(basket.getPaymentType().toString());

for(Product b : basket.getProducts()){
SalesOrderItem soi = new SalesOrderItem();
so.getItems().add(soi);
soi.setSalesOrder(so);
soi.setDiscountPercent(b.getDiscountPercent());
soi.setPrice(b.getPrice());
soi.setProductCode(b.getProductCode());
soi.setTitle(b.getTitle());
}

em.persist(so);
for(SalesOrderItem soi : so.getItems()){
em.persist(soi);
}

em.getTransaction().commit();

return so;
}

}
-----------

package
ch.maxant.till.dci_wrappers.server_sell_items_in_basket_context;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Date;

import javax.annotation.Resource;
import javax.persistence.EntityManager;

import ch.maxant.dci.util.Role;
import ch.maxant.till.client.model.Basket;
import ch.maxant.till.common.data.Product;
import ch.maxant.till.common.data.SalesLogRecord;
import ch.maxant.till.dci_wrappers.Till;

import static ch.maxant.till.common.Constants.ONE_HUNDRED;

/*package*/ class SalesLog extends Role<Till,
SellItemsInBasketContext> {

public static final String ACCOUNT_NUMBER_PRODUCT_SALE = "100045";
public static final String ACCOUNT_NUMBER_PRODUCT_DISCOUNT =
"100097";
public static final String ACCOUNT_NUMBER_CUSTOMER_DISCOUNT =
"140451";

@Resource(name = "em")
private EntityManager em;

void createRecords(Basket basket, String authID, String salesRef,
Date timestamp) {
em.getTransaction().begin();

// sales log
int i = 0;
for (Product p : basket.getProducts()) {

SalesLogRecord slr = new SalesLogRecord();
slr.setAccountNumber(ACCOUNT_NUMBER_PRODUCT_SALE);
slr.setAuthID(authID);
slr.setCredit(getNetPrice(p.getPrice(), p.getDiscountPercent(),
basket.getCustomer().getLoyaltyDiscount()));
slr.setDebit(BigDecimal.ZERO);
slr.setIdx(i++);
slr.setPaymentType(basket.getPaymentType().toString());
slr.setProductCode(p.getProductCode());
slr.setSalesRef(salesRef);
slr.setTimestamp(timestamp);
em.persist(slr);

if (p.getDiscountPercent() != null
&& !p.getDiscountPercent().equals(BigDecimal.ZERO)) {
slr = new SalesLogRecord();
slr.setAccountNumber(ACCOUNT_NUMBER_PRODUCT_DISCOUNT);
slr.setAuthID(authID);
slr.setCredit(BigDecimal.ZERO);
slr.setDebit(getNetDiscount(p.getPrice(),
p.getDiscountPercent()));
slr.setIdx(i++);
slr.setPaymentType(basket.getPaymentType().toString());
slr.setProductCode(p.getProductCode());
slr.setSalesRef(salesRef);
slr.setTimestamp(timestamp);
em.persist(slr);
}

if (basket.getCustomer().getLoyaltyDiscount() != null
&& !basket.getCustomer().getLoyaltyDiscount()
.equals(BigDecimal.ZERO)) {
slr = new SalesLogRecord();
slr.setAccountNumber(ACCOUNT_NUMBER_CUSTOMER_DISCOUNT);
slr.setAuthID(authID);
slr.setCredit(BigDecimal.ZERO);
slr.setDebit(getNetDiscount(
getNetDiscount(p.getPrice(), p.getDiscountPercent()),
basket.getCustomer().getLoyaltyDiscount()));
slr.setIdx(i++);
slr.setPaymentType(basket.getPaymentType().toString());
slr.setProductCode(p.getProductCode());
slr.setSalesRef(salesRef);
slr.setTimestamp(timestamp);
em.persist(slr);
}
}

em.getTransaction().commit();
}

private BigDecimal getNetDiscount(BigDecimal price,
BigDecimal discountPercent) {
if (discountPercent != null) {
price = price.multiply(discountPercent).divide(ONE_HUNDRED, 2,
RoundingMode.HALF_UP);
}
return price;
}

private BigDecimal getNetPrice(BigDecimal price,
BigDecimal discountPercent, BigDecimal customerDiscountPercent) {

if (discountPercent != null) {
price = price.multiply(ONE_HUNDRED.subtract(discountPercent))
.divide(ONE_HUNDRED, 2, RoundingMode.HALF_UP);
}
if (customerDiscountPercent != null) {
price = price.multiply(
ONE_HUNDRED.subtract(customerDiscountPercent)).divide(
ONE_HUNDRED, 2, RoundingMode.HALF_UP);
}
return price;
}
}

-----------

package
ch.maxant.till.dci_wrappers.server_sell_items_in_basket_context;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

import ch.maxant.dci.util.Role;
import ch.maxant.till.client.model.Basket;
import ch.maxant.till.client.model.Basket.PaymentType;
import ch.maxant.till.client.model.PrintJob;
import ch.maxant.till.common.data.Product;
import ch.maxant.till.common.data.Customer;
import ch.maxant.till.dci_wrappers.Till;

/*package*/ class Printer extends Role<Till, SellItemsInBasketContext>
{

/** call to self */
boolean hasEnoughPaper() {
return self.hasEnoughPaper();
};


/** prepares the receipt */
PrintJob prepareReceipt(String authID, UUID salesRef, Basket basket)
{

List<String> lines = new ArrayList<String>();
BigDecimal total = BigDecimal.ZERO;
for(Product b : basket.getProducts()){
lines.add(String.valueOf(b));
total = total.add(b.getDiscountedPrice());
}
lines.add("Total: " + total);
lines.add("Payment Type: " + basket.getPaymentType());
if(PaymentType.electronic.equals(basket.getPaymentType())){
lines.add("Auth ID: " + authID);
}
if(basket.getCustomer().equals(Customer.ANONYMOUS)){
lines.add("Sign up for a loyalty card today!");
}else{
lines.add("Customer Number: " + basket.getCustomer().getNumber());
}
lines.add("Reference: " + salesRef);

return new PrintJob(lines);
}

}


---------

package
ch.maxant.till.dci_wrappers.server_sell_items_in_basket_context;

import java.math.BigDecimal;
import java.util.UUID;

import ch.maxant.till.client.model.Basket;
import ch.maxant.till.dci_wrappers.RoleWithLogger;
import ch.maxant.till.dci_wrappers.Till;

class PaymentPartner extends RoleWithLogger<Till,
SellItemsInBasketContext> {

/** retrieves the payment authorization ID from the payment device.
null if unsuccessful. */
public String getEPaymentAuthID(Basket basket) {
BigDecimal amount =
basket.getTotalDiscountedPriceIncludingCustomerDiscount();

getLog().info("Getting auth ID for sale to value: " + amount);

try {
//simulated pause
Thread.sleep(1000L);
} catch (InterruptedException e) {
//no one cares really
e.printStackTrace();
}

String authID = UUID.randomUUID().toString();

getLog().info("Auth ID received: " + authID);

return authID;
}

}


---------

Mikey B

unread,
Oct 8, 2011, 6:03:29 PM10/8/11
to object-co...@googlegroups.com
On 8 October 2011 22:50, Ant Kutschera <ant.ku...@gmail.com> wrote:
On Oct 8, 2:44 pm, Mikey B <mikey...@gmail.com> wrote:
> Id like to give it a stab at helping you.

Thanks :-)

> Has the customer asked you to "program" a printer? I don't think so from ur
> posts, so all you need to know is that it receives the receipt.

The physical printer is called by a shared library (DLL, or SO) and
has a low level API.  An abstraction layer above that, the printer
supplier has a library which lets you send "print jobs" to the
printer.


Then you'll have to many another application to take the receipt format and turn it into API calls, and provide a nice way to send it.

HINT:
> POST /printer/
> Content-Type: text/xml+tillreceipt 
> <recieptdata>
< 204 OK
< Location: /printer/printJobNo-1

A print job is basically a list or strings and each string is printed
on a line.

These are my current problems:

1) all roles are played by the till, so that I can pass the "is a"
test, albeit modified to be the "with" test.  Eg. does this phrase
make sense: "printer = till with printer" - yes, so the role printer
is applicable to the till.

"till" is the wrong word, a "till" doesn't use a printer.. a "SalesTransaction" uses a printer.
 

2) there is no interaction, per se.  However, when I call a role
method, like "printReceipt()" I pass the basket to that method,
because it needs to print each product on the receipt.   Is that
"interaction"?  I guess so.


What does a SalesTransaction do?

ScanBooks --> DisplayTotal --> TakePayment --> PrintReceipt
     /\                          |
      |_______________|
 
3) Many of my roles never need to call "self" to access data from the
role player (till).  That seems really weird.  And makes me wonder if
a better role player for "Printer" and "Sales Log" would be the
basket, because it has all the information required to create a
receipt or sales records, namely the list of products.

Spot on!
 
 But a printer
"is NOT a" basket.  Nor does it make sense in my mental model that
printer is a specialisation of the basket.  Nor does "basket with
printer" really make sense, because a basket cannot be hooked up to a
printer.


printer is NOT a basket is correct.. But a receipt is a basket.
 

James O. Coplien

unread,
Oct 9, 2011, 5:34:07 AM10/9/11
to object-co...@googlegroups.com
The use case is:

Purchase Book

Precondition:
Shopper has selected one or more books for purchase.
System has been initialized and daily administrative runs are complete

Success postconditions:
Shopper has ownership of books
Shopper has a receipt which, as proof of purchase, can be used to return the books
Shop has received payment for goods sold
Store inventory is updated
System is ready for another purchase transaction

Failure postconditions:
System is ready for another purchase transaction

Scenario:

1. Shop Clerk opens counter Shopper presents books
2. Shop Clerk scans prices Till registers price on display
2a. Scanner cannot recognize item Shop clerk enters price manually
2b. Shopper disagrees with price Shop Clerk enters price manually
3. Shop Clerk signals end-of-items Till sums prices, displays total, readies card reader with amount
4. Shopper enters credit card info Card Reader verifies payment and notifies till
4a. Shopper uses cash Shop Clerk manually handles payment
4b. Credit Card isn't good Card Reader notifies Till and Shop Clerk manually handles payment
4c. Credit Card is stolen Shop Clerk retrieves card and calls the police; transaction is aborted
5. Shop Clerk signals end of transaction to till Till resets card reader, clears display, prints receipt

Non-Functional Requirements

Credit Card removal timeout is 10 seconds.

This must have transaction semantics: i.e., the bank withdrawal, receipt generation and inventory update must be synchronized

Use standard NCR and Honeywell protocols between business devices.

Open issues

Do all tills have the ability to remotely open their drawer as a sign that the transaction is complete? If so, closing the drawer should suffice for the Transaction Complete signal

Special provisions when the Shopper is the Shop Clerk?

Need scenarios for coupons and other credits.

Do we need to deal with non-native currencies?


A good use case chunks work into 5 +/- 2 steps; bigger than that and it's hard to reason about. For finer detail you break it down with substeps:

4.1 Shopper scans card in Card Reader Card Reader validates Card and displays: "Enter PIN"
4.2 Shopper enters PIN Card Reader verifies Card and notifies Shopper and Till of completion
4.3 Shopper retrieves Card Card Reader returns to start state
4.3a Shopper does not retrieve Card after 10 seconds Issue alarm and display message to remove Card

You can further break down 4.2 into 4.2.1 and 4.2.2, etc. to reflect the interactions with the Bank, if you want to.

So this is a use case in the Cockburn or Wirfs-Brock sense. It is business logic rather than programmatic decisions. Design entails mapping this business logic into code. DCI Contexts provide a natural boundary for a use case and the Actors (Shop Clerk, Shopper, Till, Card Reader) map naturally into roles. The subdivided tasks are candidates for habits.


--
You received this message because you are subscribed to the Google Groups "object-composition" group.
To post to this group, send email to object-co...@googlegroups.com.
To unsubscribe from this group, send email to object-composit...@googlegroups.com.

Ant Kutschera

unread,
Oct 9, 2011, 7:22:06 AM10/9/11
to object-composition
On Oct 9, 11:34 am, "James O. Coplien" <jcopl...@gmail.com> wrote:
> The use case is:

Excellent - thanks!

I notice two things. First, the till is an actor.

Second, you make no mention of accounting records, audit logs, etc.
Would those simply appear in the requirements specification?

I have to say, I am still struggling to identify roles here. Can you
help?

At step 3, the till has data about which items were scanned, their
price, etc. Steps 3-5 are done within a transaction.

The till being a plain domain object with little or no behaviour,
doesn't know how to write accounting records (done at some stage in
the transaction), or print receipts (step 5).

So that is the behaviour I need to inject with a role, right? Or how
would you do it? If that is role behaviour, what would the role name
be??

Thanks,
Ant

rune funch

unread,
Oct 9, 2011, 8:04:13 AM10/9/11
to object-co...@googlegroups.com
Den 09/10/2011 kl. 13.22 skrev Ant Kutschera <ant.ku...@gmail.com>:

> The till being a plain domain object with little or no behaviour

To me the till is a system (hence my previous question whether it was
online or IRL) and I would model it as such (a context on it's own
right) and let the till context play the till role in the interaction
with the scanner and the card reader. When the steps are subdivided
some of the sub steps are going to form an interaction within the
bounds of the till system and those sub-steps would be where to find
the roles for the till context. At some point it makes no sense to
divide the steps into smaller steps at which point I would start
looking for plain data.

My 5 cents
Rune

James O. Coplien

unread,
Oct 9, 2011, 8:44:24 AM10/9/11
to object-co...@googlegroups.com
Rune's design works, but note that it is based on a different use case. His use case does the same as mine in the end, but it is a different system architecture, reflecting a different mental model.

I like to follow the use case. In my use case, the Till, Card Reader, and Card are objects (as named by their roles) in their own right. In that use case the scanner is probably part of the Till. That more faithfully fits my mental model, because I view the Card Reader not only as a business entity recognizable to the user but also as a locus of processing and autonomy to fulfill a tightly coupled collection of functions — a perfect object.

So that's how I'd code it up. I'd hide the distribution of the objects using either the Remote Proxy pattern or Half-Object Plus Protocol — but that's at a level far below this use case. The important discussion takes place at the use case level. A good business use case strips away all technical considerations that don't directly relate to an expressed business constraint or requirement. Notice that it is a business requirement in my use case that the software use NCR protocols. That's low-level. That something is a business requirement does not mean it is high level. People (especially programmers) are always confusing these two dimensions.

How does the business decide whether to use, for example, Rune's use case or mine? You can probably think of why Rune said the things he did. Those come from his anticipation of how the system will be used and, more importantly, how it may evolve. I also have my expectations of how the system will be used and evolved, and I suspect it's different from Rune's. That's foundation for dialogue to make sure we both have the same picture of the requirements. That's what use cases do: structure the requirements in a way that documents agreement and makes disagreement visible.

Once Rune and I agree, we now have a business model — a shared mental model — that can be a foundation for design. But it starts with business concepts apart

I'm not saying that Rune is wrong and that I am right. But I do want to point out that I think it is two different designs.



Wenig, Stefan

unread,
Oct 9, 2011, 11:14:45 AM10/9/11
to object-co...@googlegroups.com
that was the remark that got me started:

On Di, Okt 04, 2011 at 18:31:54, rune funch wrote:
> Subject: Re: One object, many roles... identifier naming
>

> I don't see anything wrong in having the same object playing multiple
> roles at the same time. Always having the same object playing multiple
> roles does feel like a design flaw to me
>
> -Rune

I included it in my original reply, then got lost somewhere in the thread.

I never said it doesn't make sense btw, just that it might cause unexpected reentrancy problems. (And I still think that individual data objects should stick to the SRP, which the till object doesn't seem to, but that does not invalidate your statement.)

Stefan

> -----Original Message-----
> From: object-co...@googlegroups.com [mailto:object-
> compo...@googlegroups.com] On Behalf Of rune funch
> Sent: Friday, October 07, 2011 10:49 AM
> To: object-co...@googlegroups.com
> Subject: Re: One object, many roles... identifier naming
>

James O. Coplien

unread,
Oct 9, 2011, 11:55:03 AM10/9/11
to object-co...@googlegroups.com

On Oct 9, 2011, at 1:22 , Ant Kutschera wrote:

I notice two things.  First, the till is an actor.

Yes, that is my mental model. Therefore it is a role.


Second, you make no mention of accounting records, audit logs, etc.
Would those simply appear in the requirements specification?

Show me a use case that uses them. If no use cases use them, they are unnecessary.

There may be an actor missing in this use case because I missed the corresponding requirement. Call it "Sales Register" or something like that. Then put it in the use case.


I have to say, I am still struggling to identify roles here.  Can you
help?

Take a shot at it and I'll comment.


At step 3, the till has data about which items were scanned, their
price, etc.  Steps 3-5 are done within a transaction.

The use case says none of this. It may be true, depending on your design. But it may also not be true. For example, the Till may only scan the price and pass it on to another role at a lower level that remembers the value. Whether it does that or not is not important to the business at this level. It may be important if you go deeper. But let's get the analysis right before jumping into implementation. You are jumping into design and implementation.


The till being a plain domain object with little or no behaviour,

NO. It is a role. It can be played by any one of several till objects by any number of manufacturers. I see its (domain class) behaviors as:

- scanning price
- opening money drawer
- sensing money drawer closed
- displaying item cost
- displaying total due
- print receipts
- sums prices

It also has some responsibilities "managing" the Card Reader


doesn't know how to write accounting records (done at some stage in
the transaction), or print receipts (step 5).

Sure it can print receipts. That is my mental model. Most NCR model tills contain a printer. Even today's hand-held wireless tills contain printers. If that is not your mental model, then add another role that corresponds to the device that prints receipts.


So that is the behaviour I need to inject with a role, right?  Or how
would you do it?  If that is role behaviour, what would the role name
be??


Too many "thats" and "its" for me to follow your question.

Ant Kutschera

unread,
Oct 10, 2011, 4:34:37 AM10/10/11
to object-composition


On Oct 9, 5:55 pm, "James O. Coplien" <jcopl...@gmail.com> wrote:
> NO. (the till) is a role. It can be played by any one of several till objects by any number of manufacturers. I see its (domain class) behaviors as:
>
> - scanning price
> - opening money drawer
> - sensing money drawer closed
> - displaying item cost
> - displaying total due
> - print receipts
> - sums prices

OK, so there is a till in the domain model. I feel those behaviours
are quite clever ones and they are not behaviours I would typically
put in a SOA domain object model. I thought DCI too wanted dumb
objects?

Then there is also the till role. Is its name "till" or more like
"NCRTill"?

James O. Coplien

unread,
Oct 10, 2011, 12:32:07 PM10/10/11
to object-co...@googlegroups.com

On Oct 10, 2011, at 10:34 , Ant Kutschera wrote:

>> NO. (the till) is a role. It can be played by any one of several till objects by any number of manufacturers. I see its (domain class) behaviors as:
>>
>> - scanning price
>> - opening money drawer
>> - sensing money drawer closed
>> - displaying item cost
>> - displaying total due
>> - print receipts
>> - sums prices
>
> OK, so there is a till in the domain model. I feel those behaviours
> are quite clever ones and they are not behaviours I would typically
> put in a SOA domain object model. I thought DCI too wanted dumb
> objects?

What constitutes "smart" and "dumb" depends on the business Context. If they show up in a use case as something I expect a role to do (which they all do), then they are part of the role interface. It is certainly true that the underlying object must be able to implement them, so the object must be of a domain class that supports that functionality. "Dumb" to me roughly equates to "not part of the use case."

Further, there is a subtle role of OOD here, in that the role happens to be named Till, and you want to name the domain class Till. Design is much about the art of naming. I would call the name Till and would call the domain class NCR 3872 or IBM 4002 or whatever. Follow the vocabulary of your business.

You see, there are many design tradeoffs to be made here. They depend on your mental model. You liked the top level use case: that suggests to me that it maps to your mental model. Therefor, your mental model has a notion of the Till role that exhibits these functions. If you in fact envision something more generic, it perhaps should not go in the top level use case. Items from the more detailed use case steps may indeed be part of the data class. At some point the team needs to decide where it is going from analysis to design; at that point, the methods get "demoted" to domain class methods.

I use this argument for illustration but it's not how I usually do it. I usually start by asking myself: What are the domain classes and what generic functionality do they support (i.e., what is the abstract base class)? I use that together to help contextualize the use case behaviors. I try not to let the use case behaviors get into the domain class territory. It's a delicate balance, and the insight of all stakeholders is important to both of these.

Reply all
Reply to author
Forward
0 new messages