Re: implementing roles in OOP......

From: Mark Nicholls (Nicholls.Mark_at_mtvne.com)
Date: 03/31/04


Date: 31 Mar 2004 03:38:50 -0800

OK we may be getting somewhere - i.e. I want to disagree and I am also
becoming unsure of my personal paradigm => maybe I can learn something
(or maybe you can) but at the moment I'm the one with the iceberg and
the sinking ship, so it's probably me.

I may do a bit of pruning though to make the post more manageble and
focus on the areas that I understand/disagree/am becoming unsure of.

I want to drop the state machine topic to a degree - I think you have
convinced me at least at one or more levels that they can be
considered orthogonal except to say an aside.......

I personally do not distinguish between an an object invoking a method
and an object setting a public attribute - to the degree that my
classes and interfaces contains no reference to public attributes - to
me all state is internal - this view is based on the fact that how I
implement state within the class is no business of any other class so
declaring a public attribute may be misleading.

I suspect you will disagree with this, but I am interested to know
why.

I am interested in the cohesion debate because it may blow holes in my
current approach.

>
> Mostly it is a matter of experience and judgment. We will need a lot of
> methodological advances before our craft becomes true Engineering.
> However, there are useful rules of thumb...
>
> Size is an obvious indicator. When the number of individual properties
> gets beyond a dozen or so, it is time to think about cohesion and
> delegation. There are valid exceptions, so such a guideline is hardly
> written in stone. The point is simply when the threshold is exceeded
> one has to consciously justify it.

Not guilty - in fact my interfaces methods and state variables seem to
be getting fewer and fewer.

