Re: Client/Service relationships & Flow of Requirements.



Responding to Daniel T....

Doing so has no intrinsic benefits. The benefits lie in the
developer being forced to do other things properly...


I disagree strongly about that. Here is a highly abstract example:

class C:
def foo( value )
ensure: self.bar() == value
def bar()
With the above, the client calls "foo" when it wants "bar()" to return a particular value. The client is manipulating the service's state.

Hmmm. This is a different disconnect. I don't know what language you are using so I assumed the 'ensure' clause was just an embedded DbC postcondition assertion on how 'foo' changed the state of the application.

If 'foo' implements a behavior responsibility, then it should not be returning a value; in an OO context only knowledge accessors should return values. Returning a value immediately creates a hierarchical dependency in the client (i.e., the 'foo' computes the value correctly with respect to the client's responsibility). IOW the message is a procedural Do This imperative and the client is depending on what the responder does.


class C:
def foo( value ):
ensure: n/a
def bar()
With this version, the client has no intrinsic reason to send the "foo" message. The method does nothing that the client can count on. So why would the client send the "foo" message in this case? The only reason would be because objects of type C have requested that "foo" be sent to them when certain conditions are true. This gives the message receiver complete freedom to do whatever it pleases upon receipt of the message and that is an intrinsic benefit IMO.

I think that is the wrong spin on what is happening. In an OO context 'foo' is not asking for anything. Also, I think we are talking about different conditions than the DbC conditions that govern /how/ 'foo' executes that one embeds in the code as correctness assertions.

The client does something that changes the state of the application. The client realizes that and issues a message to announce the change. It is up to the developer to connect the flow of control dots by directing that message to somebody who cares _in the overall solution context_.

But 'foo' only cares because its precondition for /when/ to execute in the overall solution happens to match the postcondition of the client method when it changed the application state. The developer recognizes that and directs the client message to 'foo'. IOW, the relevant precondition is not the one for how execute the behavior; it is the precondition on when to execute the behavior.

That precondition on when to execute 'foo' may include conditions on state variables. But they aren't DbC conditions on consistency per se; they are conditions on timeliness. (However, conveniently the DbC correctness assertions often indirectly define timeliness.) This precondition always includes the sequencing constraints of operations within the overall solution algorithm.

So long as there are no hierarchical dependencies between the behavior responsibility implementations, one can apply this sort of rigorous DbC-like matching of preconditions to postconditions to determine correct flow of control when connecting the message dots. More important, one can do it at a higher level of abstraction (e.g., a UML Interaction Diagram) that is quite independent of the implementations of the individual objects and their responsibilities.

The basic assumption in the "client owns the interface" principle
is that the client needs the service to perform some action on its
behalf. Obviously the client must be able to inform the service
when to perform that action, so the client gets to add methods to
the interface describing the services it needs. However, when
messages are used to inform, rather than compel, the onus is on
the message sender to do the informing.

The predator/prey example can serve to illustrate what I mean:

The following is designed to compel Prey objects to do something:

class Prey:
def runFrom( predator ):
require: predator < 100 yards from prey
ensure: predator > 100 yards from prey

Note that the DbC precondition assertion is implemented in the Prey
but it is really checking the correctness of the Predator software
-- the Predator has no business sending the message unless the
condition {predator < 100 yards from prey} prevails. So the DbC
precondition is just validating a requirement that has already been
allocated to Predator was implemented correctly.


You are absolutly corrrect. However, there is no requirement placed on anyone to actually send the runFrom message. The precondition only states a limitation on when it can be called. In fact, I doubt that Preditor would send such a message, they usually don't want the prey to run.

Per the above, that requirement is implicit in the behavior sequencing necessary to solve the overall problem in hand.

[Let's not quibble about who sends the message. That just gets side tracked in anthropomorphization and other issues. The point is that /somebody/ recognizes that the {predator < 100 yards from prey} condition now prevails in the overall application.]

Similarly, the Prey has a responsibility to react to the message,
which is described by the DbC postcondition for the Prey's response
to the message. That postcondition just expresses the requirements
allocated to the Prey behavior.


Correct again, and therein lies the problem. Whoever sends "runFrom" is doing it for the express purpose of making the prey satisfy the post condition, the prey object has little choice in the matter. Sure it can implement the runFrom message any way it sees fit (it could fly, or swim for example) but it *must* move away from the predator and the client sending the message is counting on that.

Au contraire! They should send the message because the condition {predator < 100 yards from prey} prevails in the overall application state. They should not care who it goes to. That is a flow of control problem for the overall solution. It is the expectation of the response that makes the runFrom name immediately suspect in an OO context.

Whatever code sends the "runFrom" message to prey objects, it
expects a particular result, it is specifically sending the
message so that the prey will move away from the predator. (I.E.,
to cause the state to change such that the predator and prey are
more than 100 yards away from each-other.) There is no requirement
on the client to send the "runFrom" message if the client doesn't
want the prey object to move.

However, if the message is designed to inform rather than compel:

class Prey:
def predatorWithin100yards( predator ):
require: n/a
ensure: n/a

Now, the contract (which can't be stated properly in either the
require or ensure clause) is that anytime a predator is within 100
yards of the prey object, it shall be informed of that fact. The
onus is on the client to do the right thing. The Prey object need
not do anything at all in response to the message.

I can't buy your conclusion. The message announces that a
particular condition exists (i.e., predator < 100 yards from prey). That condition is exactly the same as the first case, so I think the
DbC precondition is the same. (The name of the message is
misleading; it could have been a predatorSneakedUp announcement and
the precondition would still hold.)


I think you are wrong in your assesment. In the first example the message means both "a predator is within 100 yards" *and* "I want you to run until you get outside that 100 yard radius." If the latter phrase isn't true, the client has no business sending the "runFrom" message.

I am arguing that in a OO context there should be no *and*. All the client needs to know is that the condition now prevails.

In the "predatorWithin100yards" example, the client has no expectations on the prey's behavior. The prey can do anything it likes, including running toward the predator, hiding, playing dead...

My point here is that the examples are identical except for the message name. Exactly the same preconditions and postconditions apply in both examples. The name in first example /suggests/ that the Predator expects a particular behavior from a Prey. But one can't know that unless one looks at the implementation of the Predator method.

I think we are getting caught up in 3GL type systems. In a 3GL the client /must/ know who the message is going to and /must/ know the receiver has the responsibility due to physical coupling. But that is benign so long as the client implementation does not depend on who the responder is or what it does.

As an extreme example, in a well-formed OO application one can define all the methods of all the objects without generating any messages (calls to other objects). One can then resolve the messages in a UML Interaction Diagrams. Finally one can back-fill the message generation by generating the messages in the client methods using the receiver's names. Because one has fully defined the behavior responsibilities implementations without any reference to other object behaviors, one is guaranteed that there are no hierarchical dependencies despite the physical coupling in the 3GL type system.

[BTW, R-T/E people will actually do this sometimes. They create the state machines and completely define what each action does before they think about where the transition events will be generated. They then use a DbC-like approach to match action preconditions with other action postconditions and stick the event generation at the end of the action with the matching postcondition.]

At that point, though, the application will superficially look like a procedural Do This paradigm because the client methods are directly invoking service methods. One can only tell if it is truly a well-formed OO application by looking at the client method implementations.

Similarly, the Prey still has the same response to receiving that
announcement (i.e., to move such that predator > 100 yards from
prey). So I think exactly the same DbC postcondition applies here.


There you are absolutly wrong. The prey can do any of a number of things in response to "predatorWithin100yards" including attacking the predator!

I think we are bogging down in 3GL details. I assumed the 'ensure' clause was a DbC postcondition on what the Prey method should do that captured the requirements on the responsibility. So I was simply extrapolating from that postcondition. I don't care what the Prey does in response. But neither should the Predator (at least for this message).

That is, the reason one separates message from method and thinks of
messages as announcements in the OOA/D is not because of any
intrinsic difference in the way responsibilities are allocated. It
just prevents one from creating a dependency in the message sender
on what the specific response is.


I am looking deeper. In the former example, the client decides when the message will be called (as long as the precondition holds,) and the client knows what the object will do in response to the message (at least to some extent.) In the latter case, the service establishes when the message should be sent and decides what to do in response to that message.

Do you have 'former' and 'latter' reversed in the paragraph? There weren't any DbC assertions in the latter case.

Assuming they are reversed, I sort of agree with the first sentence, at least with respect to the implied procedural mindset. But I don't agree with the second sentence at all. Putting the DbC correctness check in the service is purely an artifact of the way the 3GL supports DbC validation.

[FWIW, I don't think {predator < 100 yards from prey} is a proper DbC precondition, which is another basis of disconnect. (I didn't bring it up before because I didn't want to get side tracked and it seemed acceptable for an example.) That condition really has nothing to do with the Prey's /intrinsic/ behavior; it is an artifact of the particular solution. For example, suppose one wants the Prey to move 100 yards away from a bad smell. Then there is no predator at all so one can't reuse the behavior.

If we separate message and method so that we think of the Prey response as needToMoveOutQuickly, then we would have exactly the same intrinsic behavior responsibility for Prey but we could trigger that response with either a predatorSneakedUp message or a toxicWasteDetected message, depending on the specific problem context. The Prey response should be completely indifferent to that external context. Now the precondition would need to be something like {something nasty < 100 yards away from prey}. Good luck on quantifying that as a DbC assertion. B-) But it gets worse. Suppose you need Prey to move when {predator < 100 yards from prey} but you need Prey to move when {bad stink source < 200 yards from prey}. So even the quantification of 100 yards isn't a valid contract condition unless you enumerate every possible context.]

[At OOP time the message is going to be the method signature and the
Predator will have to know the type of Prey. So all of that
conceptual decoupling will have been apparently wasted because of
physical coupling in the 3GLs. But from a design viewpoint it will
not have been wasted because the Predator will be constructed so
that the physical coupling of the 3GL is benign.]


Let's stay at the analysis and design level.

OK, but then the contract specification for the precondition isn't in Prey at all. B-)

Now let me get back to the opening statement about the client owning the interface. It is true that one designs the interface encapsulating the service semantics to the needs of the client. IOW, the client determines the /way/ in which the semantics is accessed. So in a large project with different teams working on different subsystems, the teams will typically negotiate the interface that the service subsystem provides.


Just like a procedural function. The client is *using* the service to perform some sub-function that it needs. That's not OO.

No, the client is sending a message when some condition prevails. The syntax of the messages is negotiated.

Remember the context of this thread is client/service relationships at the subsystem level. My position is: (A) the client/service dependency reflects a flow of requirements and (B) that dependency is orthogonal to communication between the subsystems. The interface is a communication mechanism.

Maybe the example was too abstract. Let's try something more dear to the trade:

class ProceduralStack:
def push( object ):
ensure: self.size() = self.size() + 1
ensure: self.top() = object
A client has no business sending the "push" message to the above class unless he *wants* the stack's size to increase by one, and its 'top' method to return the object pushed. I.E., the client is asking the stack to do something for it. ("Here, could you hold on to this for me?")

class OOStack:
def dataAvailable( object ):
ensure: n/a
In this case, the client is expected to send the message whenever data is avaiable, no matter what the OOStack intends on doing with the data. The client has no reason or right to expect the OOStack to hold the data. It is simply saying the data is available. (I grant that such a stack class wouldn't be very useful for the client, but it does serve to illustrate the difference.)

I think we have a problem over what your 'ensure' clauses mean. I also think we are getting bogged down in 3GL syntax where one can't separate message and method.

My pushback would be: why would an OOStack not have a 'push' responsibility? That is intrinsic to what a stack does so exactly the same postconditions should prevail in both cases.

The condition that triggers executing the responsibility is none of the stack's business. (So I like these conditions better than {predator < 100 yards from prey} because they don't depend on context.) Conversely, the client is announcing hereIsAValueThatNeedsSaving. To make that announcement the client doesn't need to know anything about stacks.

The fact that hereIsAValueThatNeedsSaving maps to Stack::push is an issue of flow of control in the overall solution that the developer must resolve. That mapping is independent of expectations of either client or service implementations.

- - - - -
If I add a compel type method to the Prey class, the client need not call it unless the client wants the prey to do what the method compels it to do.

If I add an inform type method to the Prey class, the client must be modified to call it or the Prey objects won't work right.

- - - - -
It is obvious (at least to me) why the "inform" interface is better, the prey is making its own decisions as to what it will do in response to the predators position (it may decide to charge rather than run for example.) But the prey can't do the right thing, if it isn't informed when the condition holds.

This also shows how DbC is an inherently procedural methodology. Well constructed OO methods have no ensure clause.

I have to disagree with that conclusion also. I suppose one could argue that putting DbC precondition assertions in the receiver is a procedural view, but I think that is a stretch.


No, putting the postcondition assertion is a procedural view. I leave you with one more example. The ultimate in OO, the observer pattern. The observer's "update" method has no postcondition Observers need do nothing at all when they receive an "update" message. However, the Subject *must* send "update" to every interested observer whenever its state changes. The onus is on the client to do what the server demands, not the other way around.

I'm afraid I don't follow this at all. A postcondition simply defines <the relevant portion of> the state of the application when a behavior has completed. I think that is true for any software behavior whether is is OO or procedural in nature. In the Observer, Oserver::update does something with the state data from ConcreteSubject and there will be some relevant postcondition on the correctness of that processing.

That postcondition is driven by the requirements that have already been allocated to the message sending object during problem space abstraction, not the message receiver.


Exactly. The sender is acting as a "god object" telling the receiver what to do and when to do it. That is the essence of procedural. In OO, we don't run around telling obects what state they must be in, we tell them what state we are in and let them do what they please with that information.

Alas, no agreement. I made a typo. Change 'postcondition' to 'precondition' in my quoted text.


*************
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



.



Relevant Pages

  • Re: Client/Service relationships & Flow of Requirements.
    ... The client is manipulating the service's state. ... The following is designed to compel Prey objects to do something: ... predator < 100 yards from prey ... not do anything at all in response to the message. ...
    (comp.object)
  • Re: Client/Service relationships & Flow of Requirements.
    ... Obviously the client must be able to inform the service when to perform that action, so the client gets to add methods to the interface describing the services it needs. ... The following is designed to compel Prey objects to do something: ... predator < 100 yards from prey ... Similarly, the Prey has a responsibility to react to the message, which is described by the DbC postcondition for the Prey's response to the message. ...
    (comp.object)
  • Re: Client/Service relationships & Flow of Requirements.
    ... called when particular external conditions are met. ... The basic assumption in the "client owns the interface" principle is ... The following is designed to compel Prey objects to do something: ... predator < 100 yards from prey ...
    (comp.object)
  • Re: Client/Service relationships & Flow of Requirements.
    ... called when particular external conditions are met. ... The basic assumption in the "client owns the interface" principle is ... The following is designed to compel Prey objects to do something: ... predator < 100 yards from prey ...
    (comp.object)
  • Re: [Full-disclosure] [Professional IT Security Providers -Exposed] PlanNetGroup ( F )
    ... the review a second time and incorporate some of your suggestions. ... to do what the client will pay him for. ... exactly a vulnerability assessment is... ... Your response to question 3: ...
    (Full-Disclosure)

Loading