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

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


Date: 22 Apr 2004 06:16:53 -0700


> Responding to Nicholls...
>
> >>The key element of the mindset is to avoid thinking procedurally (i.e.,
> >>starting with a preconceived sequences of operations and then design
> >>objects and behaviors around that sequence). As I indicated in the Part
> >>II response, everything should be driven by abstracting the problem
> >>space. Only when that is finished does one worry about connecting the
> >>dots in the overall solution sequence.
> >
> >
> > I think I do, my draft interfaces define what sort of thing I would
> > expect to able to do to an abstraction - order comes last.
> >
> >
> >>However, if one separates the concerns of relationship instantiation
> >>from relationship navigation, one no longer cares about verification
> >>_when navigating_. A model simulator or an OOPL's type system will
> >>still get very upset when the relationship is not correctly instantiated
> >>and one tries to navigate it. However, that verification is of the
> >>instantiation, not the navigation.
> >
> >
> > I think I need to see this in 3GL world.
>
> class X
> {
> public:
> setR1 (Y* y) {myR1 = y;}
> doIt ()
> ...
> private:
> Y* myR1;
> ...
> }
>
> class Y
> {
> public:
> doYThings ()
> ...
> }
>
> class Z
> {
> public:
> doIt ()
> ...
> }
>
> Z::doIt()
> {
> x = new X;
> y = new Y;
> x.setR1 (y); // instantiate
> }
>
> X::doIt()
> {
> ...
> myR1->doYThings () // navigate
> }
>

I understand this......what if the relationship is set up in the
constructor? (comments - it's far more likely that that is where I
would set such a relationship)

> Despite what it looks like at the compromised 3GL level, all X::doIt is
> doing is sending a message to whoever is on the other end of the
> relationship R1 between X and Y. Mentally replace the name of
> Y::doYThings with Y::IDidMyXStuff. Now the X::doIt call reads like an
> announcement but the Y method name makes no sense in that context.

I think I'm with this.

>
> That's life in 3GLs. At the OOA/D level, though, X sends the
> IDidMyXStuff and Y responds with doYThings

responds with doYThings behaviour? or message? surely behaviour?

>. As a result the developer
> is unlikely to depend on doYThings when constructing X::doIt. Since
> X::doIt doesn't care about anything except what it did, it is
> unconcerned about whatever properties Y has or even that Y exists when
> it sends the message. It has faith that the message will go to someone
> who cares.

OK, I think

>
> All the responsibilities for ensuring that the message goes to the right
> place with the right properties are in Z::doIt, who instantiates the
> relationship. In effect it is Z::doIt that is matching up message to
> properties. However, at the 3GL level that is a mechanical
> implementation of what the developer defined in the class diagram (the
> R1 relationship between X and Y) and the UML Interaction Diagram
> (mapping message to from/to classes).

OK, I think

>
> Unfortunately when we code at the 3GL we have to pick a single name for
> both views. The convention is to use the receiver view (doYThings)
> because that makes life more intuitive when dealing with type systems,
> which are based on receiver property access.
>
> >>>>>>When there are multiple selection conditions by code in ComponentList
> >>>>>>looks like:
> >>>>>>
> >>>>>>int wheelCount = 0;
> >>>>>>int tireCount = 0;
> >>>>>>int lugNutCount = 0;
> >>>>>>...
> >>>>>>int* count_table [COMPONENT_TYPE_COUNT] = {
> >>>>>> &this.wheelCount,
> >>>>>> &this.tireCount,
> >>>>>> &this.lugNutCount,
> >>>>>> ...
> >>>>>> }
> >>>>>>static (()*) func_table [COMPONENT_TYPE_COUNT] = {
> >>>>>> Component::getWheels,
> >>>>>> Component::getTires,
> >>>>>> Component::getLugNuts,
> >>>>>> ....
> >>>>>> }
> >>>>>>...
> >>>>>>ComponentList::add (Component* comp)
> >>>>>>{
> >>>>>> (*count_table [comp->type])++; // update right count
> >>>>>>
> >>>>>> // do add stuff
> >>>>>>}
> >>>>>>
> >>>>>>Component* ComponentList::selectComponent (int comp_type)
> >>>>>>{
> >>>>>> return func_table [comp_type](comp_type);
> >>>>>>}
> >>>>>>
> >>>>>>Wheel* ComponentList::getWheels (int comp_type)
> >>>>>>{
> >>>>>> Wheel* wheels = new collectionDeJour(this.wheelCount);
> >>>>>> Component* comp = this.myRoot;
> >>>>>> while (comp != NULL)
> >>>>>> {
> >>>>>> if ((comp.type == comp_type))
> >>>>>> wheels.add (comp);
> >>>>>> // update comp via traversal algorithm
> >>>>>> }
> return wheels;
> >>>>>>}
> >>>>>>
> >>>>>>
> >>>>>>One key point here are that all the enumerations are captured in data so
> >>>>>>that they all Just Work with one executable statement and they are all
> >>>>>>defined in a single table. Perhaps more important, it is all
> >>>>>>encapsulated as knowledge responsibilities in a single class.
> >>>>>>
> >>>>>>A similar point applies to invoking behaviors -- there is one call
> >>>>>>statement that fits all selections. All one needs is to add an entry a
> >>>>>>jump table for a new selection and encapsulate actual selection behavior
> >>>>>>in a private method. [One can optimize away the individual iterations
> >>>>>>in the methods to a single iteration. I was just keeping the type
> >>>>>>management simple.]
> >>>>>>
> >>>>>>A much more important point is that ComponentList only does collection
> >>>>>>sorts of things. It doesn't understand the semantics of Wheel in the
> >>>>>>same way as the Client that collaborates with it. Similarly, the Client
> >>>>>>doesn't know anything about the organization of car parts.
> >>>>>>
> >>>>>
> >>>>>
> >>>>>I understand all the above as well - yippeee..
> >>>>>
> >>>>>You think this is simple though? less complicated than two visitors?
> >>>>>more maintanable ? less likely to go wrong ?
> >>>>
> >>>>Count again. B-). The code I provided accounts for three enumeration
> >>>>Visitors and three selection Visitors. But let's compare complexity in
> >>>>detail:
> >>>>
> >>>>Implementation | Me | You
> >>>>------------------------------------------+-----------+-----------------
> >>>>Number of classes | 1 | 7
> >>>>------------------------------------------+-----------+-----------------
> >>>>Number of methods to execute all | |
> >>>> enumerations | 1 | 3x7 = 21
> >>>>------------------------------------------+-----------+-----------------
> >>>>Number of executable statements to | |
> >>>> execute enumeration | 2 | visit all
> >>>>------------------------------------------+-----------+-----------------
> >>>>Number of methods to execute all | |
> >>>> selections | 4 | 3x7 = 21
> >>>>------------------------------------------+-----------+-----------------
> >>>>Number of executable statements to | |
> >>>> execute selection | same | same
> >>>>------------------------------------------+-----------+-----------------
> >>>>Number of lines to add a new enumeration | 2 | new class + > 6
> >>>>------------------------------------------+-----------+-----------------
> >>>>Number of lines to add a new selection | |
> >>>> (excluding traversal method) | 1 | new class + > 6
> >>>>------------------------------------------+-----------+-----------------
> >>>>
> >>>>So I am curious about what your criteria is for 'simple'. B-)
> >>>>
> >>>>Also I am curious how you find the Visitor solution more maintainable,
> >>>>given the last two table entries above. B-))
> >>>
> >>>
> >>>AHA - hard data eh!
> >>>
> >>>That puts the cat among the pigeons, before I start let me say I would
> >>>expect that the two versions should be similar if they do the same
> >>>thing - I claim that visitor should have slightly less dangerous code
> >>>because the if is encapsulated (though now my head is above the
> >>>parapet).
> >>>
> >>>We need to be careful about how we set up the 'experiment' and also
> >>>note at the beginning that I have a position and so do you, so neither
> >>>of us are going to be objective - but lets have a go.....
> >>>
> >>>My 1st observation is that your code does NOT do what mine would - for
> >>>me given any element I can assess how many Wheels, nuts etc there are.
> >>
> >>Huh? The way Visitor works to, say, enumerate the Wheels in the
> >>structure one would have to visit every element.
> >
> >
> > Every element starting with the element you have, there is no
> > assumption that you need to have the root element i.e. Car.
>
> Every Element must be visited.
>
> The count is only incremented once if one visits an Element of the
> desired type. Therefore to get an accurate aggregate count, every
> Element of that type in the structure must be visited. But without a
> priori knowledge of the structure there is no way for the client to know
> which Elements were of the right type. (Otherwise the client would
> already know the count.) Therefore the client must visit every Element
> in order to accumulate an accurate count.

