Re: What doesn't lend itself to OO?

From: H. S. Lahman (h.lahman_at_verizon.net)
Date: 07/24/04


Date: Sat, 24 Jul 2004 16:17:22 GMT

Responding to Nicholls...

>>I would point out, though, that clients and servers are different
>>critters. The most efficient and robust interface between clients and
>>server is a pure data transfer interface. So the client should be
>>completely indifferent to whether the server is constructed with
>>stateless objects or not and vice versa -- the interface will look the
>>same either way so the respective implementations are completely decoupled.
>>
>
>
> I think I agree with this, if I understand it, but I think that a
> "pure data transfer interface" is not a particularly natural OO beast
> - I have no problem using it, and have no problem with it being un OO
> (it seems an aestetic point).

A pure data transfer interface consists of messages that have a message
identifier and an <optional> data packet. The message identifier
consists of a message type, which is used to map the data packet, and an
<optional> unique message identifier, which may be used to synchronize
responses.

The data packet values are all by-value. The structure of the data
packet is defined by the message and it is encoded and decoded by sender
and receiver, respectively using their own semantics for the message
type. In an event-based system an event is a classic example of a pure
data transfer interface.

That is quintessential OO because it is about using pure messages to
decouple implementations. The message itself is an object but it is a
dumb data holder that is constructed solely for the collaboration. It
has no semantics in terms of the problem in hand; just the semantics of
data transfer. That decoupling of semantics through the message is why
message and method are separated in OOA/D.

Contrast that with the idea of passing object references or accessing
objects remotely. The semantic coupling is horrendous and one is
essentially exposing the client or service implementation. Even worse,
one is inviting the other process to modify the object or access it in a
completely open-ended and unpredictable fashion. Essentially one has
unlimited opportunity for foot-shooting.

>
> "the interface will look the
> same either way so the respective implementations are completely
> decoupled"
>
> I'm not so sure about this, or I don't completely understand.

Since this message type and data packet are uniquely interpreted by
sender and receiver (i.e., each may have a different semantic view) and
the data packet exists only until the message is consumed, the sender
and receiver implementations are completely decoupled. Each side only
understands its view of the message content.

>
> i.e.
>
> statefull approach
>
> class CRemoteClock
> {
> private Time time;
>
> CRemoteClock(Time time)
> {
> this.time = time;
> }
>
> void SetTime()
> {
> //set the time.
> }
> }
>
> Stateless approach
>
> class CRemoteClockAPI
> {
> static void SetTime(Time time)
> {
> //set the time.
> }
> }
>
> The interfaces look different to me - traditional COM/CORBA objects
> look like CRemoteClock, stateless ones like web services etc look like
> CRemoteClockAPI.

You need something for CRemoteClock to do that needs a time value to
demonstrate the difference. The stateless approach would have no
SetTime accessor because there would be no unique instance to preserve
that time until the behavior was invoked. If a CRemoteClock method
needed the time it would be passed as an argument by the client or it
would be accessed synchronously from the DB or client.

However, the real issue here is that the client should have no knowledge
of CRemoteClock, not even its existence, when it sends a message. There
was an implicit assumption that I didn't make clear: in a client/server
environment there have to be interfaces that encapsulate the entire
client and the entire server. Those interfaces are the ones that are
pure data transfer and they dispatch to the internal objects like
CRemoteClock.

However, you are correct that I made another assumption that is much
less general. I was using the IT view of client/server where the server
is a DB and processing is transaction-based. In that world the layer
interface will have self-contained messages (i.e., queries) so the layer
interface will always look the same way. Whether and how objects like
CRemoteClock are instantiated to respond to the transaction will depend
upon stateful vs. stateless strategy.

When the processing is more complex than traditional IT transaction
processing, the interface will be different depending upon whether the
target is stateless or stateful. That's because the message may not be
self-contained. If the server is stateless, then it is possible for it
to access state information from client or DB synchronously on an
as-needed basis to respond to the message. However, it will usually be
more efficient, if the client has the data, for the client to include it
in the message (e.g., as an XML string to be parsed).

