Re: What doesn't lend itself to OO?

From: Mark Nicholls (Nicholls.Mark_at_mtvne.com)
Date: 07/26/04


Date: 26 Jul 2004 03:39:00 -0700


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

Don't get me wrong, but the vanilla (naive) OOA/D approach does not do
this, and such issues are rarely if ever talked about in OOA/D books,
and in fact is absent from most mainstream middleware packages until
very recently, the mantra has been not to worry about where the
objects exist because that has been abstracted away in a puff of OO
magic, sadly the real world intervenes because the problems associated
with interprocess/interapplication/intersystem communication don't sit
well with the direct coupling of state and operation.

We've talked about this before and agreed, i.e. the success of windows
and other such operating systems has far more to do with this sort of
message/message pump architecture than it has to do it's OO properties
and in fact in the context of when these architectures and the state
of OOD at the time, you may well argue that thier lack of OO qualities
was a stroke of luck (though I suspect there were some wise men behind
it).

I have shot myself in the foot this way myself, at one point
constructing a primitive object communication protocol in OS9, only to
my some applications having multiple threads and others having none
(OS9 is supposed to have 1 per process)....oh how I laughed....not.

>
> >
> > "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.

We're on the same page now i.e. if there is an 'object' it is the
system itself.
>
> 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).

I agree.

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

I have just written a new example with GetTime and timezones and then
noticed you have done it for me....

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

This is similar to my rewitten example -
CStatelessService::GetTime(zone) opposed to CGMTTimeZone::GetTime() -
so we're doing well.

>
> 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.
>
I agree

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

not so sure about this, If my GetTime() service was exposed as a
service on the internet and simply accessed a clock, I wouldn't want
it to be statefull.

Though I do agree that the use of DB servers in most business apps
makes the implementation of stateless services relatively easy except
to say that I don't really consider them to be as stateless as client
held state...the server application is maintaining state but via some
sort of handle, client held state to me is much to be prefered because
the server is not responsible for maintaining any state at all.

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

If possible on the client BUT I accept there are a whole raft of
scenarios where this is unreasonable and unwanted i.e. the raison
d'etre (!) for the service is to supply some sort of persistence.

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

It doesn't matter to use, but the OP was asking about things OO was
bad at...I think historically, vanilla OO is bad at this sort of
thing.

>
> 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 agree, but I don't like there implementation, they make schema
changes very messy, and the idea that you don't need to how the wizard
code works a lie.

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

Yep

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

I agree.

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

I don't know, you wouldn't like my code - sometimes I don't - some of
it has to do with working in an inherently multithreaded world where
changing state is a nightmare...I usually just push everything into a
single threaded queue.

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

hmmm I still don't understand and I don't think I agree.

I personally think COM encourages composition over inheritance,
because there is no temptation to use implementation inheritance to
expose hooks. I don't believe a 'Printable Sony Walkman' is any more
likely in COM than in C++, in fact probably less - you know I'm a COM
fan. COM gets a bad rap because of VB, VB gets a bad rap because it's
relatively easy to use - this is a good thing BUT you do tend to get
the more bodge together mentality because more inexperienced people
can use it - I am a big fan, there now appears to be a huge gap in the
market for a simple but serious mainstream development tool in the
market now MS are busy trying to out nerd the java world.

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

Bad implementation does not make a bad tool. COM may encourage this
because it encourages composition over inheritance, there is less
coupling within an application and so it becomes much easier to coble
a bodge together than have to reengineer the system due to a new
requirement.

To me the COM paradigm is good, it encourages a clean seperation of
concerns and a shallow inheritance tree - I have problems with the
text book implementation of multiple inheritance of interfaces on a
class but I see the same problem in C++ and C#.



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?
    ... >>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)