accuract count in the context of the question, which is dependent on
where the visitor starts - it starts at car - it will get a rundown of
all the components that make up a car, if it starts a screw it will
get a different answer - because it's a different question.

>
> >
> > Your solution (I think at the moment) counts the number of elements
> > that are directly held in the componentlist.
> >
> > i.e if
> > ComponentList A;
> > ComponentList B;
> >
> > A.Add(&B);
>
> Not a valid operation. A ComponentList is not a Component (in Gof
> Composite terms). It is just a collection class for those things.

Exactly, that's what I'm saying, your code is different - because we
don't have a real problem we've constructed different solutions on the
basis of what we think the requirments are - I expect in reality that
I would err towards this solution than yours, but the solutions are
different, so we are comparing apples with donkeys.

>
> >>>My 2nd is that your structure is NOT a composite structure, it is a
> >>>list - I am able to effectively put a ComponentList in a ComponentList
> >>>and it all works out of the box.
> >>
> >>Yes it is. Note the comments, "// do add stuff" and "// update comp via
> >>traversal algorithm". That's where the navigation code lives for the
> >>Composite (which can be navigated fully at the Component superclass
> >>level). That code would understand subassemblies (e.g., where to put a
> >>Wheel).
> >
> >
> > Can you add ComponentLists to ComponentLists? (a question).
>
> No. Assuming a true Composite structure I would further expect most
> implementations would not allow adding anything but Leafs.

So the solutions do different things.

>
> However, I can imagine an interface that would allow adding Composites
> (e.g., Component-by-Component copy into the structure). In that case,
> since the root Component is always a Composite, one could effectively
> add in another ComponentList's Components in a single call.
>

OK, so the solutions are different - mine does more.

> >
> >
> >>>For you to do the same I assume you would have to implement some sort
> >>>of double linkage so that adding a nut to a door would trickle back up
> >>>the chain - multos nastios codeos.
> >>
> >>No. All I need is the Composite relationships to navigate, starting at
> >>the root assembly related 1:1 to ComponentList. [You need exactly the
> >>same thing. As I comment below, your code will not correctly enumerate
> >>unless the AcceptVisitor call is made against the root component in the
> >>hierarchy.]
> >
> >
> > Yes it will, it will tell you how many wheels a car has if it is given
> > a car.
>
> Of course. That's because Car /is/ the root of the tree. But if you
> visit some random DoorHandle node in the tree your are very unlikely to
> get a correct count of the number of Wheels on a Car.

Yes - because if I ask "how many wheels does a door have?" I would
expect a different answer to "how many wheels does a car have?".

they are different questions.

>
> >>>Now to look at your metrics......
> >>>
> >>>I'm not convinced by some of them.
> >>>"Number of classes" - are you claiming that the maintainablity of code
> >>>is dependent on number of classes ? The least mainanable code in my
> >>>experience (!!!) is usually large bloated god classes.
> >>>Again I'm not convinced by - "Number of methods" as a good indicator
> >>>of maintanability - but again based on experience only.
> >>
> >>ComponentList is hardly a god class; it has very limited collection
> >>semantics.
> >
> >
> > OK, it's higher up the deity stakes than my composite, because it
> > knows about counting and retrieval.
>
> True. But almost all collection classes are responsible for the same
> sorts of things. That's what collections do.

yep, but not composites - this concern is seperate.

>
> More to the composition point, in the problem space it is a single
> identifiable <conceptual> entity with those responsibilities. One
> doesn't conceive of separate CollectionCounter and CollectionRetriever
> entities. If CollectionCounter and CollectionRetriever did exist as
> problem space entities one would have to model them as separate classes.
> Then when one wanted a count, one would collaborate with
> CollectionCounter and if one wanted to retrieve collection members one
> would collaborate with CollectionRetriever.

I'm not quire with this.

>
> >
> >
> >>Number of methods is an indicator of overall complexity. Each method
> >>represents access to a logically indivisible behavior responsibility.
> >>Therefore more methods => more identifiable behaviors => more complexity.
> >
> >
> > I have multple seperate function with no parameters - you have a
> > single parametised function that returns values based on a lookup.
> >
> > I'm not sure whether we are comparing cows with potatoes - I can argue
> > that your function is logically divisible - because I have divided it.
>
> They are cows and potatoes. One is a static knowledge solution while
> the other is a dynamic behavior solution.

OK, lets call them cows and potatoes - yours is a cow, and mine is a
collection of potatoes.

Which is more complex ? (retorical)