To clarify my point here, consider your example of a clock. The only
way such an entity could be stateless is if it read the current time
from the hardware when accessed. [Having the client pass the time when
asking for it would be somewhat pointless. B-)] So if the only thing
the client actually accesses from the clock is GetTime(), then the
interface will look the same whether time is stored as an instance
attribute or not.

Now suppose our clock is fancier and it needs to synchronize clock ticks
with a calendar. Now we need a SynchTime(calendar time) behavior. A
stateless version of the clock might store that in hardware as well or
as a static attribute. The stateful version might store the synch value
as an instance attribute. The interfaces that client sees are still the
same.

Unfortunately this is a tough example because so far there is no need
for more than one instance, regardless of stateless vs. stateful. So
let's assume we want to synchronize for all time zones. In a stateful
version we could have an instance for each time zone, each having its
own synch value. The GetTime(zone) interface would use the zone to
dispatch to the correct instance.

In a stateless version the synch values would have to be stored in the
DB or in hardware. Alternatively, it could be passed in with
GetTime(calendar synch value) because there would by no SynchTime method
since the time is not persisted. To that extent the strategy could
affect the interface.

However, as a practical matter I think passing the calendar synch value
for time zones would be a Bad Idea because it exposes the implementation
(i.e., the client of GetTime needs to understand too much about the
mechanisms of synching to the calendar). One is going to have to
identify the time zone somehow (i.e., SynchTime(zone) and GetTime(zone))
anyway because the server interface has to do the dispatching properly.
  It would be better interface design to look for a more generic way to
do that.

For example, it would probably to better to provide SynchTime(zone,
calendar synch value) anyway. The server implementation can still avoid
multiple instances by providing a static lookup table that a single
instance of CRemoteClock.GetTime(zone) accesses. Of course that won't
work in situations where the data is more complex and one gets back to
the fundamental trade-off of where to keep state: client, server, or DB.

However, I would point out that the server interface should be driven by
the semantics of the service and what data it needs to provide its
services. While that may involve a strategic decision about keeping
data in DB, server, or client, the interface should still be as generic
as possible. IOW, one should seek a way to use GetTime(zone) rather
than GetTime(calendar synch value) when implementing the strategy.

In addition I would point out that the performance problem that dictates
stateless objects pretty much only exists on DB servers and middleware
where the main problem is processing large gobs of data rather than
solving business problems. The reason transaction processing lives on
despite the demise of batch processing is that it is a good paradigm for
data processing. So in those sorts of environments one already has to
live with that paradigm. So the issue of self-contained messages
becomes pretty academic.

In other environments where the software is solving business problems
the collaborations will generally by more complex and will inherently
require some degree of persistence in the service between client
requests. The remote clock handling multiple time zones and calendar
synchronization is an example of that. In those situations it is almost
always better to provide the persistence in the service or in some
common repository like a DB or shared memory.

>
>
>>I would also point out that servers have unique problem spaces where
>>different paradigms -- including statelessness -- apply. Those
>>paradigms can be abstracted just like any other problem space in an OO
>>manner. That will naturally result in a bunch of dumb data holder
>>objects like Request, Message, and Buffer that are ephemeral and a few
>>control-rich objects like Dispatcher, Query, and XMLPaser with only
>>scope variables.
>
>
> I accept this, but its a bit like the burger/sausage thing, if I make
> a long think pork burger is it a burger of a sausage, i.e. when does
> an OO approach really become a procedural one?

I would argue that it doesn't matter. One abstracts the problem space.
  As you have no doubt observed, I am a strong advocate of isolating DB
access in a subsystem of an <client> application. When one does that
the interface is dictated by the problem solution (e.g., "Store this
pile of data I call X so that I can get it back later using that name").
  The subsystem does the mapping of that data into an RDB schema.

When one does that for a couple of applications, one finds one is doing
pretty much the same thing each time. If one steps back and looks at
the invariants one realizes that all RDBs are exactly the same and the
crucial objects of that problem space are things like Schema, Table,
Tuple, and Field. It then becomes relatively easy to design a subsystem
that is configured by external configuration data for the particular
schemas the application in hand needs.

Then one can reuse the same RDB access subsystem for large sets of
applications. All one has to provide locally for an application is a
configuration file to cross reference with the DB schema and a Facade
for the particular application's access needs. The only reason it won't
work for all applications is special optimizations that might have to be
made, such a caching, or unusual situations, such as creating schemas
programmatically. Note that the RAD IDEs figured this out many, many
moons ago. B-)

