Notes 13 Jan 2004
Domain Driven Design (VII)
Guest Author: Eric Evans
(Ken Scott-Hlebek starts with a discussion of the “Master of Software
Arts” program at UIUC. He is part of the group of students involved
in a trial run.)
Russ: Perhaps we could move on to Chapter 10? (starting p.246, Intention-Revealing
John B: Smalltalk Best Practice Patterns, “Intention-Revealing Selector.”
I tend to think of the Java keyword for interfaces.
Russ: When you are talking about “intention revealing interface”,
is it the design-by-contract sense of syntax and semantics?
Eric: Yes, Design By Contract was one of the main factors. I don’t see
it as just the method signature.
Tracy: You’ve gone to the trouble of creating a Ubiquitous Language…I
like the way the intention-revealing interface pattern is connected. And
your example is good to show how it can be hard. “Paint.paint()”,
Russ: artPaint.GetBlue() vs. artPaint.Blue()? I’m a little surprised
you went that way.
Eric: It’s kind of entrenched as a convention in the Java world, and
it’s not too bad.
Russ: Having “create” on creational methods… it’s useful,
sometimes, to have a naming convention that is a common idiom to help
Eric: I’d like the user not to care whether they were fetching a variable
value or calculating a derived value.
John B: I’ve run into that problem.
Russ: We typically use “get” for retrieving primitive values,
like ‘int’. We use “get” a lot for accessors.
Tracy: Whether it’s calculated or not.
Russ: We don’t tend to use “compute” or “calculate”.
Mark: It’s like J2EE or JavaBeans conventions.
Phil: Yes. EJBs use the JavaBeans conventions to make introspection easier.
You can look at the JavaBean and see what state is accessible.
John B: They call it a Design Pattern but obviously it isn’t.
Phil: But it’s a convention that makes it easier to discover properties.
Mark: But even Sun, you get String.valueOf(…) etc.
Eric: But isn’t a naming convention an awkward way to put in metadata
for a tool?
Jeff: But it gets used over and over and over again. Custom attributes
in Java are the
Eric: Sometimes “has” is better than “is” for a boolean.
Mark: I’ve seen “isHas…” and “getIs…” on accessor
John C: Were these native English speakers?
Mike: You want the convention to extend beyond is/has for your accessors.
If you enrich that language for describing, for removing ambiguity of
the interfaces, than the current state of things.
(some discussion of accessors and mutators, including conventions in
Java, Smalltalk, and Forté).
Scott: If you’re going to follow a convention that you use get…, you
shouldn’t have to care whether you fetch a field value or compute it.
Eric: It would be OK, if it were side-effect free. A lot of “calc”
methods actually modify values.
John C: Truly side-effect free? Or are you willing to have things cached?
Eric: I define it here, as no observable side effects. So caching is
OK if it’s appropriate. I don’t suppose anyone would like to make the
case against an intention-revealing interface?
John: “Too much typing?”
Jeff: “It’s such a pain to change the name of everything all the
time when you change the behavior!”
Eric: That’s the worst case, when someone has chosen a good intention-revealing
name, and someone else changes the behavior. The method name is lying
(question: should the name of iterators or index variables be long/meaningful?)
Russ: I label an iterator with the type of the instance coming out, so
iColor is an iterator over colors.
Phil: eachColor is the convention I’d use.
(internal vs. external iteration mentioned)
Phil: There are things you can do with external iterators you can’t do
with internal iterators.
Eric: But 99.9% of the time you don’t. You just run down all the items
in the collection.
Russ: But it makes you feel like a real programmer to write an iteration
(a discussion of iterators and collections, sometimes the application
is just lists, and it doesn’t matter whether you use iterators or whether
you count up via the index…)
Tim: Iterating over a 2D thing… I find that people do a better job
naming when they use iterators than when they index with integers. There
was one case where they used i and j to loop over the results, and in
one place they had misplaced an i for a j or vice versa, and something
that was supposed to be sent to one user was sent to all of them.
Eric: (discussion of a difficult case refactoring a nested loop with
embedded conditional logic)
Mike: A server test program that opened a number of sockets, one for
each thread, and sent for each socket, a number of messages, and waited
to see if each one had the proper completion token returned from the server.
So there was a loop of bursts …
Eric: Wouldn’t it have been easier to have an object to manage the threads?
Wouldn’t it have been quicker:
Mike: Probably. I just ran out of time, and it worked.
David V: Do you declare an iterator in the same scope as the loop (so
it is out of scope after the loop)? (expanded question: “Do you think
it’s important to declare a variable as close as possible to its point
Show of thumbs: some up, some down, several sideways.
Mike: You may need it in a larger scope, so you can’t declare it right
next to use.
Eric: Sometimes you can have a declarations section up at the top of
Phil: I put my fields up at the top of a class, certainly. Though I’ve
seen people sprinkle their fields around.
John: I use IntelliJ to write my iterators. What does it do?
Phil: So it writes the loop for you.
John C: There was a “GIST” specification language. They compared
‘declared once” specifications with “declared plus shown as
summaries”, and the ones with generated summaries were more useful.
(some extended discussion of how to organize functions, logical and physical
representation of the code, etc.)
p. 250, “Side-Effect Free Functions”
John B: So do side-effect free functions take a value object and give
you a value object back?
Eric: Sort of. Often the return value is a value object. But the connection
is rather that *every method on a value object must be a side-effect free
Phil: Couldn’t a value object function take an entity, change it and
give it back?
Eric: I wouldn’t write it that way. But maybe if the value object was
designed for that purpose. But I’d rather pass it to the entity, “change
yourself according to this transformation object”.
Tim: More like a service than a value object, that its job is to change
Eric: That alone wouldn’t make it a service, but a service might do that
sort of thing.
Russ: Perhaps you could clarify this? You’re talking about changing function
Tracy: That started with Phil’s example.
Eric: I think it’s risky business to change function arguments. A lot
about supple design is to be very obvious. Side-effect free methods are
easy to test.
John B: And to combine.
Eric: And to combine.
Russ: This kind of lets out the notion of Collecting Parameter.
Eric: When the design is exactly that, it would be sensible.
Phil: Collecting Parameter is more like a mechanism than business logic.
Eric: That might be a way to discuss Collecting Parameter, to try and
keep all the business logic out of it and put the meaning in the hands
of other objects. I don’t want to be too all-encompassing here. But you
can make things really easy to understand if you can put the complexities
onto side-effect free functions on value objects, and keeping your mutators
on your entities really simple. And having assertions (in the design by
contract sense, though they could be implemented as unit tests) on the
simple state of the entities. So client code will be really easy to understand,
as a combination of intention-revealing side-effect free functions. That’s
why these three patterns work very well together.
Russ: You’ve spent a little time thinking about this, then.
Eric: Yes, a little [spoken heavily].
John B: Closure of Operations following Standalone Classes?
Eric: Yes. This last part of the chapter is about trying to reduce the
number of dependent concepts. There are two different things here. They
both contribute to reducing interdependence. When I talk about dependencies,
I don’t mean compile-time necessarily, it’s a matter of conceptual dependency.
If you have a manager class, it doesn’t make sense out of the context
of an employee class. But perhaps depending on your model, you could understand
Employee all by itself. A genuine standalone class is one that you don’t
have to refer to other classes. Closure of an operation yields a method
that can be understand all by itself. Some of the things I talk about
here aren’t strictly closed, some of the Standalone classes aren’t completely
standalone. But if you had a class that only referred to system types,
strings and ints, these classes aren’t strictly standalone, but for practical
purposes it doesn’t add any intellectual load.
Mike: If you can move it to the next directory over, and it will still
compile and be usable by client code there, that’s a good test.
Eric: At the level of the code. Let’s say, often a street address is
a standalone class whose state consists of ints and strings.
John C: Then when you start doing business overseas, you start caring
about how an address is composed for use in other postal systems.
Eric: You could have other approaches, you could have a bunch of smart
formatters for various countries’ conventions. But most likely, as you
say, you’d model an address as a composed object.
John B: Or one big string.
Eric: There are two very important points here. Dependency is not a matter
of tracking compilation dependencies. In fact, the worst dependencies
are often ones which are implicit. For example, a class in one place which
operates on an ID, or on some object state which is represented as ints
and strings. That sort of <em>implicit</em> dependency counts.
Dependencies are very costly in terms of adding complexity. So these rules
talk about (1) Make the dependencies explicit and (2) Make them as few
Russ: The conceptual version of coupling and cohesion.
Russ: I know about closure of operations from mathematics. But perhaps
you could give a clearer sense of what it means here?
Eric: Operations where you put in one type and get another instance of
the same type are examples of closed operations. Selecting from a collection
gives you another collection.
Russ: So the way you wouldn’t want to do it would be to mix a pigment
and a pigment and get a paint, then pigment would not be closed over mixing,
and you’d have to be concerned whether you were dealing with a pigment
or a paint.
John B: So the part of Java’s String protocol, toUpper(), substring(),
etc. which all return String (not StringBuffer, char, etc.), would be
a set of operations closed under String.
Eric: Closed operations are much easier to compose, and that makes them
easier to work with. The fact that they are instance methods on the same
type which they return groups them cohesively together.
Conceptual Contours (pattern)
Eric: I feel it’s very important, but I have difficulty explaining it.
It’s too much the product of intuition, I guess. Some of the ways I suggest
to find them are sort of circular.
Mark: So perhaps some of the advice is the right level of detail for
your model. Things like atoms are too fine-grained for a model of a car,
they’re outside the conceptual contour. You’d want steering wheel, tires,
etc. Not a monolithic “Car”, and not atoms.
Eric: And not front half/back half.
Jeff: Unless you’re doing finite element modeling of your car.
Russ: Conceptual distance is something you might want to discuss here.
And the sense of what concepts are close to one another. There are interesting
things (Kohonen maps) that are self-organizing clusters of concepts, ways
to get a more global picture when you’re just starting with the sense
of a bunch of individual things and their conceptual distance to one another.
Eric: That sounds like it’s the right neighborhood. As long as we don’t
get too carried away and turn it into another Rational Rose.
Russ: But a tool that understood conceptual distance could be useful
in some sense. Maybe even notifying people who are concerned with issues
that are conceptually close.
Russ: Dirk Riehle has done some work in executable UML. It might be good
to have him in to discuss the other side of things.
David: There’s this design that if you attune yourself correctly, it’s
not necessarily the code, or the diagrams, but you can talk about it.
Eric; “The model that can be named is not the true model”?
Mike: Modeling as a distraction rather than a catalytic activity. You
want a catalyst but you have a modeling language? So developing a ubiquitous
David: I had a professor say: “after a while, your math is in your
wrist”. But as soon as the diagram becomes “the thing”,
where it’s important, where you’re generating things off it, you can get
in some very bad places.
Eric: When I review this chapter (Chapter 10, “Supple Design”),
it starts out talking about how good design has this tactile quality (picture
of a bicycle), talks about some of the old Smalltalk idioms, moves on
to Design by Contract, this vague thing about Conceptual Contours. So
by the end of the chapter, the odd assortment of stuff is what you need
to accomplish the goal, a supple design that is pleasant to work with.
These pieces are not traditionally grouped, but I see a deep connection.
Do other people?
Robert: Some of each. Are they connected? Yes.
Wayne: I didn’t think of them as an odd assortment until you called it
out. Then I thought, “yes, this is a mixed bag.”
John B: Like listening to a director’s track on a DVD, they note all
the inconsistencies that you’d never notice.
Russ: Wouldn’t some other things belong here? Value Objects vs Entities
could belong here.
Eric: They show up in discussion. I didn’t want to repeat a pattern definition.
I didn’t mean it to becomprehensive, to cover all the techniques you would
use to make the design supple. But enough to convey the idea.
Mike: You are at different levels in the practices of good design. The
language connecting the pieces may need to be enriched, but the practices
are all important.
Eric: The first three patterns, to me, seem to hang together very well,
very closely. Conceptual Contours… that’s where the picture of the bicycle
comes in. You can see there are some things that are easy to take apart,
other things that are tightly bound assemblies. At the level of the designers
of a bike, the stack of gears, the gear assembly, was the part, rather
than each gear individually.
Mike: Bikes have these parts which are interchangeable to such a degree
because there are standards involved. Tolerances and specifications.
Mike: The success in communications software is based on standards. TCP/IP,
Eric: I think that’s an example of a supple design as well. They’re well
composed, and well separated as well. The breakdown was one that allowed
for growth and adaptation.
Russ: Isn’t that a topic from chapter 14?
Eric: And those things are related, aren’t they. But I think of the Internet
protocols as a set of supple designs done right. It’s not the first thing
they came up with. The initial prototype isn’t what we have now.
Jeff: It took them at least four versions to get it right. (reference
Eric: If you freeze too early, establish the standard without investigation
and practice, you end up with a stiff, awkward kind of design.
John B: “rough consensus and running code”, at least two successful
independent implementations of a proposed standard.
Jeff: And it helps if the two implementations talk to one another.
Eric: And the OMG is trying to create this standard before anyone has
actually implementing anything like it. (refering to UML support for MDA)
David V: Eric, I’m looking for a way to “sell” supple design.
So can I say, “If you use supple design, it’s less likely you’ll
have to throw away an application and rewrite it from scratch?”
Eric: Yes, it’s key to being able to hold onto your program and being
able to modify it for a long time.
David V: I was hoping your chapter conclusion would make a stronger point
Robert: That’s the whole-book benefit, rather than just the Chapter 10
Russ: Chapter 10 applies even when you’re outside the domain part of
your application code.
Mike: I might express it as “Debt-free design” in the words
we used last week.
Eric: I would say that with a supple design, your project may just start
taking off at the point a conventional project would be heading for legacy
Eric: Did anyone actually read Chapter 8 and the example at the end of
Chapter 10? Chapter 8 is an extended example, and the end of Chapter 10
has a more close-up view of the same transformation. Almost, that is.
It’s more like taking the stiff early version and turning it into something
supple. That’s a nice combination to get into.