>
> >>>We need to be careful about lines like;
> >>>(*count_table [comp->type])++; // update right count
> >>>
> >>>does this really constitute a single line of code - hmmmmm - not to me
> >>>- I would say probably 3 things are happening here (many people would
> >>>put it on 3 lines + 1 for a variable declaration).
> >>
> >>It's one line and one executable statement in C++. And its not doing
> >>anything sneaky like an operator overload; it is just a vanilla C++
> >>statement.
> >
> >
> > Again I don't like your metric.
> >
> > i.e. is
> >
> > (*count_table [comp->type])++;
> >
> > less complex.
> >
> > int x = *count_table [comp->type];
> > int x=x+1;
> > *count_table [comp->type] = x;
> >
> > it would appear so - but is it less maintainable - if it is, then is;
>
> As it happens, I would tend to lean towards the second version for
> maintainability. However, I know a lot of hard core C++ types who would
> argue that the single statement version is essentially an instantly
> recognized idiom and, therefore, is actually more readable.

I completely accept that - it's been swirling around in my head - I'm
not sure what to say.

if it were common usage to go

x=y=1;

would it become acceptable to do so - simply on the basis of common
usage.

hmmmmm

The problem with common usage is it's fine for the "hard core", but
the rest of us who boggle at the incomprehensible warnings and errors
a C++ compiler spews forth everytime I go near them are also there,
tinkering in the code.

Anyway we may agree here - which is good.

>
> But we are getting far OT in quibbling about the metrics. I cited
> several commonly used metrics for complexity and they all came up the
> same way. I could also have broken things out in metrics like McCabe or
> Halstead but I was too lazy to do the counting. It all still comes down
> to the fact that the GoF Visitor version has more of whatever one is
> measuring.

That's why I was quibling - because I don't have the line of code
therefore to count it as 1. would be unfair on the potatoes.

If we are to compare then we need to agree metrics (this really was my
point about 'hard' data - but lets not go there).

>
> >>>We need to be careful about your
> >>>
> >>>int* count_table [COMPONENT_TYPE_COUNT] = {
> >>> &this.wheelCount,
> >>> &this.tireCount,
> >>> &this.lugNutCount.....
> >>>
> >>>They are not executable but they are certainly a sign of naked
> >>>explicit complexity in your code (which I think the visitor hides).
> >>
> >>The complexity lies in the fact that to add a new enumeration I only
> >>have to add a single line in a table. To do a true GoF Visitor you have
> >>to add a new Visitor subclass and that subclass must have a method
> >>implementation for every existing Element. As it is, you provide a
> >>method for each operation plus the corresponding Accept implementation.
> >>
> >>I don't agree that is hidden in Visitor at all; it is explicit Visitor
> >>code that must be written. It is also code that any maintainer is going
> >>to have to understand when contemplated the Element.Accept implementation.
> >
> >
> > your table is order specifc, accidentally delete a line from you array
> > and LOTS of code breaks - mine is dependent on the explicit name of a
> > method - I prefer the latter.
>
> As I said previously, delete a line and the compiler is going to issue a
> warning. Developers that don't pay attention to compiler warnings get
> what they deserve.

Delete a line -get a warning that says "there aren't enough entries in
your array", add an entry in the WRONG place.

No warning.

run the code - BBBOOOOOOOMMMMMMM.

>
> >
> > accidentally delete the 1st entry (and insert one at the end when the
> > compiler warns you), ALL your code breaks - that's a lot of accidental
> > coupling between the behaviour of your code and the order of your
> > array.
>
> Come on, you are postulating an idiot developer. Why would the
> developer add the deleted line at the end rather than where it was deleted?

The point about maintanability is to make as few assumptions about the
idiot developer as possible. If he were completely expert and knew
everything there is to know about the system, to me this is a key
value of encapsulation, it simplifies it to the point where mere
mortals can be productive (another reason not to like C++), I like
systems that assume I'm stupid - they are very difficult to break.

The example given is exactly the sort of thing that does happen,
because sometimes we're all the idiot programmer.

>
> However,...
>
> >
> > I have NO array, if I mess up 1 accept function it is decoupled from
> > every other one - that's good.
>
> If one is going to postulate these two cases, I would prefer the one
> where all the code breaks. Then there is less chance of the problem
> escaping to the field. In either case there is still just one fix in
> one place.

Well yes, I do sort of agree to a degree - I prefer the one that
breaks the system completely before it is shipped, but not after it's
shipped.

The coupling magnifies the bug, if it magnifies it to the point where
it is caught then that is good, if it magnifies it but it still isn't
caught, then that is bad.

I don't think that that means that we should deliberately write
coupled code on the grounds that it magnifies bugs (though oddly I
have worked on systems where the software would deliberately crash the
whole system, if an internal problem was found - but there was a
mirrored system that would cut in).

>
> >>>I don't mind "Number of executable statements to execute selection" -
> >>>and unsuprisingly these are the same.
> >>>I don't mind "Number of lines to add a new enumeration" if we only
> >>>count unenforced lines i.e. not declaration enforced by interface
> >>>association but entries in arrays, array declaration (if not
> >>>explicitly enforced in some manner) and executable code.
> >>
> >>The issue here is maintainability. Which is better: adding one table
> >>entry or an entire Visitor subclass? I'll take the table entry any day.
> >> B-))
> >
> >
> > hmmmm,
> >
> > 1 entry to table versus 1 method to visitor.
> >
> > errrrm answer.....
> >
> > 1 method to visitor - there is no less (none I can see) accidental
> > coupling.
>
> You are now comparing my code to your version of Visitor. I provided my
> code as an alternative to the GoF implementation of Visitor, which is
> what I thought we were discussing at the time (we were still talking
> about no-op Visitor methods). And the metrics I provided were against
> the GoF implementation. You provided code for your version in response
> to that message. Your version is actually much closer to mine than it
> is to the GoF Visitor.

I refactored it.

:-)

Originally I didn't like "X->type" and "if" you refactored to a look
up, you didn't like my no-ops and I refactored to redefine the
operator.

I still think it's a GoF visitor.

>
> >
> >
> >>BTW, I don't see the superiority of my enumeration solution as being
> >>even remotely debatable for maintainability, reliability, or
> >>performance. It just makes no sense at all to implement such things
> >>with Visitor. However, that's less about Visitor than it is about a
> >>knowledge implementation vs. a behavior implementation; knowledge is
> >>always better when one can do it reasonably.
> >
> >
> > I believe you have data to back up the statement, I'm not sure about
> > your interpretation of that data, in the same way I don't believe
> > maintainability has a direct relationship number of lines,
> > irrespective of the contents of those lines of code - otherwise we'd
> > all be removing our test code and assertions.
>
> There's always Delphic sampling. Take a poll of the forum.
>
> I'll bet almost everyone will prefer a table lookup to inline code as a
> general guideline, especially if the inline code is in multiple places.
> I'll give odds that almost no one would use a Visitor or a
> Visitor-like approach to counting members of a collection compared to
> making the counts attributes of the collection and updating them as
> members are added or removed.

