Re: What doesn't lend itself to OO?
From: Mark Nicholls (Nicholls.Mark_at_mtvne.com)
Date: 07/28/04
- Next message: Rod Davison: "Re: Design m-to-m relationship"
- Previous message: Gerry Quinn: "Re: Rework [Was: Static vs. Dynamic typing...]"
- In reply to: H. S. Lahman: "Re: What doesn't lend itself to OO?"
- Next in thread: Thomas Gagne: "Re: What doesn't lend itself to OO?"
- Reply: Thomas Gagne: "Re: What doesn't lend itself to OO?"
- Reply: H. S. Lahman: "Re: What doesn't lend itself to OO?"
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
Date: 28 Jul 2004 05:48:01 -0700
>
> Alas, even there it is mostly in the vendor training classes. [In the
> book I am writing I figure it is important enough that I devoted six
> chapters to it. Alas, progress there is rather rather glacial. B-)
> There is a thumbnail overview on my blog.]
Is it just an RT book or general development, I think there is a hole
in the market for a good OO book that takes subsystem design
serioursly (and similarly an enterprise architecture book that takes
system design within that context seriously).
> >
> > I posted a message here several weeks ago about the nature of
> > decomposing systems especially 'enterprises' into seperate subsystems,
> > I am trapped by BPR consultants on one side doing top down analysis
> > who see a holistic lump of a system and software engineers on the
> > other who also see the ideal as one super system. I suspect because of
> > a history of being a R/T engineer, the idea of breaking a system into
> > cohesive subsystems before designing those systems is natural.
>
> Right, application modularization and layering were developed in R-T/E,
> dating back to OSes like Multics. While those notions migrated to other
> arenas, it was pretty spotty. Layering made it for CRUD/USER processing
> because it was a good basis for automation. Modularization made it in
> the procedural world but got lost when migrating to OO because class
> encapsulation stole the show. The whole idea that a subsystem is just
> an object on steroids where all the same notions of encapsulation,
> implementation hiding, decoupling interfaces, etc., etc. apply just got
> lost in the shuffle.
I agree, I think that early OO was so involved in
subtyping/inheritance it ignored subsystems on the grounds that these
paradigms were difficult to apply directly or irrelevant.
>
> >>Just out of curiosity, though, why shouldn't the clock service be
> >>stateful? It isn't really a resource issue because there only needs to
> >>be one instance.
> >
> >
> > I think we need to be clear about what 'stateless' programming means
> > in this context, I don't particularly think it means state rather than
> > specific identity.
> >
> > If the clock service has identity then the client looks like...
> >
> > // create a specific instance of the clock service on the server
> > CGMTClockService clock = new CGMTClockService();
> > // invoke GetTime on that specific object
> > Console.WriteLine(clock.GetTime());
>
> The first line exists in the server (presumably in the setup code).
> Where does the second line live?
My psuedo code is possibly the MS view (COM or .net) view of remoting
objects between client and server i.e. as far as the client code is
concerned it is unaware that "new CGMTClockService();" creates a local
proxy and instructs the server to constuct the real object.
>
> If, as I suspect, it lives in the client, then I don't like
> "clock.GetTime". The client should be making a generic request for the
> time from the server with no knowledge that the server implements that
> request by instantiating a clock object.
>
It was an example of a 'statefull' service i.e. one with a client
specific identity, and why it was bad.
> <Digression>
> A good model for subsystem interfaces is that they are two-way. The
> external interface is the traditional input interface whose
> implementation dispatches external requests to appropriate objects that
> implement the subsystem. The internal interface is the one that the
> objects implementing the subsystem talk to when they want to send a
> message to the outside world. That interface's implementation provides
> any glue code to resolve syntactic mismatches between the interface the
> subsystem expects and the interface the other subsystem actually
> provides. This addresses a common problem in reuse where different
> contexts can demand difference access to the same service semantics. It
> allows the glue to be isolated in a manner that is completely separate
> from either subsystem implementation. Better yet, it does so in a very
> convenient way so that it can easily be implemented (e.g., linking in a
> Facade class for the glue code).
I think I understood that.
>
> I mention this because my assertion above needs to be qualified. A
> common technique at the OOA level is to provide surrogate objects in the
> model that represent the subsystem's view of the interface. So one
> might have a clock object in the model and one invokes clock.GetTime()
> from it. However, that is the local semantic view of the interface and
> has nothing to do with the semantics of the server that actually has a
> clock implemented. In the OOD/P that clock object will go away and
> clock.GetTime() will be replaced with interfaceFacade.GetTime() or
> however the internal interface Facade class is defined.
> </Digression>
OK I'm with this.
>
> >
> > Now what happens on the server if the client crashes after the 1st
> > line, there is an object with no client, and we need to create all
> > sorts of dreadful strategies for making sure it is still needed -
> > timouts, pinging etc.
> >
> > What happens if I want to create a farm of two servers if clock is
> > created on server A, the load balancer needs to know this else it may
> > send the request to server B and this specific clock doesn't exist
> > there.
>
> I agree, we have different views of statelessness. B-) I see this as
> an orthogonal problem related to registration, protocols, and
> implementing a bunch of interoperability requirements.
>
I agree they are mildly different but state => identity, if there is
only 1 set of state then we need only 1 identity i.e. a singleton i.e.
the subsystem itself, but the principle problems to me are associated
with the servers supplying multiple objects (identities) to the
outside world, and the overhead associated with maintaining these
objects. Not orthogonal but a subset of the same issue.
> I see stateless objects as a mechanism to deal with limited resources.
> If objects have state variables, each instance needs to be created to
> persist those variables. If there are a lot of them (e.g., many
> shopping carts active at a POS site or, worse, many cached web pages),
> one will run out of memory and begin page faulting and the DB is enough
> of a bottleneck. One also has the problem of dealing with discarded
> objects (i.e., the client went away leaving stuff behind), otherwise
> physical disk space becomes a limitation.
I agree but
CGMTClockService and CCETClockService are both stateless but I still
wouldn't implement them as individually instantiatable (by the client)
objects but either as 1 stateless service or (debtably) 2 stateless
services - I don't have to though, and vanilla OO would not do this,
it would do as I have done above and did so for many moons.
>
> So the answer is to implement objects on the server that don't have
> state variables. All the necessary state data to process is provided
> with the request or accessed as needed from the DB. More important,
> when the particular request processing has completed, the state data
> disappears. Of course once the objects have no state variables, there
> is really no reason for multiple instances; the instance identity can
> also be supplied as input data. So one abstracts single instances of
> generic objects. IOW, stateless objects are just a variation on the
> ancient idea of reentrant device drivers.
I can't comment about "reentrant device drivers", but we're largely on
the same page.
>
> >
> > Note clock really has no 'state' except it's class and this is
> > important because
> > CGMTClockService does something different to CCETClockService.
> >
> > So don't do it, just do the equivalent of
> >
> > Console.WriteLine(CClockService.GetTime(GMT));
>
> We are back in agreement here, except that I interpret CClockService as
> the server interface Facade (or a surrogate for it that knows how to
> reach it over the network).
yep.
>
> >
> > there is only one request so it can be farmed, load balanced and any
> > state data required is created and released in a single request.
> >
> > a functional API.
>
> Right. Subsystem interfaces tend to be indistinguishable from
> functional APIs when developing synchronously. [In the translation
> world our interfaces are always event-based so there is technically only
> one interface function, acceptEvent(event). So it tends to be less
> obvious. But the set of events accepted by the subsystem interface can
> be regarded as a set of functional requests.]
yep
>
> >>>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.
> >>
> >>There's no free lunch; wherever state is maintained persistently there
> >>is a downside. If it is in the server, then one has resource issues in
> >>a multi-client environment. If it is in the DB, then one has the
> >>performance hit of additional physical I/O access and one increases the
> >>likelihood of needing deadlock resolution. If it is in the client, then
> >>the client needs to know a lot about the persistence limitations and
> >>there will be encoding/decoding overhead into generic formats like XML.
> >> (Not to mention that most of the client state will originally come
> >>from the DB through the server anyway; all one is really doing is
> >>optimizing the access.)
> >>
> >
> >
> > If no persistence is required, then there would seem to be no need for
> > the extra headache. Keep the state on the client and make the
> > interface a completely stateless one, all state needs to be passed in
> > to get a given result.
>
> You've only removed the server headache. In doing so you have given the
> client a brand new set of headaches. Client requests that were
> conceptually separate before because of inherent sequencing in the
> problem solution are no longer independent and each request may have to
> be aware of others. Basically we have:
>
> Request A provides state data {S1, S2, S3}
>
> Request B provides state data {S4} but there server needs {S2, S3} as
> well to process {S4}.
>
> If B always follows A in the solution, there no reason for whoever
> generates B to worry about any data other than {S4} -- providing the
> server persists {S2, S3}. Otherwise, whoever generates B must be aware
> that the {S2, S3} data from A is needed and include it.
???
If the server holds absolutely no state then the client simply orders
his requests in the correct sequence to provide the correct behaviour
???
>
>
> >
> >
> >>Essentially one has made every client responsible for also managing and
> >>optimizing DB access in addition to solving the customer's problem.
> >>That duplication fine if all the customer is doing is ad hoc reporting
> >>and data entry because it can be largely reused as in RAD IDEs. But
> >>when the applications are also solving significant problems, one has to
> >>touch /all/ those solutions when something changes in DB-land. That's a
> >>maintainability problem, which must be important because one is doing OO
> >>in the first place.
> >>
> >
> >
> > If you need to put stuff in the DB, I would put it in and keep that
> > state in the DB server.
>
> Then there will be a whole lot more physical I/O on the DB that makes
> the critical path performance bottleneck even worse.
We talking past each other,
If i say keep the state on the client, you have a problem, if I say
keep it on the server, you have a problem...I've missed something...or
you have.
>
> >>>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.
> >>
> >>Use state machines. B-) If the only problem is data consistency during
> >>concurrent execution (as opposed to hard R-T constraints), then all one
> >>needs to do is provide multiple instances of the event queue manager.
> >>The event doesn't get on the queue until the data is consistent,
> >>regardless of whether there are one or many queues, so data consistency
> >>comes for free in just getting the asynchronous processing to work for
> >>the object state machine interactions.
> >
> >
> > I am currently either creating objects with readonly state, and/or
> > creating a single threaded queue and/or passing data by value.
> >
> >
> >>One can then allocate object state machines to event queue managers for
> >>concurrency and put each event queue manager in its own thread. Simple
> >>blocking constraints can even be supported via semaphores in the event
> >>queue managers rather than invoking the <expensive> thread pausing
> >>mechanisms.
> >>
> >
> >
> > hmmm, can't quite see this.
>
> Which part, blocking for data integrity or queue manager per thread for
> concurrency? (The answer might be lengthy to either, so I don't want to
> answer until I know which one.)
if "blocking for data integrity" means ensuring single threaded access
to internal state within an object i.e. lock(this) then you need not
explain...I understand this but dislike it as I see it as an
unnecessary overhead associated with usually unnecessary
multithreading and it's very easy to get deadlocks etc.
I also implement a single threaded queue manager and push my
environments multithreaded events into it.
what I don't understand is your use of state machines.
>
> >
> >
> >>[If blocking is necessary, it has to be managed somehow. Doing it by
> >>pausing state machines, though, is more aspect-like (i.e., the code
> >>looks the same everywhere) and is easier to manage because the scope is
> >>at the state action (method) level and one can count on the FSM rules to
> >>ensure that scope.]
> >>
> >
> >
> > interesting but not with it.
>
> Do you mean I didn't explain it well or that it is irrelevant to your
> applications?
I don't understand it (I do know what a FSM is but not it's
application in this context), I need a very simple example e.g.
something trivial like.
class CFoo
{
private int x;
private int xPlusOne;
void SetX(int y)
{
x = y;
xPlusOne = y+1;
assert(x + 1 == xPlusOne); // sometimes this fails.
}
}
how do you prevent this using a FSM.
>
> >>We seem to have different views of COM. B-) To me the primary
> >>mechanism of COM composition is mixin-like inheritance. In one wants to
> >>add a streaming facility to the component in hand, it is inherited from
> >>Streamable (or whatever it is called -- it's been a long time since I
> >>was exposed to COM and I wasn't paying much attention even then).
> >
> >
> > How would you do it in C++. If you would use MI in C++ then you can MI
> > the interface and message forward (weak delegation), if you would do
> > it another way, and I expect you would, the COM would allow you to do
> > that.
>
> The way I see COM is that it is equivalent to C++ MI of implementations.
> I am not a fan of MI on cohesion grounds (Printable Sony Walkmans).
> Nor am I a fan of implementation inheritance; it seemed like a good idea
> at the time but opened a Pandora's Box of foot-shooting.
But as I say it does not I believe encourage this any more that MI of
completely abstract base classes.
>
> >
> > You can't do implementation inheritance - so delegate.
>
> This would be my choice for a variety of reasons beyond MI and
> implementation inheritance. Among other things, subclassing relations
> are static so they can't be modified without touching the code.
> Delegation, OTOH, is very useful for parametric polymorphism. That
> allows one to encode invariant behaviors while parameterizing detailed
> differences in external data. Then dynamic relationship instantiation
> can be used to change the solution without touching the code.
I agree, though I do now use implementation inheritance with a
template method for the first time seriously the other day, and then
after a bit of refactoring wished I hadn't.
>
> >>>>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.
> >>
> >>And when one has collected together a flock of
> >>almost-but-not-quite-the-same widgets and the requirements change, how
> >>does one decide which widgets need to be modified or replaced? One can
> >>guess that only a specific context is affected and modify that. Then
> >>one must have a whole lot of faith in one's test suite.
> >
> >
> > Again to "... collect together a flock of
> > almost-but-not-quite-the-same widgets", would not be good practice, to
> > have them available is nice and better than not having them available.
>
> You are losing me here. The only way to avoid that is by modifying the
> existing widget for the new requirement rather then composing a new,
> very similar COM widget. But then one is back doing what you argue COM
> avoids -- re-engineering the application. That's because the only way
> one widget can serve all contexts is by ensuring all contexts are
> consistent with the requirements change vis a vis side effects.
>
We missed each other - I thought you were talking about purchasing and
using at the same time components from a 3rd prty that do almost the
same thing.
But I still don't agree, I see nothing in COM that makes the
production of multiple very similar widgets any more likely that in
C++.
> >
> > I would not subscribe to the view that a tool should be complex and
> > difficult to understand and use, and not have as many 3rd party
> > components available as possible - if the cost of this is that less
> > experienced people can build bad software then so be it, I don't blame
> > the tool for that, it is an unfortunate consequence of it's success.
> >
> >
> >>Alternatively, one can go examine every context where each flock member
> >>is used to see which widgets are affected by the requirements change.
> >>That puts one knee deep in Spaghetti Code. B-).
> >
> >
> > Hopefully, being a wise and sensible developer you would choose
> > components from reliable sources.
>
> Again we seem to be talking past one another. I am talking about the
> COM objects that the developer cobbles together as building blocks to
> form the application, not the fundamental library entities. Those
> objects are constructed from other COM objects via composition in a
> bottom-up fashion.
>
If you have a tool where you can declare interfaces, and implement
them, delegate weak and strong, compose aggregate and all the rest why
should the lack of implementation inheritance make this any more
likely....you may genuinely not like a lot of COM code that you have
come across but that is not COM's fault except to say that if it's
easy to create such component you may get less competent people doing
it.
> >
> >
> >>But the real issue here is architectural drift. That increases the
> >>chances that a relatively minor change in the problem space will trigger
> >>a massive refactoring of the application. That sort of refactoring is
> >>independent of whether COM techniques were used; is is a direct result
> >>of the software structure not matching the problem space structure,
> >>regardless of the construction techniques. My point is the the COM
> >>composition paradigm strongly encourages architectural drift. IOW, it
> >>may be somewhat more difficult now to implement an individual change
> >>while preserving the problem space structure than doing so while
> >>ignoring the problem space structure, but one will pay for it later.
> >>
> >
> >
> > Again, it encourages decoupled code, and this can be abused - but I
> > would hope that you were a champion of high cohesion and low coupling,
> > we can if you want return to the days of deep inheritance tree's where
> > every small shift in requirements triggers the avalanche - I prefer
> > that my architecture to be robust and flexible enough to absorb many
> > shifts before it collapses around me.
>
> Alas, I see COM objects developed for a specific application as the
> antithesis of high cohesion! I agree COM reduces inheritance breadth
> effectively to one level. But that first level is a killer because it
> is arbitrarily broad due the MI of composition. More important, that MI
> is completely arbitrary; there is no notion of the cohesion resulting
> from problem space abstraction around unique and well-defined entities.
>
I see objects developed for a specific application as the antithesis
of high cohesion, but I see no need to specifically insert the word
COM in this sentence.
I have programmed VB6 for many years and produced effective soft real
time apps, transactional server apps and lots of other noddy stuff in
a fraction of the time it would take in C++, I admit to having
mutliply inherited interfaces, but only in very specific times and
stylalised ways - after my sofware crissis and our discusions of roles
I no longer have to do this at all if I so choose - but that is a
wider question of OOP style independent of COM.
- Next message: Rod Davison: "Re: Design m-to-m relationship"
- Previous message: Gerry Quinn: "Re: Rework [Was: Static vs. Dynamic typing...]"
- In reply to: H. S. Lahman: "Re: What doesn't lend itself to OO?"
- Next in thread: Thomas Gagne: "Re: What doesn't lend itself to OO?"
- Reply: Thomas Gagne: "Re: What doesn't lend itself to OO?"
- Reply: H. S. Lahman: "Re: What doesn't lend itself to OO?"
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
Relevant Pages
|