I argue that if one looks at the server problem space, then one will
abstract invariants for that problem space naturally in the same manner.
  In addition, once the <stateful> OOA is done, elaborating an OOD
optimization strategy for stateless objects will be straight forward.
If full code generators can do it routinely for translation, so can
developers manually elaborating an OOD.

Will the final 3GL code be OO? Maybe not superficially and that may
make it more difficult to maintain. However, performance is not a forte
of OO development and one may have to make compromises. The OOPLs
already do through the use of procedural message passing and procedural
block structuring. Also, one has to make compromises on performance
even in procedural developments (e.g., at the heart of most large
performance critical applications there is going to be some Assembly
code somewhere; we had an entire DSP command interpreter burned in
binary in an EPROM). My point is, that if some compromises with
performance have to be made, that's life in software. Where things get
out of hand is building the software around such techniques.

>
> If I did this for all my code (and I do it more than most e.g. my
> likeing for dumb readonly shared composites and behaviour rich
> visitors) you would suggest I wasn't doing OO I think.

How much of that is because you are dealing with "canned" layered model
infrastructures that exposes too much about the data layer? Sadly, most
of those infrastructures aren't very OO despite the marketing hype.
That's just another reason to isolate persistence and UI in application
subsystems whenever one is beyond CRUD/USER pipeline applications.

>>Where the problem arises is when "canned" infrastructures designed to
>>hide the client/server boundary as an application layer define a layer
>>interface that exposes the server implementation model. That forces the
>>client application to employ the server implementation model. That is
>>bad OO.
>>
>
> hmm, you'll have to illustrate.

Consider COM components. They utilize a very mixin-based composition
approach. That is great for providing highly general infrastructures
where one can't anticipate the final structure. But it is not a very OO
approach for individual applications because it trashes cohesion and
deviates from the problem space (e.g., Printable Sony Walkman). That
composition bias is very apparent because inheritance is the primary
vehicle for cobbling things together so it tends to foster that sort of
construction at the individual application level.

In contrast, individual applications are abstracted to a very particular
problem in hand. So if one routinely applies the COM composition
paradigm one ends up bypassing OO maintainability in favor of the view
that it is easier to cobble together a new component for a specific
change than to worry about modifying the existing application to be in
synch with the problem space. The result is enormous code bloat.
Worse, it leads to architectural drift in the applications away form the
problem space leading to a maintainability nightmare when the
application matures.

*************
There is nothing wrong with me that could
not be cured by a capful of Drano.

H. S. Lahman
hsl@pathfindermda.com
Pathfinder Solutions -- Put MDA to Work
http://www.pathfindermda.com
(888)-OOA-PATH



Relevant Pages

  • Re: What doesnt lend itself to OO?
    ... The whole idea that a subsystem is just ... > The first line exists in the server. ... objects between client and server i.e. as far as the client code is ... > external interface is the traditional input interface whose ...
    (comp.object)
  • Re: What doesnt lend itself to OO?
    ... >> proxy and instructs the server to constuct the real object. ... rather than client code. ... If 'clock' is instantiated in the server, ... > for the server interface at the OOA level. ...
    (comp.object)
  • Re: What doesnt lend itself to OO?
    ... >> The problem with stateless programming is that it turns object ... > server is a pure data transfer interface. ... So the client should be ...
    (comp.object)
  • Re: What doesnt lend itself to OO?
    ... > A pure data transfer interface consists of messages that have a message ... > essentially exposing the client or service implementation. ... stateless ones like web services etc look like ... I was using the IT view of client/server where the server ...
    (comp.object)
  • This is going straight to the pool room
    ... or not the client has privilege to do what they're trying to do, ... The server environment is this: ... 3GL User action Routines that Tier3 will execute on your behalf during the ... Routine Name: USER_INIT ...
    (comp.os.vms)