Democracy!

Hmmm - I wont do it, because I'd lose - but that's because they are
unenlightened savages (a joke!).

You may well be right but the problem is

"(*count_table [comp->type])++;"

just because it's a familiar idiom that the "hard core" would
instinctively understand doesn't make the principle correct.

The poll may well come down in your favour - but this does make you
right (though it would be a reasonable metric for an indication - I
accept).

Hopefully noones watching.

>
> >>>my solution would look something like.... (I'll make all components
> >>>potentially composite).
> >>>
> >>>interface ICarComponent
> >>>{
> >>> void AddSubComponent(ICarComponent);
> >>> IEnumerator GetSubComponents();
> >>> void AcceptVisitor(ICarComponentVisitor)
> >>>}
> >>
> >>What's an IEnumerator? I ask because it seems to me it must reflect the
> >>Composite somehow (i.e., it can't be a vanilla collection of Components;
> >>it must distinguish somehow between CCompositeComponent and CCar, etc.).
> >>[Hmmm. That gets answered below because you are doing a straight
> >>hierarchical list rather than Composite.]
> >
> >
> > IEnumerator is an interface defined by my environment, we can put,
> > ICarComponent[] it makes little difference.
> >
> >
> >>I already have a problem with composition here. B-) AddSubComponent is
> >>an instance method. That means that if a client wants to add a lug nut
> >>to a wheel, it must navigate the structure to find the right Wheel and
> >>then invoke AddSubComponent for that Wheel instance.
> >
> >
> > Yep, that's because our solution do different things.
> >
> > I am trying to model the real world more closely than you (an
> > observation).
> >
> > If I want to add a nut to a wheel in the real world, 1st I go to car,
> > then 1 traverse until I find wheel and then I add nut.
>
> So does my ComponentList. The difference is that you do not encapsulate
> the traversal; you force the client adding the LugNut to navigate the
> structure. In my version that is not so. The client just hands
> ComponentList a LugNut and ComponentList figures out where to put it.
> Worse, you force /every/ client who might want to add a part to
> understand the structure. Worse yet, if the structure changes you have
> to modify every one of those clients.
>

I don't understand. If I add a LugNut to your componentlist there is
no sense that I am adding it to a wheel, I'm just adding it to the
list.

The client needs to understand the structure - yes - because the
composite stores the structure - the componentlist doesn't.

> >
> > It's more complex. but that because it does more (an observation).
>
> Quite the contrary. Your solution does less because you palm off
> functionality (traversal) onto the client.
>
> >
> > I can say that a nut is part of the wheel, you can't (an
> > observation)..
>
> Yes I do. Those are problem space rules for the construction of the
> structure. They are encapsulated in ComponentList.add.

I don't understand you need to give an example of a client adding a
lugnut to a wheel that is part of a car and then enumerate the number
of lugnuts on the car, and the number of lugnuts on the wheel.

>
> >
> >
> >>This breaks both encapsulation and cohesion because the client must
> >>understand the traversing algorithm plus other rules and policies for
> >>adding elements to the structure (e.g., a Wheel must have exactly N
> >>LugNuts). To see why that is bad, imagine an application where ClientA
> >>adds Wheel assemblies (Wheels, LugNuts), ClientB adds drive train
> >>assemblies (Engine, Transmission, etc.), and ClientC adds body
> >>assemblies (Fender, Door, DoorHandle).
> >>
> >>The problem is that ClientA, ClientB, and ClientC must all understand
> >>the traversal algorithm. They must all execute it as well. So if one
> >>changes the traversal algorithm, say from your simple hierarchy to a
> >>true Composite, then each of those Clients will have to be modified.
> >
> >
> > No, I would hopefully encapsulate the traversal algorithm (of the tree
> > structure which is different from simply traversing the children) -
> > it's an orthogonal concern to adding nuts i.e. I need to find the
> > correct wheel - I don't care how.
>
> Huh?!? You just agreed immediately above that the client has to do the
> navigation (i.e., AddSubComoponent is an instance method and the client
> has to find the right instance).

Something needs to do the navigation - my problem here is client - yes
I think so.

>
> >
> > There may be cases in which different clients want to traverse in
> > different ways!
>
> That's fine. One just needs a Strategy pattern for ComponentList.
> Whoever understands the context instantiates the relationship to the
> correct strategy.
>
> >
> >
> >>It is to avoid exactly this sort of problem that I encapsulated the
> >>traversal algorithm in a separate class, ComponentList. Now any client
> >>that wants to add, remove, or find a particular CarComponent asks
> >>ComponentList regardless of what its context is. More important, that
> >>client is completely indifferent to the actual structure for organizing
> >>CarComponents and traversal redundancy has been eliminated.
> >>
> >
> >
> > If you used simple recursive tree traversal (i.e. depth 1st) in your
> > componentlist, then you have embedded it in the componentlist, this
> > may or may not be sensible.
>
> ComponentList is designed to the problem in hand. If that problem's
> requirements change in the future, the changes are isolated to
> ComponentList.

Our requirements are different - I accept that I have taken a broader
view and gold plated - so in this context I will probably have to
admit that my code is more complex because it does more, because I
have gold plated the requirments up front.

>
> >
> > I actually can't see it in your code, because I don't believe your
> > structure is recursive.
>
> Given the GoF Composite relationship structure and the fact that I have
> structured the model so that ComponentList has a 1:1 relationship with
> the root Composite of the structure, how else would one do it???

I don't understand the answer - if you can put a componentlist in a
(even indirectly) componentlist then, to me, it is a recursive data
structure, if you then add a lug nut to one of the grandchildren then
that lig nut is part of the root structure - again I can see this
depends on the context of the problem domain - that was my
interpretation.

