Re: Client/Service relationships & Flow of Requirements.



On Mon, 29 Jan 2007 20:29:33 +0000, H. S. Lahman wrote:

Responding to Carter...

The client/service relationship does, indeed, reflect a dependency
between the client and the service. However, that dependency is
reflected in a flow of requirements rather than a function calling
hierarchy. That is, the client(s) define the requirements for the
service. In particular, communications between the client and service
subsystems are entirely orthogonal to the flow of requirements. This is
an enormously important point in understanding the structure of OO
applications.

3) I regard the dependency tree as clearly involving the call graph in
two ways.
a) If the service explicitly names compile time symbols of the client,
do not be surprised if it becomes very difficult to ever reuse that
service. (The old cry, "Waah! We can't do software reuse, it's too
difficult. Yes, well. You didn't write reusable software so don't cry
now that you can't get software reuse.) If enough symbols are involved
change "difficult" to "impossible to reuse", even within the same
executable.

My response is: Don't Do That. B-) In fact, using the interface
approach I advocated on the blog for subsystems, it is impossible to get
into that situation because the interface will be pure message-based
(i.e., {message ID, <by-value data packet>}). As a result no element of
the implementation of one subsystem will be known to the implementation
of any other subsystem.

You keep saying that, other people keep saying that, but it still isn't
making sense to me.

Whether POD (plain old data) message passing or synchronous function
called based has nothing to do with the issue. As far as I'm concern the
most message passing allows is for some time slack, decoupling threads
from exact tooth-for-tooth intermeshing cog-like synchronous handling of
events. That's all, end of story.

The issue is who owns the message ID? Who owns the the structures and
enums and types that make up the definition of the message packet? The
client? Or the Service?

If any of these are owned by the client, then such message (or function
call) from the service to the client inflexibly and unreusably creates a
cyclic dependency that couples the service to that particular client. Why?
Since any other client must have explicitly the same message ID's (name
clash!) and same structures (name clash or non-DRY maintainance horror) as
the first.


b) As an empirical observation, rather than a hard and fast rule. But
one with a high probability of being valid. Service code that makes
explicit synchronous function calls to the client often create thread
races and subtly smash invariants.

That is exactly why one prefers pure message interfaces for subsystems,
especially event-based interfaces.

Whilst that may help, in general there are still "gotchas" even in pure
message passing interfaces.
a) The first arises from the implicit mindset that says, "I'm thinking
about what I'm thinking about, unless my Client(boss) tells me to think
about something else. My own thoughts are inherently serialized and
sequential. No Issue. I expect my Client to behave in certain simple ways
gauranteed by my preconditions, which I can protect, queue, mutex into a
sane sequentiality. If now my worker services now start arbitrary complex
asynchronous conversations with me, I'm going to derail my train of
thought. I wish my workers would just do what I tell them, and deliver
single simple "done" or "failed" event on completion."

Where the Wheels fall off and programmer after programmer delivers deeply
subtle thread races if the both the service and the client are attempting
to send structured events to each other, which clicks the other through
asynchronously through a complex state machine.

The key to spotting thread races is to remember complete transactions must
be serialized, not accesses. eg. Anything that is doing
if (A)
doB
else
doC

It doesn't help ever to serialize access to A, doB and doC individually
whether by mutexes or making everything message passing.

Otherwise after the "if (A)" the value of A can change under you and you
promptly do exactly the wrong thing.

To make it explicit...
Client sends message to service "Do this"

Service sends message asking client, "Ok, but what is the value of 'A'?"

Client sends message to service, the value of A is..."

In the time gap between sending and acting on the message, the value of
'A' may change.

Service receives now outdated message from client and....

Does exactly the wrong thing.

ie. The entire transaction must be serialized. ie. No message passing
interface is going to help you if the events are walking a state machine
through a transaction. You still can get thread races and invariant
smashes.

ie. Making a design pure message passing masks some thread races, but is
really an entirely orthogonal notion.

ie. The correct solution has nothing to do with message passing or not
message passing but "Tell, don't Ask" or the "Law of Demeter".

ie. Design the system so the correct service owns the needed state and all
you need say is "Do this, I am asking you since you are the owner of the
needed info, tell me when done".

ie. Explicitly, and in words of few syllables...

If your service must send information in a form or at a time or in a
sequence that requires explicit knowledge of the client, even as you have
clearly said is OK, using an asynchronous structured message, you are
heading into the Land of Trouble.

BTW, I am uncomfortable with the use of 'call graph' in the first
sentence of (3). First, the idea of synchronous calls to an API is not
a real good way to think about message-based communications in an OO
context. One is better off thinking in terms of asynchronous
announcement messages for inter-subsystem communications. If the
interface is designed that way at OOA/D time, it can always be
implemented synchronously during OOP. But if it is designed around
synchronous calls, it may not be possible to implement it in a truly
asynchronous and/or concurrent environment.

Point taken. Although currently I'm working at the point where the design
is long done, the code has been written, it is now a matter of reverse
engineering the true architecture as built rather than as designed, and
then refactoring and enhancing.

I will explore the rest of your extensive reply later.

Thank you for your time and attention.


John Carter Phone : (64)(3) 358 6639
Tait Electronics Fax : (64)(3) 359 4632
PO Box 1645 Christchurch Email : john.carter@xxxxxxxxxx
New Zealand


.



Relevant Pages

  • Re: What doesnt lend itself to OO?
    ... >>server is a pure data transfer interface. ... essentially exposing the client or service implementation. ... >>paradigms can be abstracted just like any other problem space in an OO ...
    (comp.object)
  • Re: Text terminal rendering design
    ... free to give it any object that satisfies that interface. ... giving it a real facade object if I choose. ... Facade to avoid touching the client. ... completely incompatible with this subsystem interface. ...
    (comp.object)
  • Re: Programming to an Interface
    ... I think the problem you are having with it is related to the point Daniel T. made that you are thinking about it at too low a level of abstraction (i.e., the mechanics of the OOPL de jour rather than as a design issue). ... I think the point here is that one designs the interface first and then the implementation. ... I believe the point here is that interfaces allow one to define responsibilities in terms of invariants, often at a higher level of abstraction than particular implementations. ... One has identical implementation behaviors for both classes but the interface that the client sees is much more generic in the second case. ...
    (comp.object)
  • Re: Abstract public member variales?
    ... Entity has no encapsulation and no real methods, but the great thing about it is that its public interface will never have to change during maintenance. ... Assuming there is at most only one Property instance for each property type, then the R1 collection class would own the smarts for finding the right one. ... However, I would point out that the client of the getter is someone who needs to collaborate with a specific Property, not the Entity itself. ... The second line generates a message to the Property for the collaboration. ...
    (comp.object)
  • Re: Text terminal rendering design
    ... Which is exactly what would happen in your case when the interface of the implementation object changes. ... But then you need a new object to own the actual responsibility within the subsystem implementation. ... I can pop in a facade in a completely painless manner without being forced to rename or kludge anything. ... Facade to avoid touching the client. ...
    (comp.object)