>
> Complexity is another. I do state machines for behavior and as soon as
> I think I need more than half a dozen states I re-evaluate cohesion.
> And if I am even tempted to use Harel state machines I /know/ cohesion
> is broken. [An inside joke I couldn't resist. Steve Mellor and Bran
> Selic lead two opposing camps on state machine construction. They had a
> debate reprinted in IEEE Software and the moderator indicated they
> weren't on speaking terms at the end. B-)]

Also not guilty - if I had multiple (>7 say) states I would look to
see if I could compose them by the cross product of two or more state
spaces.

>
> Whether one uses state machines of not, large actions or methods are
> often a symptom of lack of cohesion. I tend to start wondering about
> cohesion at half a dozen statements. [Another guideline with major
> exceptions. Algorithmic processing that is defined outside the specific
> problem (e.g., mathematically) is often buried in methods or function
> libraries.]
>

Also not guilty- number of statements are becoming concerningly low
i.e. often 1!

> Decisions are another aspect of complexity to watch. A lot of decision
> conditions in a method can mean trouble because each condition
> represents a different business rule. The more there are, the more
> likely that future maintenance will require them to be split up to
> provide different collaborations. IOW, many business rules in a method
> implies lack of logical indivisibility. Splitting those up into
> individual methods then raises the property count. Again, nothing to
> write in stone but worth evaluating on a case-by-case basis.

Also becoming worryingly few - if I have an 'if' statement it concerns
me!!!!!

>
> Collaborations are another clue. When behavior responsibilities are
> exclusively accessed by two other classes or groups of classes that are
> clearly quite different contexts, that suggests the object in hand is
> providing disparate concerns.

I don't think so.

>
> Another clue lies in "spider" collaborations where the object
> collaborates with many other objects. (Note that collaboration is done
> over relationship /paths/ so the number of direct relationships is not a
> good diagnostic.) That usually indicates a god or controller object
> that knows far too much about how other objects collaborate. IOW, its
> implementation captures overall solution sequencing.
>

I'm not sure - my code is slowly becoming like a ball of tumbleweed
with each twig of weed a different concern intertwined with others
concerns my problem here I suspect where these concerns interact.

On the basis of your rundown, I'm 90% sure I am not guilty of low
cohestion.

My final question in this line though is why are high cohesion and low
coupling supposed to be competing aims - this may be the root of my
confusion - to me they seem to be complementary?

>
> Let me try to rephrase my view. As a translationist I see automation of
> the computing space as inevitable. Jacobson and I may disagree about
> how long it will take, but the handwriting is clear that OOP developers
> will eventually be as rare as Assembly developers are today. However,
> automation has its price in performance. No 3GL program will be as fast
> as hand tuned Assembly and no OOPL program will be as fast as a
> procedural 3GL program. Nor do I expect any executable generated
> directly from an OOA model to be as fast as a hand-crafted OOPL program.
> However, optimization at each level can be as good as possible because
> everything plays together _at that level_.

I'm not a follower of the 'big red button' and I don't believe the
streets will be lined with 3GL programmers with their begging bowles -
to me designing in code or UML are relatively equivalent activities (I
know that may ring alarm bells), the problem with UML etc is that
it's abstraction is too high and too static to really get into the
nitty griity and to be able to rigourously test, and the problem with
3GL code is that the semantic level is too low to be able really
really be productive - many of the finest UML designs have completely
fallen to pieces when examined rigourously in code but many an
expertly crafted 3GL application has disolved into a inconsistent mess
- to me the world will merge - there will be no 'step change'.

I actually don't mind the 'big red button' as long as it doesn't
precepose that a programmer is than going to go in and tweak the code.

>
> Each of those levels represents a unique level of abstraction and a
> unique suite of generic construction paradigms. Where I have a problem
> is with providing local infrastructures solely to make life easier for
> the developer _at a particular level of abstraction_. If one is going
> to make life easier for the developer by raising the level of
> abstraction, then do it consistently across the entire level rather than
> piecemeal.

I agree but there is an (to abuse mathematics to an extreme) injective
mapping from the OOD to the OOP and from the OOA to the OOD.

>
> If I am programming at the Assembly level, I make do with the tools I
> have rather than using macros to introduce procedures. There is no way
> I can do that as efficiently and accurately as the 3GL compiler using
> block structuring for scope and modern OS and hardware support (e.g., a
> processor stack pointer register). If I am programming in C I make use
> of structs and functions in physical modules without introducing logical
> infrastructures like classes. There is no way I can do that as well as
> a well-designed OOPL can. If I am programming in C++ I deal explicitly
> with relationship collection classes because there is no way I can fully
> abstract out implementation dependencies in a local infrastructure as
> well as an OOA MDA profile.
>

I really don't understand this view - why program with one hand tied
behind your back.

If you're a C programmer and read Rumbaugh or Booch (as I did) you
immediately want to leverage this new paradigm, I can (and did) very
easily create OOP in C, and as I am sure you know C++ originally was a
preprocessor. X windows was designed in just such a way - I think.

To me you start with C....
then you stylelise how you write it to get v-tables, encapsulation and
polymorphism.
then you formalise that into a preprocessed language.
then you formalise further until you have the final product C++.

While I personally hate C++ as a license to write bugs, I still accept
that it is possible for OOP to evolve out of .....whatever we call non
OOP - (FP?).

>
> This comes back to my point above about being consistent with the
> paradigm. That sort of thing is fine for an AOP environment where one
> has tool support for managing cross-cutting properties. But in a
> traditional OO environment it thoroughly trashes cohesion and
> encapsulation, two of the fundamental techniques of OO construction.

As above....the visitor is an AOP enabler - it may not be perfect but
if I can encapsulate aspects of a system within a visitor, I will do
so in the name of good OO design (this is why I believe OO spans AO
but is sematically too weak to do it as well as a specifically
designed tool - it can be demonstated that I can write a weakened form
of OOP in a non OO language C (Rumbaugh), so why not?).

>
> > While I accept that, I will persevere - I'm fed up with writing the
> > same code (semantically) time and time again to be thwarted by rigid
> > syntax.
>
> Then become a translationist so you don't have to worry about that icky
> code at all. B-)
>

There may be some sense in that.

>
> If you find you are reinventing classes or functionality, that's another
> story. One should never have to encode a collection class for a mundane
> 1:* relationship. That's a job for class library reuse; just declare

"class library reuse" - bingo - effectively thats what I'm trying to
do.

BUT it isn't easy - at all.

>
> Somehow I think there is something else going on. (That opinion gets
> confirmed further below.)
>
> Facade is routine for subsystem interfaces and the Factory family is
> also routine for instantiation from external configuration data from
> long before GoF. I've used State and Strategy fairly often. I am
> pretty sure I used Composite and Observer at least once. I may have
> used a couple of the other GoF patterns. But I can't recall a situation
> where I needed to directly combine GoF patterns.
>
> So I haven't been "constantly reconstructing" using design patterns.
> Now either we are doing very different software or one of use is doing
> something seriously wrong. B-)

Fantastic - at the moment I don't really care - if I'm doing something
seriously wrong then it's best I find out about it as soon as
possible.