>
> >>>interface ICarComponentVisitor
> >>>{
> >>> void VisitCar(ICarComponent);
> >>> void VisitWheel(ICarComponent);
> >>> void VisitTyre(ICarComponent);
> >>> void VisitLugNuts(ICarComponent);
> >>>}
> >>>
> >>>private CCompositeComponent : ICarComponent
> >>>{
> >>> ArrayList marrChildren = new ArrayList();
> >>>
> >>> void AddSubComponent(ICarComponent)
> >>> {
> >>> marrChildren,Add(carcomponent);
> >>> }
> >>
> >>You create a new marrChildren when each component is added?!?
> >>
> >>
> >>> IEnumerator GetSubComponents()
> >>> {
> >>> return marrChildren.GetEnumerator();
> >>> }
> >>>
> >>> void AcceptVisitor(ICarComponentVisitor)
> >>> {
> >>> throw exception.
> >>> }
> >>>}
> >>>
> >>>class CCar : CCompositeComponent
> >>>{
> >>> void AcceptVisitor(ICarComponentVisitor)
> >>> {
> >>> CarComponentVisitor.VisitCar(this);
> >>> }
> >>>}
> >>>
> >>>class CWheel : CCompositeComponentt
> >>>{
> >>> void AcceptVisitor(ICarComponentVisitor)
> >>> {
> >>> CarComponentVisitor.VisitWheel(this);
> >>> }
> >>>}
> >>>
> >>>class CTyre : CCompositeComponentt
> >>>{
> >>> void AcceptVisitor(ICarComponentVisitor)
> >>> {
> >>> CarComponentVisitor.VisitTyre(this);
> >>> }
> >>>}
> >>>
> >>>class CLugNuts : CCompositeComponentt
> >>>{
> >>> void AcceptVisitor(ICarComponentVisitor)
> >>> {
> >>> CarComponentVisitor.VisitLugNuts(this);
> >>> }
> >>>}
> >>
> >>This is not a Composite pattern. In Composite one would have
> >>
> >> [CComponent] -------------------------------+
> >> A |
> >> | |
> >> +-------+-------+----+----+-------+ |
> >> | | | | | |
> >> [CCar] [CWheel] [Ctyre] [CLugNut] [CCompositeComponent] ----+
> >>
> >
> >
> > No. they both are !
> >
> > I have defined all components to be composite role.
> >
> > p165 Collaborations GoF
>
> You've named them that way but in the code you haven't defined them that
> way. For example, CCar is derived from CCompositeComponent. That is
> not possible in the GoF structure; they are siblings. CCar can only be
> derived from CComponent.
>
> >>That is, a Composite is an assembly of some sort that is a peer with the
> >>non-assembly components. So CCar, CWheel, etc. can't inherit from
> >>CCompositeComponent. More important here is that /only/ a
> >>CCompositeComponent can have descendants.
> >>
> >>What you have actually implemented is a simple hierarchical list:
> >>
> >> +------------------------------+
> >> | |
> >> | parent of |
> >> | 0..* |
> >> [CCompositeComponent] ------------------+
> >> A 0..1 child of
> >> |
> >> +--------+-----+-----+----...
> >> | | |
> >> [CCar] [CWheel] [CTyre]
> >>
> >
> >
> > Yep and that is a type of GoF composite.
>
> Sorry but it's not. It is a simple connectivity hierarchy that has no
> notion of an assembly as a unique entity. Nor is there any explicit
> limitation on the associations that Leafs can have (i.e., none). And
> the Composite associations are not conditional. The structures are
> completely different.

I put the quotes from GoF but you've removed them.

I can subcalss Composite if I want. p167.

I can define child management at the root so I can "treat all
components uniformly" p167

I have to deal with meaningless operations like adding children to
things that shouldn't allow it - so I throw an exception - (that's the
price I pay p167).

but it's still a strict GoF composite pattern - it's NOT the one in
the role model but it is a valid one according the implementation
section.

If you look at the consequences (166) they are all the same -
including your critisism.

I have done 5. p 169

"Should component implement a list of components?"

It is valid but has a cost penalty - space.

>
> >
> >
> >>>
> >>>class CEnumeratingCarVisitor : ICarComponentVisitor
> >>>{
> >>> public long mlonCars = 0;
> >>> public long mlonWheels = 0;
> >>> public long mlonTyres = 0;
> >>> public long mlonLugNuts = 0;
> >>>
> >>> private void VisitChildren(ICarComponent)
> >>> {
> >>> foreach(Component in CarComponent.GetSubComponents)
> >>> {
> >>> Component.AcceptVisitor(this);
> >>> }
> >>> }
> >>
> >>Where does CarComponent come from? B-)) (Big grin because I think the
> >>answer to that is going to cause you some problems.)
> >
> >
> > Sorry, my lazyness meant that I only declare the parameters type, the
> > variable names are missing.
>
> OK, I should have thought of that. It was late and I was tired. No
> problems.
>
> >
> >
> >>Note that your foreach is doing an implicit test on the conditionality
> >>of the relationship (i.e., the loop is a no-op if the CarComponent in
> >>hand has no children). This is equivalent to calling GetComposite() and
> >>testing the result in the GoF example.
> >
> >
> > Except there is no test - look less code = less bugs
> >
> > :-)
>
> The condition may be hidden by the compiler syntax but there is a
> machine instruction for it at the beginning of every iteration. The
> foreach tests the conditional relationship in my hierarchical list model
> above. So...

Yes - but conceptually I am treating all components identically - so
there is no if. The "if" has merged with the "while" - because it's
simpler.

>
> >
> > Why treat a element with no children different from one with ? There
> > seems no reason to make it a special case.
>
> You are treating it differently. The foreach effectively provides an
> else... clause for situation where the relationship is not instantiated.
> It just happens to be a no-op clause.

What you are saying if

If Children.eof = false
{
        while Children.eof = false
        {
        .....
        }
}
else
{
}

but I haven't got the if because there is no special case, if I am
processing children in a loop I see no increase incomplexity if the
relationship is always >0 or >= 0 or >10 or >= 100,000

The cost is the unused ArrayLists in 'leaf' elements, the benefit is
KISS.

> >
> >>> void VisitCar(ICarComponent)
> >>> {
> >>> mlonCars++;
> >>> VisitChildren(CarComponent);
> >>> }
> >>>
> >>> void VisitWheel(ICarComponent)
> >>> {
> >>> mlonWheels++;
> >>> VisitChildren(CarComponent);
> >>> }
> >>>
> >>> void VisitTyre(ICarComponent)
> >>> {
> >>> mlonTyres++;
> >>> VisitChildren(CarComponent);
> >>> }
> >>>
> >>> void VisitLugNuts(ICarComponent)
> >>> {
> >>> mlonLugNuts++;
> >>> VisitChildren(CarComponent);
> >>> }
> >>>}
> >>
> >>I assume a typo here and the argument to VisitChildren should be
> >>ICarComponent. If not, where does CarComponent come from?
> >
> >
> > sorry, Lazyness
> >
> > ICarComponent is an interface and the variable is implicitly
> > CarComponent.
> >
> > i.e. it should read
> >
> > void VisitLugNuts(ICarComponent CarComponent)
> > {
> > mlonLugNuts++;
> > VisitChildren(CarComponent);
> > }
> >
> >
> >
> >>Note that you said above that you could enumerate from any Element, but
> >>that is not possible from this code. The VisitChildren is a simple
> >>depth-first traversal that must start from the topmost Component in the
> >>tree. If it starts from an arbitrary point in the tree the enumeration
> >>will be incorrect if that point is below or sibling to one of the
> >>components one is counting. Unlike AddSubComponent, there is no way (as
> >>written) to fix this with a hidden traversal back up to the top of the
> >>hierarchy.
> >
> >
> > Our code do different things - mine is more complex and more powerful
> > - I agree.
>
> More complex, Yes. More powerful, I don't think so.
>
> We do exactly the same things from the client's viewpoint except that I
> provide more functionality and flexibility because the client doesn't
> have to do the traversal and I allow the structure to be modified
> transparently to the client. I also provide all the features of both a
> full Composite pattern and a full Visitor pattern.

