Re: Client/Service relationships & Flow of Requirements.
- From: "H. S. Lahman" <h.lahman@xxxxxxxxxxx>
- Date: Fri, 02 Feb 2007 17:10:01 GMT
Responding to Daniel T....
Responding to H. S. Lahman...
I'm snipping because I think our discussion is getting rather fragmented and it seems as though there is some confusion about various terms. So I will attempt to remove the confusion and then restate my case. Part of removing the confusion is to do away with the terms "client" and "service", they simply don't apply in an OO context. Instead I will use "Sender" and "Receiver" Respectively.
I don't buy your object to client/service in an OO context, but let's not go there.
In DbC, the precondition does not state when the message should be sent, it only states what shall be true when the message is sent. For example:
class Stack:
def pop():
require: self.empty() == false
Such a precondition does not mean that the pop() message must be sent whenever the stack is not empty...
def sqrt( value ):
require: value >= 0
Such a precondition does not mean that sqrt must be called for every nonnegative value in the system.
However, true OO methods *do* place a requirement on when they are called. An Observer *requires* that its Subject send the "update" message whenever the Subject's state changes. I.E., the Receiver (Observer) is making a demand on the Sender (Subject.) This requirement cannot be adequately stated in DbC.
I think this is the core of our disagreement. The /solution/ requires that /somebody/ send Observer a message that will trigger the 'update' behavior responsibility. That says nothing about when or why the behavior should be invoked.
The Observer has an obligation to provide an implementation of an 'update' responsibility when the application solution needs it. It does not know when or why that might be. It does not know that a Subject even exists in the application.
The developer has a vision of the overall solution to the customer's problem. That vision caused the developer to decide that an 'update' behavior responsibility was needed to solve the problem and the logical owner of the responsibility would be a particular problem space entity abstracted as an object. That is basic problem space abstraction that is done before any solution dynamics is even considered. At that point, though, the requirements on the responsibility are fully defined. The requirements on that responsibility are intrinsic to the underlying problem space entity and are independent of any solution context.
Another, quite different, part of the developer's vision of the solution is the algorithmic sequence of behavior steps in the overall solution. The developer connects the dots of that vision by determining who sends messages and who responds to them at the level of a UML Interaction Diagram. That is a <design> requirement placed upon /collaboration/, not on individual object responsibilities. The ability to do that is enabled by the fact that OO behaviors are intrinsic, self-contained, and logically indivisible. Those characteristics allow the behavior implementations to be independent of exactly the sort of context constraints that you arguing the receiver places on the sender.
The fact that connecting those dots is dynamically complex for other reasons may cause the developer to introduce the Observer pattern. But that does not change in any way the requirements and contracts for what individual object behaviors do. The requirements on the 'update' responsibility and the Subject's implementation where notify() is invoked are exactly the same as they would be is there was exactly one Observer in a fixed association.
What both of your 'require' clauses specify is <part of> a precondition that must prevail for executing the responsibility _in the overall problem solution_. The developer uses those preconditions to connect the flow of control dots, but that condition specifies nothing about how, why, or when those conditions should prevail in the overall solution. Consequently they do not constrain what any other specific object must do or must not do.
In DbC, the postcondition tells the Sender what the Receiver guarantees to be true after the message is processed. The postcondition is an obligation on the Receiver to do a particular job.
class Stack:
def push( value ):
require size() == old size() + 1
The require clause *tells the Sender* exactly what is going to happen as a result of the push message.
Wow. I really don't know how to respond to this. This is a postcondition of the processing of stack itself, not anyone else. How could this imply anything at all about the message sender?
Why would the sender need to know this? Why would the sender care whether the collection was a FIFO stack or a B-Tree? Why would the sender care whether the operation was a push or a pop if it is a stack? To me this epitomizes exactly what the OO paradigm seeks to avoid -- hierarchical dependencies where the sender does depend on knowing such things!
To restate my case: There are only two reasons for the Sender to send a particular message to a particular Receiver. Either the Sender wants the Receiver to do a particular job, or the Receiver insists that it receive the message when some state holds true. The former case is a distinctly procedural outlook, the Sender is demanding that the Receiver perform a particular service. The latter is an OO outlook, the Receiver is demanding that the Sender inform it of a particular condition.
And to reiterate my position, in an OO context the sender does not send a particular message to a particular receiver. The sender identifies a change in the overall solution state resulting from something it did and announces that change. That is all the sender knows about; it doesn't even know the eventual receiver and its response exist. The developer decides who cares about that state change and directs the message to a particular object responsibility. That mapping is done at a higher level of abstraction than individual behavior implementations.
[It is fairly common during maintenance of a well-formed OO application to change the solution by simply rerouting messages to different objects so that just the processing sequence is changed without touching the implementations of any of the involved behaviors. That is only possible when messages are pure announcements with no expectation of what the specific response will be.]
It is only at the 3GL type system level where message and method are married through the procedure signature that the sender needs to know anything at all about the receiver. That 3GL problem of physical coupling simply doesn't exist at the OOA/D level where the application design is formed.
*************
There is nothing wrong with me that could
not be cured by a capful of Drano.
H. S. Lahman
hsl@xxxxxxxxxxxxxxxxx
Pathfinder Solutions
http://www.pathfindermda.com
blog: http://pathfinderpeople.blogs.com/hslahman
"Model-Based Translation: The Next Step in Agile Development". Email
info@xxxxxxxxxxxxxxxxx for your copy.
Pathfinder is hiring: http://www.pathfindermda.com/about_us/careers_pos3.php.
(888)OOA-PATH
.
- Follow-Ups:
- Re: Client/Service relationships & Flow of Requirements.
- From: Daniel T.
- Re: Client/Service relationships & Flow of Requirements.
- References:
- Re: Client/Service relationships & Flow of Requirements.
- From: Daniel T.
- Re: Client/Service relationships & Flow of Requirements.
- From: H. S. Lahman
- Re: Client/Service relationships & Flow of Requirements.
- From: Daniel T.
- Re: Client/Service relationships & Flow of Requirements.
- Prev by Date: Re: Adapter Pattern X Proxy
- Next by Date: Re: Critique of Robert C. Martin's "Agile Principles, Patterns, and Practices"
- Previous by thread: Re: Client/Service relationships & Flow of Requirements.
- Next by thread: Re: Client/Service relationships & Flow of Requirements.
- Index(es):
Relevant Pages
|