>
> If you encapsulate the traversal algorithm elsewhere, then it is none of
> Component's business. The navigation is explicit in the relationship
> instantiation, which is an external concern from Component's intrinsic
> semantics. So I see nothing about a Component that needs to be
> "visitable". (Alas, this disconnect gets worse below.)
>

This is where it gets good - because I don't completely understand
from here on, and this is stuff I think I can confidently talk about -
which is good :-).

I may learn something and the mists may clear.

I agree with you but I wouldn't read too much into 'visitable' -
visitable is simply a mechanism (to me at least) by which an object
can declare it's membership to a set of aspects or behaviours.

To me, in the pure GoF interpretation, class CFoo declares itself to
be a member of class CFoo by telling the visitor (by invoking
VisitCFoo), I must admit I extend it's usage to allow it to declare
itself to be a member of all sorts of sets of classes and multiple
sets, i.e. a CDog class may well call VisitMammal and VisitDog, in
this way the visitor can interogate the object for all sorts of
'aspects' of it's behaviour and process it accordingly.

> >
> >
> >>However, I think there is a more fundamental issue here. We basically have:
> >>
> >> *
> >>[Car] <#>--------------- [Component]
> >>
> >>That 1:* relationship is going to be implemented as a collection class.
> >> Whoever needs to know how many Components a particular Car has will
> >>navigate to Car then, through Car, to that collection. It will then ask
> >>the collection object because that's pretty much what it is about.

This is good because I want car to be a Composite from day 1 - and you
don't.

I want to combine the two roles in some manner and you don't.

But what I don't see, in general, is how to keep these roles seperate?

What does your collection class contain?

Would it hold 4 references to CWheels and they in turn implement
GetComponentList and this GetComponentList is declared in an interface
to provide polymorphic behaviour so that my traversing object does not
need to know exactly what object it's talking.

I would suggest at this point that both CWheel and CCar are now
composites! (GetComponentList is not a million miles from
'GetChild(i)' GoF) and are thus now implementing 2 roles.

IF your references in your collection class does not contain a direct
link to CWheel but to the ComponentList associated with wheel I have a
pretty meaningless list of lists with no way of navigating to the
meaningful object - CWheel or CCar.

(from my perspective) they would still at this point need to implement
Visitable or at give access to something that is Visitable so that
they can declare themselves to be members of their own class, either
that or we get what I consider to be horrendous i.e. reflexion on the
object followed by a switch case list - bluuueeegh - also note I that
is generally all I do in a AcceptVisitor method - I do not generally
embed traversal of children (but I don't think this really matters).

So lets say it returns an object in some manner - lets say it creates
it - that is Visitable and it's sole role is to declare its
creator/owner as a CCar via a VisitCar method on the visitor.

Fine.

But this is where I started - this is the scenario (apart from syntax)
and the solution that I originally proposed.

CCar has three ways of providing support for Visitable,Car and
Composite either.

i) it creates a new object on request to implement that role
ii) it implements that role itself.
iii) it returns an 'owned' object that implements that role.

Here it would seem that Car implements car via ii)
Implements Composite via iii)
And implements Visitable via i)

And it's not completely fine, actually Car still supports 3
polymorphic interfaces i.e. Car, GetComponentList,GetVisitable -
though I would argue that these interfaces do not constitute explicit
roles but are simply a mechanism for navigation.

This is NOT the text book implementation - the text book would
'extend' the class by implementing those roles itself directly and
navigation via casting between each interface.

>From the OOA point of view of abstraction I have no problem with the
thought that Car and Wheel in some way satisfies these three roles,
but from an OOD/P perpspective I do.

Even this solution makes me worried - I don't like the circular
reference between a Car and the reference the ComponentList must have
back to the Car - so actually when the traverser gets the
ComponentList I would probably create an object that holds and exposes
the reference to the Car/Wheel and delegates to a hidden component
list.

I am also now worried that I have several objects potentially
accessing the same shared state.

What if car does something that effects the ComponentList i.e. void
Crash() - which destroys some elements of the ComponentList.

My current solution is to only return ComponentLists by value! thus if
some traversing object is navigating the ComponentList and Crash()
happens the navigator only has a consistent possible out of date
snapshot rather than risk inconsistent state.

I now think I'm potentially in a mess - I have copies of my state is
either splattered all over the place or potentially inconsistent
state, if I'd done it the textbook way I personally think the solution
would have been cleaner and simpler - but still wrong and a god.