hmmm, OK, I don't think you do, you do more in the sense that you
specify the traversal (I could have done this by embedding it in
CComposite - in this context I should have done on the grounds of
KISS).

I may have got the wrong end of the stick though.

>
> >
> >
> >
> >>Also note that this is not a Visitor pattern. Each subclass of Visitor
> >>encapsulates a different operation but you are combining operations in
> >>CEnumerationVisitor in the same way I combined them in ComponentList.
> >>What you have actually done is much closer to my ComponentList. In so
> >>doing you have eliminated the need for a bunch of no-op methods, just as
> >>I did.
> >
> >
> > Semantics.
> >
> > I have defined my operator to be "count the number of elements of each
> > type" rather than "count the number of elements of type wheel"
>
> This harkens back to the point that I originally understood enumerations
> would be added as needed via Visitor. You can add counts but it isn't
> through the Visitor mechanism; you just modify an existing class
> implementation (just as I do).
>
> >>However, there are some problems here. CEnumeratingCarVisitor is not
> >>really a problem space entity. It is just a class facade wrapped around
> >>a set of functions. IOW, you are organizing a function library. The
> >>acid test is to ask (without prompting) a problem domain expert what he
> >>thinks an Enumerating Car Visitor is and what a Component List is.
> >>You'll get a reasonably accurate description for the Component List but
> >>good luck on Enumerating Car Visitor.
> >
> >
> > I don't buy this - my conceptual model is not bounded by "Domain
> > Experts".
>
> If you are doing OO development, it should be because...

Well, it depends what you mean by Domain Experts.

"Domain Experts" usually have a strict focus on their own context and
may not see the wider picture.

e.g. If I am creating an accouts system for company XYZ I may have
access to their CFO as a 'domain expert' - he will tell me how XYZ
works BUT I have a different set of requirements, processes change,
companies are taken over, I sell systems to different people so I need
to create a model that contains the domain experts model BUT it may
well need to be more general because I may have my own set of
requirements concerned with not having to change software and update
systems (very expensive) and wanting to sell the same system to the
bloke down the road.

Thus his model need to inject into mine BUT not vice verca.

>
> >
> > I need them to help me shape my conceptual model, their model needs to
> > map INTO my model, but my model does not need to map INTO their.
>
> The reason OO development is tied so closely to the problem space is
> that customers don't like change any more than developers do. So when a
> customer faces change it is resolved in a manner that will cause minimal
> disruption to the existing environment.

In my 'experience' it usually involves as little 'pain' to them as
possible - but this often involves significant change to software if
it is not modelled around the underlying model but about the specific
local implementation.

>
> If software reflects the customer's environment faithfully, then it is
> much more likely that when change filters down to the software it can be
> accommodated relatively easily. That's because the customer has already
> done a lot of the work in figuring out the path of least resistance.

hmmm, I'm not so sure - local politics often overrides external pain -
my pain.

>
> Traditionally software development has been very much a product-out
> industry. Developers have a computational model and they expect the
> world to conform to it. IOW, the customer's model must map INTO the
> computational model. OO breaks with that tradition by explicitly
> adopting to the customer's world view through OOA and using that
> solution as the framework for computation. Thus the computational model
> must map INTO the customer model.

Danger!!, embed your clients processes in your system at your peril,
processes change every week, the underlying abstractions of the system
don't.

Local Domain Experts often don't see the more abstract underlying
model - they think it terms of cars and engines, not IVehicle and
IPropulsion.

Should I create a word processor for a client based on his own
particular requirements or should I try to see the bigger picture, it
obviously depends on the context but the requiremnet will be bigger
than those define by the "Domain Expert" because there are other
stakeholders - including me - that have their own set of valid
requirements.

>
> >>Another problem lies in collaboration. CEnumeratingCarVisitor now owns
> >>the responsibility for knowing the counts. So anyone who needs one of
> >>those counts is going to ask CEnumeratingCarVisitor for it. But how do
> >>those counts gets initialized? You would have someone invoke
> >>CCompositeComponent.AcceptVisitor. IOW, they would be collaborating
> >>with CCompositeComponent rather than CEnumeratingCarVisitor.
> >>
> >
> > Everyone who want to know how may components there are of a each type
> > inside a given component, would indeed have to set off the visitor to
> > do it.
> >
> > My code does something different to yours.
>
> Right. The original problem was to enumerate the parts of each type in
> the entire aggregate (i.e., a Car), which is what I did.
>

OK, I've admited to gold plating.

>
>
> >
> >
> >>To me that doesn't make sense. If CEnumeratingCarVisitor owns the
> >>responsibility for knowing the values, it should also own the
> >>responsibility for maintaining them. So if someone wants those values
> >>changed, they should talk to CEnumeratingCarVisitor. IOW,
> >>CEnumeratingCarVisitor should have a UpdateEnumerationCounts() method.
> >>
> >
> >
> > ???
> > The counts are implied by the structure of any given component. I can
> > as you have done, store and maintain them explicitly OR I can provide
> > behaviour that will derive them.
>
> Again, I solved the original problem of aggregate counts. You solved a
> different problem.

OK, gold plated, I do more (or at least something different) we both
can give the number of Tyres.......

>
> >
> > BUT for your code to do what my code does, it would have to be much
> > more complicated.
>
> Au contraire. It is trivial in my solution to provide the number of
> parts in any given subassembly -- that's an obvious knowledge
> responsibility for an assembly and the client would collaborate directly
> with that assembly to get it. ComponentList would just have to make
> sure the assembly was informed when a part was added.
>

You'll have to give me an example (if possible) - I can't see it.

> >
> >
> >>However, that method could simply navigate the structure and count what
> >>was there -- exactly like my ComponentList. There would be no need for
> >>callbacks in CCompositeComponent. That would provide decoupling because
> >>CCompositeComponent would not even know it was being counted much less
> >>that CEnumeratingCarVisitor was doing the counting.
> >>
> >>Another problem with your scheme of collaboration is incremental updates
> >>of components in the structure. AddSubComponent is an orthogonal
> >>responsibility in another object. If a Wheel is added, how do the
> >>counts in CEnumeratingCarVisitor get updated so that the mlonWheels
> >>value is accurate when it is accessed by someone else? In your code
> >>AddSubComponent would need to invoke CEnumeratingCarVisitor methods like
> >>IncrementWheelCount(), IncrementTyreCount(), etc. _for the correct type
> >>of component_.
> >>
> >
> >
> > They don't, every time I want to know how many component there are I
> > have to derive that information with a visitor.
> >
> > It's different - not wrong, just different.
>
> Wow! You update all the counts even none may have changed. And the
> efficiency gets worse if you had solved the original problem of
> aggregate counts for CCar.

Yep (that wasn't defined in our requirements).

>
> >>Second, you are counting lines that would have to be there anyway. They
> >>are necessary to construct the aggregate's relationships. More to the
> >>point, they are intrinsic to Composite and don't really have anything to
> >>do with the Visitor issue. That's why I didn't bother with them and
> >>just used the "// do add stuff" placeholder. (I also noted that the
> >>code for my two placeholders will be found in both versions.)
> >>
> >>There is no way to add a LugNut to the structure at the proper place
> >>(i.e., as a child of a Tyre without enough LugNuts) without
> >>understanding the types of components and the tree structure. By making
> >>AddSubComponent an instance method you have simply shifted the code for
> >>doing that sort of checking into the client. To find the right place to
> >>invoke AddSubComponent the client is going to have to invoke isWheel()
> >>and getChildCount() methods from CCarComponent.
> >
> >
> > It could use a visitor to find the wheels !!!!!
>
> More of the same sort of indirected complexity.

indirected? I'm not with you.

>
> >
> > It needs to know which visitor and which car.
> >
> > I don't understand how your code encodes the knowledge of a nut being
> > on a wheel - I don't think it does.
>
> There are any number of ways, depending upon what the actual rules are.
> However, odds are that would all be encapsulated in a Factory that
> created the instances in the first place. As the instances are created,
> the relationships would be instantiated. Once the relationships have
> been instantiated all the rules are enforced, regardless of the reasons
> for the navigation (enumerations, selections, or whatever) or how many
> navigations there are. From then on all one needs is a type attribute
> in Component for ComponentList to use in its navigations.

I don't see it again, how do you navigate from a specific wheel to a
specific nut - lets say the the components have id's so we can
identify a specific wheel.

>
> >
> >
> >>[Actually, I shouldn't say "no way". You could use Visitor for
> >>AddSubComponent. However, after including all the gloppy stuff for that
> >>on type of the enumeration stuff things would be pretty near
> >>indecipherable. Frankly, I already found it obtuse because I had to
> >>keep flitting back and forth between classes and interface definitions
> >>to understand what was going on. Note that my solution had one
> >>interface and one class implementation that were relevant while your
> >>solution one jumps around among two interfaces and six classes to figure
> >>out what's going on.]
> >
> >
> > Seperating the concerns does this - I accept.
> >
> > Is it more extensible ? yes
> > Is it more powerful ? yes
> > Is it conceptually simpler ? (I actually would argue I only have 2
> > conceptual roles + client) - possibly
> > Is it more code ? yes
> > Is it more executably code (+lookups) ? at the beginning yes -
> > incrementally, similar.
> > Is it more maintianable ? I think possibly yes (on the grounds of
> > incidental coupling).
> > Is it harder to understand ? yes
> > Does it do the same as is comparable is a simple manner? no.
>
> If you agree that separating the concerns in my solution provides all
> these benefits, why are we still arguing over this? B-)) (The only
> things I still disagree with here are the last two items.)

I think I was saying.....

my version......

is it more extensible - yes.....

etc
>
> >
> >
> >>>The problem is that I still think my code is slightly 'safer' simply
> >>>because my coupling is explicit and not based on things like order - I
> >>>really, really don't like the manual maintenance to the arrays and the
> >>>fact they are order sensitive - it really is the sort of thing that
> >>>hides bugs, because the order of the 'types' has to be the same as the
> >>>order of the array elements - for me this comes for free - I don't
> >>>maintain them, the compiler does - it is not part of intrinsic
> >>>complexity and thus disappears in a puff of OO magic smoke.
> >>
> >>Data Structures are good in the same sense that unconditional
> >>relationships are good. They enforce problem space rules in the static
> >>structure rather than in dynamic code. The tables I used are
> >>technically ordered but that really doesn't matter. The index
> >>represents identity and that is defined orthogonally.
> >>
> >
> >
> > The index in the declaration indicates the size of the array (I don't
> > need this).
>
> So? That ensures consistency (otherwise one gets the compiler warning).

Yes but who maintains it? - you do.

>
> >
> > More importantly the order of the array is coupled to the enumeration
> > of type.
> >
> > You can get around this and do what the compiler does for me but you
> > need to add
> >
> > func_table[COMPONENT_TYPE_WHEEL] = Component::getWheels;
> > func_table[COMPONENT_TYPE_TYRE] = Component::getTyres;
> > func_table[COMPONENT_TYPE_LUGNUTS = Component::getLugnuts;
> > ...
> > etc
> > ...
> >
> > but suddenly your number of lines of code is beggining to grow
>
> No, it's exactly the same line count because now there is no table
> initializer.

OK, we are swapping the array for executable lines of code.

I was unsure of whether the array declaration ws included in your
example or not.

>
> >>Since the '70s 3GLs have provided lots of ways for defining identity in
> >>one place (e.g., C's #define) so that it can be used consistently and
> >>with developer-friendly mnemonics. Look-up tables are actually a very
> >>good way to enforce consistency once identity has been properly defined.
> >>
> >
> >
> > but exhibut incidental coupling unless initialised as I have
> > demonstated.
>
> There are more exotic ways to enforce consistency and even ways to allow
> initialization from external data so that the code doesn't need to be
> touched at all. It was just an example; let's not get bogged down in
> language details and other mechanical issues.

But we are arguing the mechanics - you have to implement mechanics I
don't that's my point.

your doing (a version of) Rumbaugh C - I'm doind OO.

>
> >
> >
> >>However, the real advantage of look-up tables lies in the fact that they
> >>are static structures rather than dynamic structures. As a general rule
> >>static structures are more stable over time and are much easier to modify.
> >>
> >
> >
> > The stability is a function of the inherent complexity, if the
> > inherent complexity changes then both my Accept and you lookuo
> > changes, if it doesn't neither do.
> >
> > To me your statement should read, complexity that can be described in
> > static tables is more stable over time, the expression of that
> > complexity is irrelevant to the stabilty.
> >
> > The question of maintainability is open to question - I would claim
> > you encapsulation of this complexity is classic SA/SP and not OO and
> > your hard data would imply that it is thus probably not as
> > maintainable as an OO solution - whether visitor is a sensible OO
> > solution is another matter.
>
> The tables are an object implementation. The OOness lies in the
> encapsulation of that implementation, separation of concerns, and
> decoupling -- all of which are superior to the Visitor approach.

Then Rumbaugh C is OO then - which is fine - but I thought you didn't
think it was :-))