The iceberg looms, the claxon rings, it's time to man the life boats
and get onto that plumbing course.

>
> Note the quoted sentence above and the quoted paragraph below relative to...

I couldn't find the quoted sentence!

>
> Car is not the Composite pattern, Component is. ComponentList
> understands the navigation of the Composite pattern because we don't
> want to burden Car with that; Car has its own set of concerns, like
> Drive(...).

see above, it either does it itself i.e. IS a composite component, or
navigates to GetComponentList.

>
> What does this Visitable role actually do? I think I need some words
> around this because I really have no idea what it could be doing in this
> example context.
>

see above - in my generlised usage it declares the class to be a
member of some set (possibly dynamically) without the use of reflexion
and switch/case.

> The rules and policies of navigating the Composite's relationships are
> encapsulated in ComponentList. An individual Component doesn't need to
> know it is even in a Composite pattern or even that there are other
> Components or a Car or a ComponentList.

Though if it doesn't it needs a way of navigating to this information.

>
> >
> > it now satisfies 3 roles Composite,Visitable and Car all of which are
> > unencapsulated from each other even though they are mostly orthogonal
> > - though they do have some shared state variables.
> >
> > To me a Car should not know how to walk it subcomponents (it doesn't
> > in real life) - why should it? why make it a god? why not create a
> > visitor called EnumeratingMechanicVisitor to count them as I would in
> > the real world.
>
> Car doesn't. Separating those concerns is why we have ComponentList.
>

And navigation.

> >
> > It depends on who is navigating, if it is the component then there is
> > no problem (apart from I think it's quickly going up the deity
> > charts).
>
> It should never be a Component because that would require that its
> implementation understand its context.

In general yes.

> The relationships that define a
> Composite pattern require someone external to construct it. (Note
> that's one reason why constructors are static class methods; one can't
> ask the instance to create itself.) Similarly, it must be someone
> external to the components that understands how to navigate the
> /overall/ structure.

But you still need to be able to navigate from Car to its Components?
(and Components to Car)

>
> > From the point of view of a visitor - it is only through the Composite
> > interface - it knows of nothing else.
> > From the point of view of client code it doesn't need to know - all it
> > wants is the subcomponents to be enumberated - how that gets done is
> > the visitors business - all it knows is a car is Visitable.
>
> The visitor here is some arbitrary object who has the responsibility of
> understanding special rules and policies for navigating the overall
> structure. In the example above, ComponentList is the visitor. But all
> it needs to do is navigate the existing relationships. That requires no
> semantic participation by individual Components.

As above - it doesn't BUT if I am navigating the list I need to be
able to get to the CCar and CWheel.
And if I have a CWheel I want ot navigate to it's list of components.

I personally want to externalise the navigation - but I don't think
this is a sticking point - in this context I agree it doesn't matter.

>
> Conversely ComponentList knows nothing about Component semantics (even
> identity may be implicit); it just "walks" (and manages) relationships.
> Any client (e.g., Car) who has a need to interact with a particular
> Component (or group of Components) just asks ComponentList to find it
> and the client then collaborates with it directly.

Ahhh, we agree then there needs to be navigation.

>
> Therefore neither Car nor Component needs to have any special properties
> other then what they intrinsically are. IOW, there is no Visitable
> property here.
>

as above I want Visitable because I want the class to be able to
declare it's nature before being processed in a typesafe manner and
without reflexion and switch case.

>
> That's a problem. B-) In /my/ analysis Car has only one role so far:
> Drive. ComponentList has the collection management role for the
> Composite "collection" (add, remove, find sorts of things). And
> Components are just assemblies or ordinary parts.
>

You have split them - yippeee :-)

And I want to.

But my iceberg is looming (as above) - I still need polymorphic
interfaces to allow the client code to navigate between CCar/CWheel
and CComponentList and I want each CCar/CWheel to implement IVisitable
so that it can declare it's nature in an extensible manner.

In conclusion

i) my state is worryingly being accessed by different people at
different times i.e. walking the list and Crash - I'll go for copying
the component list for the moment.
ii) the solution is quite complex - much more complex that simply
implementing ICar,IVisitable,IComposite and delegating, but state
management seems easier.
iii) I would prefer to stylelise the model.

Either

i) I'm going in the right direction and I just need to have the
confidence of my convictions and carry on.
ii) I'm disappearing into a cul-de-sac.
iii) My fundamental paradigm is flawed in some manner and I should do
some plumbing.

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


Loading