>
> >
> >
> >>Another advantage of look-up tables is that they can be constructed from
> >>external configuration data. Then one can change the application
> >>behavior without touching the code itself. That wasn't the case in my
> >>example because I didn't supply the Factory to do so. But I could have
> >>(even to initialize func_table, though it would be pretty ugly at the
> >>OOP level).
> >
> >
> > It would.
>
> Only because OOPLs don't directly support a decent non-lexical mechanism
> for identifying behaviors. (The knowledge attributes are trivial to
> do.) One has to resort to things like relative entry point numbers.
>

I don't really understand this - but if this is part of the OOPL then
it is part of context of the solution.

> >
> > It's ugly because it makes something in you (your OOP experience
> > possibly) whisper
> >
> > "ooooo, that looks like it's going to go wrong"
> > "ooooo, that looks like it's going to go wrong"
> > "ooooo, that looks like it's going to go wrong"
> > "ooooo, that looks like it's going to go wrong"
> > "ooooo, that looks like it's going to go wrong"
> >
> >
> >>We have been around on this before, but the compiler is going to provide
> >>an error or warning somewhere in my solution for every situation where
> >>it would for your solution. I can't invoke methods that don't exist any
> >>more than you can.
> >
> >
> > you can put functions that satisfy the prototype but are not sensible.
> >
> > I am constained by the interface.
> >
> > 1 error putting an entry in creates multiple errors in the execution.
> >
> > 1 error calling the wrong visit is 1 error.
> >
> > If you create a new 'type' but don't update your COMPONENT_TYPE_COUNT
> > you'll get a run time error, I'll get a compile error.
>
> int const TYPE_A = 1;
> int const TYPE_B = TYPE_A + 1;
> ...
> int const TYPE_Z = TYPE_Y + 1;
> int const COMPONENT_TYPE_COUNT = TYPE_Z;
>
> Whether adding, inserting, or replacing one always updates the entry
> following the change. This sort of thing is pretty much an idiom when
> assigning identity to indexes.
>
> [I've worked with languages where one can make a macro that will do this
> correctly at compile time; all one has to do is add the new name to a
> comma-delimited argument list.]
>

OK, but do you not accept that these are more lines of code, and they
exhibit incidental coupling.

[I accept most 'modern' OOPL would have some sort of enum construct
that would be maintained by the compiler]

> >
> >
> >>>>>I don't.
> >>>>>
> >>>>>I think your recreating the wheel (!!) here, your func_table looks
> >>>>>very much like an exposed v-table - (do I now know what you mean by
> >>>>>'v' ?)
> >>>>
> >>>>To the extent func_table and a vtable are both jump tables, yes. But in
> >>>>the end it is just a look-up table. As a general rule look-up tables
> >>>>are much preferred over conditional in-line logic.
> >>>
> >>>
> >>>I agree, but, encapsulated auto managed v-tables should be prefered to
> >>>exposed manually managed - In fact if I didn't know better (which I
> >>>don't) I would say this is the Rumbaugh C style of writing the
> >>>solution!!! :-)
> >>
> >>But the look-up tables are encapsulated and hidden; they are a private
> >>implementation of ComponentList. In fact, that's pretty much the point.
> >> They are an efficient, easily maintained implementation encapsulated
> >>in one place.
> >>
> >
> >
> > They are encapsulated from the client code, but maintainability has a
> > lot to do with whats abstracted away from the programmer - and they're
> > lounging around in all their dreadful naked glory - saying "go on just
> > edit me, one slip and the whole app comes crashing down on your head,
> > go on, you know you want to".
>
> All I can suggest at this point is to take a poll of the forum and see
> how many supporters you can find for that position. There is no way
> editing a single table entry compares to multiple edits in multiple
> places for maintainability.
>

Hopefully noone is watching - I expect in a democratic sense you may
win.

P.S. I don't accept the comparison - I believe the number of free form
edits would be very similar - I accept I would have more
(re)declarations of methods declared in an interface but they are
exactly comparable to your table entries (unsubprisingly) but they
wouldn't exhibit the same accidental coupling to each other and their
'type' identifiers.

>
> *************
> 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: Message-based vs. method-based interaction [was: Re: LSP and subtype]
    ... interface is always the method signature. ... We can name it for the owner context of what it does or we can name it for the client context, but we can't do both. ... overlaying some sort of developer structure on the 3GL syntax. ...
    (comp.object)
  • Re: Creating a "toy" OO/AO language...
    ... That is a major difference between the OO paradigm and procedural/functional paradigms where behavior always dominates the construction. ... In an OO context there are two ways to implement a closure in the functional programming sense of a method that operates on a bounded set of variables. ... The only object that should have an interface to the strategy ... In the OO paradigm one tries to minimize that by limiting it to client and service through peer-to-peer collaboration by eliminating a hierarchical call chains. ...
    (comp.object)
  • Re: Tell, Dont Ask
    ... That precondition forces the client to keep track of what order the messages are sent to Range and help ensure the Range object's invariants. ... The whole point was for Range to ensure its own invariant. ... But there is a big difference between a contract that requires the context to provide values in a specific order and a contract that requires the context to provide pairs of values in any order. ... Here my solution was constrained by separate setHigh and SetLow interface methods in the original example. ...
    (comp.object)
  • Re: implementing roles in OOP......
    ... > as you descibe does NOT constitute any sort of scientific fact - I ... > I would actively encourage people to collect metrics but beware not to ... > could imply that you can have a Car that isn't an MotorisedVehicle! ... where behaviors need to be substituted depending on execution context. ...
    (comp.object)
  • Re: learning asm.
    ... context switch is required but that a CALL - sans context switch, ... As for context switches and user -> supervisor transitions, ... Yes, sort of... ...
    (alt.lang.asm)