* Re: Decoupled Mutual Recursion Challenger [Was: Mut. Recurs. in Ada9X w/o Breaking Encaps.? (LONG)] [not found] <1994Oct7.130100.26953@swlvx2.msd.ray.com> @ 1994-10-11 13:58 ` Norman H. Cohen 1994-10-12 13:17 ` Decoupled Mutual Recursion Challenger John Volan [not found] ` <1994Oct7.165517@di.epfl.ch> 1 sibling, 1 reply; 3+ messages in thread From: Norman H. Cohen @ 1994-10-11 13:58 UTC (permalink / raw) In article <1994Oct7.130100.26953@swlvx2.msd.ray.com>, jgv@swl.msd.ray.com (John Volan) writes: |> So far on this thread, all the solutions to the "withing" problem |> (decoupled mutual recursion in Ada9x) share one common feature: They |> have all used type derivation and inheritance to support the |> distinction between "opaque" and "transparent" object identities, with |> the translation between them being handled by safe downcasting |> ("narrowing") and safe upcasting ("widening"?). ... |> |> The trouble with solutions that exploit inheritance is that they use |> it solely as a mechanism for decoupling, without (as Adam Beneschan |> pointed out) correlating it to any true generalization/specialization |> relationship present within the problem domain. It can be argued that |> this is an abuse of the inheritance mechanism, an example of the |> reason why inheritance has been branded by some as the "GOTO of the |> Nineties". In fact, such inheritance schemes can even *get in the |> way* of more "legitimate" uses of inheritance that do implement |> generalization/specialization relationships. I think this is a natural and appropriate use of inheritance. To review, the scheme in question is: package Type_1_Parent_Package is type Type_1_Parent is tagged null record; type Type_1_Pointer is access all Type_1_Parent'Class; end Type_1_Parent_Package; package Type_2_Parent_Package is type Type_2_Parent is tagged null record; type Type_2_Pointer is access all Type_2_Parent'Class; end Type_2_Parent_Package; with Type_2_Parent_Package; package Type_1_Parent_Package.Refinement is type Type_1 is new Type_1_Parent with private; -- declaration of various primitive Type_1 subprograms, including -- some involving Type_2_Pointer private type Type_1 is new Type_1_Parent with record Associated_Type_2_Value: Type_2_Pointer; -- other components end record; end Type_1_Parent_Package.Refinement; with Type_1_Parent_Package; package Type_2_Parent_Package.Refinement is type Type_2 is new Type_2_Parent with private; -- declaration of various primitive Type_2 subprograms, including -- some involving Type_1_Pointer private type Type_2 is new Type_2_Parent with record Associated_Type_1_Value: Type_1_Pointer; -- other components end record; end Type_2_Parent_Package.Refinement; The type Type_1_Parent models some real-world entity, perhaps an office or an employee. When a type models a real-world entity, it does not model every property of that entity, only those that are relevant to the intended use of the type. For example, a type modeling an employee might not include a Favorite_Scotch component if that information is not relevant to the application. The only feature of Type_1_Parent is its ability to be pointed to by a Type_1_Pointer value. The type Type_1 is a specialization of Type_1_Parent. The real-world entity it models happens to be the same as that modeled by Type_1_Parent, but it provides a more specialized VIEW of that entity, one that offers not only the ability to be pointed to by a Type_1_Pointer value, but a number of primitive subprograms as well. An instance of Type_1 "is an" instance of Type_1_Parent, because it can be used in the same way as any other instance of Type_1 (and in additional ways as well). There is nothing wrong with a class and a subclass modeling the same real-world entity, but with the subclass providing a more specialized view of that entity, with a more complete programming interface. -- Norman H. Cohen ncohen@watson.ibm.com ^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: Decoupled Mutual Recursion Challenger 1994-10-11 13:58 ` Decoupled Mutual Recursion Challenger [Was: Mut. Recurs. in Ada9X w/o Breaking Encaps.? (LONG)] Norman H. Cohen @ 1994-10-12 13:17 ` John Volan 0 siblings, 0 replies; 3+ messages in thread From: John Volan @ 1994-10-12 13:17 UTC (permalink / raw) ncohen@watson.ibm.com (Norman H. Cohen) writes: >In article <1994Oct7.130100.26953@swlvx2.msd.ray.com>, jgv@swl.msd.ray.com >(John Volan) writes: >|> The trouble with solutions that exploit inheritance is that they use >|> it solely as a mechanism for decoupling, without (as Adam Beneschan >|> pointed out) correlating it to any true generalization/specialization >|> relationship present within the problem domain. It can be argued that >|> this is an abuse of the inheritance mechanism, an example of the >|> reason why inheritance has been branded by some as the "GOTO of the >|> Nineties". In fact, such inheritance schemes can even *get in the >|> way* of more "legitimate" uses of inheritance that do implement >|> generalization/specialization relationships. >I think this is a natural and appropriate use of inheritance. To review, >the scheme in question is: [snip outline of scheme -- but excerpted again below] >The type Type_1_Parent models some real-world entity, perhaps an office >or an employee. When a type models a real-world entity, it does not >model every property of that entity, only those that are relevant to the >intended use of the type. For example, a type modeling an employee might >not include a Favorite_Scotch component if that information is not >relevant to the application. The only feature of Type_1_Parent is its >ability to be pointed to by a Type_1_Pointer value. >The type Type_1 is a specialization of Type_1_Parent. The real-world >entity it models happens to be the same as that modeled by Type_1_Parent, >but it provides a more specialized VIEW of that entity, one that offers >not only the ability to be pointed to by a Type_1_Pointer value, but a >number of primitive subprograms as well. An instance of Type_1 "is an" >instance of Type_1_Parent, because it can be used in the same way as any >other instance of Type_1 (and in additional ways as well). >There is nothing wrong with a class and a subclass modeling the same >real-world entity, but with the subclass providing a more specialized >view of that entity, with a more complete programming interface. Everything you say is reasonable from a certain perspective, and I have no doubt that this scheme can be useful in some applications, as long as the designers share the same perspective. But you still have not addressed the challenge: Show that this use of inheritance (providing two views of the same class of real-world entities) does not interfere with the more "customary" use of inheritance (support for the relationship of generalization/specialization between two different classes of real-world entities). To review the challenge: > package Type_1_Parent_Package is -- e.g. Employee > type Type_1_Parent is tagged null record; > type Type_1_Pointer is access all Type_1_Parent'Class; > end Type_1_Parent_Package; > > package Type_2_Parent_Package is -- e.g. Office > type Type_2_Parent is tagged null record; > type Type_2_Pointer is access all Type_2_Parent'Class; > end Type_2_Parent_Package; package Type_3_Parent_Package is -- e.g. Project type Type_3_Parent is tagged null record; type Type_3_Pointer is access all Type_3_Parent'Class; end Type_3_Parent_Package; package Specialization_Of_1_Parent_Package is -- e.g. Manager type Specialization_Of_1_Parent is tagged null record; type Specialization_Of_1_Pointer is access all Specialization_Of_1_Parent'Class; end Specialization_Of_1_Parent_Package; > with Type_2_Parent_Package; -- add this: with Specialization_Of_1_Parent_Package; > package Type_1_Parent_Package.Refinement is > type Type_1 is new Type_1_Parent with private; > -- declaration of various primitive Type_1 subprograms, including > -- some involving Type_2_Pointer -- as well as some involving Specialization_Of_1_Pointer > private > type Type_1 is new Type_1_Parent with > record > Associated_Type_2_Value: Type_2_Pointer; Associated_Specialization_Of_1 : Specialization_Of_1_Pointer; > -- other components > end record; > end Type_1_Parent_Package.Refinement; > > with Type_1_Parent_Package; > package Type_2_Parent_Package.Refinement is > type Type_2 is new Type_2_Parent with private; > -- declaration of various primitive Type_2 subprograms, including > -- some involving Type_1_Pointer > private > type Type_2 is new Type_2_Parent with > record > Associated_Type_1_Value: Type_1_Pointer; > -- other components > end record; > end Type_2_Parent_Package.Refinement; with Type_1_Parent_Package.Refinement; with Type_3_Parent_Package; -- If this "with" were not already here, then -- adding it should not affect Type_2. package Specialization_Of_1_Parent_Package.Refinement is -- DILEMMA: Should we say: type Specialization_Of_1 is new Specialization_Of_1_Parent with private; -- using inheritance to provide a more complete view -- or should we say: type Specialization_Of_1 is new Type_1 with private; -- using inheritance for a real-world generalization/specialization -- declaration of various primitive Specialization_Of_1 subprograms, -- including some involving Type_1_Pointer -- and some involving Type_3_Pointer private type Specialization_Of_1 is new ???? with record Associated_Type_1_Value : Type_1_Pointer; -- assuming cardinality=1 Associated_Type_3_Value : Type_3_Pointer; -- assuming cardinality=1 -- other components end record; end Specialization_Of_1_Parent_Package.Refinement; -- (Of course, in all of the above, assume that there is a liberal -- sprinkling of "use" clauses :-) >-- >Norman H. Cohen ncohen@watson.ibm.com Let me state it categorically: I think that using the mechanism of inheritance to solve the problem of decoupled mutual recursion is misguided. Luckily, I think there is a better solution, but it involves another feature of Ada entirely ... -- John Volan -------------------------------------------------------------------------------- -- Me : Person := (Name => "John Volan", -- Company => "Raytheon Missile Systems Division", -- E_Mail_Address => "jgv@swl.msd.ray.com", -- Affiliation => "Enthusiastic member of Team Ada!", -- Humorous_Disclaimer => "These opinions are undefined " & -- "by my employer and therefore " & -- "any use of them would be " & -- "totally erroneous."); -------------------------------------------------------------------------------- ^ permalink raw reply [flat|nested] 3+ messages in thread
[parent not found: <1994Oct7.165517@di.epfl.ch>]
[parent not found: <1994Oct7.222716.8690@swlvx2.msd.ray.com>]
[parent not found: <1994Oct11.122356@di.epfl.ch>]
* Re: Decoupled Mutual Recursion Challenger [not found] ` <1994Oct11.122356@di.epfl.ch> @ 1994-10-11 23:34 ` John Volan 0 siblings, 0 replies; 3+ messages in thread From: John Volan @ 1994-10-11 23:34 UTC (permalink / raw) Robb.Nebbe@di.epfl.ch (Robb Nebbe) writes: >|> I should point out that, in any object-oriented application, >|> responsibility for *many* aspects of the system's functionality winds >|> up being distributed across *many* classes of objects. >This is the way it should be. However, you are suggesting that a single >aspect of the system's functionality be distributed accrossed multiple >packages, which isn't the same thing. I am indeed suggesting that a *single* aspect of system functionality can be, and often should be, distributed across multiple classes (and therefore, packages). That's what I thought I said in the quote above, but obviously, my phrasing wasn't very clear. Let me rephrase: It's *often* the case that if you take any *particular* *single* "functionality" of an object-oriented system, you might *not* find that functionality totally encapsulated within any *one* object class. Instead, that functionality may actually be manifested as a multitude of similar methods scattered over many different classes of objects. Each of these methods only deals with that "functionality" directly in terms of its own particular object class, and that is as it should be. However, there may very well be *collaborations* among these methods: Any one of these methods, in any one of these classes, may wind up calling the corresponding methods in one or more of the other classes, and these may wind up propagating related calls to yet other classes, and so on. But the "algorithm" for that overall system functionality never appears completely in one place. The only way to visualize the "algorithm" is to trace the collaborative propagation of method calls across the various classes. That, I think, is the difference between functionally-designed systems and systems designed in an object-oriented fashion. In a functionally-designed system, you may very well see a major system functionality manifested as a single module of code. The algorithm can be found all in one place, but it winds up touching many different types of data. Other functional modules may wind up touching all of the same data types. So if you ask the question "What code affects this one particular data *type*?" you will not be able to find the answer encapsulated in any one place. You wind up having to search through all the functional modules. On the other hand, in an object-oriented system, we make the data types (i.e., the object classes) central, and the functions become secondary. Any one class of object may participate in many different areas of system functionality, to its own extent. But those same functional areas might also be dealt with partly within many other object classes as well. So if you ask the question "What code affects this one particular data *type*?" the answer is simple: the methods in this class. But if you ask the question "What code implements this particular *functionality*?" you wind up having to search through many object classes to find it all. As I said before, this difficulty in "seeing" overall functionality within an object-oriented system is only a symptom of *code*. Object-oriented *analysis* and *design* techniques have the liberty of presenting multiple complementary views to show different aspects of a developing object-oriented system. These views can include functionally-oriented "cuts" at the design, where you can focus on just those object classes, and just those methods of those classes, that pertain to a given functionality. That way, you can get a sense of the overall "algorithm" implemented by these disparate methods, by seeing their pattern of propagation. But just because you provide such a view during analysis or design, does *not* suggest that your final implementation must have a functionally-oriented module that totally encapsulates all of those methods in one place. >Your solution seems to contradict itself. You want to put the >abstractions in separate packages which suggests loose coupling but >you want them to include pointers to each other which suggests tight >coupling. You seem to be sitting on the fence trying to decide which >side you want to be on. Face it: OBJECT CLASSES WILL BE COUPLED TO EACH OTHER TO THE EXTENT THAT THEY SHARE RESPONSIBILITIES FOR OVERALL FUNCTIONALITIES OF THE SYSTEM. This coupling is everywhere. But how *deep* must this coupling be? Can we limit the coupling to having one separately-encapsulated module make calls to other related, yet still separately-encapsulated, modules? Or do we wind up having to encapsulate all classes within a single module, every time we find some functionality that they share responsibility for? >In article <1994Oct7.222716.8690@swlvx2.msd.ray.com>, jgv@swl.msd.ray.com (John Volan) writes: >|> C'mon, Robb, you are totally misunderstanding me. I am not claiming >|> that "my" implementation is the "best" one possible. I'm just claiming >|> that is one *possible* implementation. >... and for which you have seen good solutions but which don't >satisfy your constraints. I'm not claiming that there is no advantages >to implementing the relation with two pointers; I'm claiming that to >do that in two completely separate packages doesn't provide you with >any advantages. >If you want to implement a realtionship with pointers put the two >abstractions in the same package or in the same hierarchy of packages. >If you want to put them in completely separate packages then use an >ADT to handle the relationship. The issue of associations is only a particularly poignant special case of this overall issue of "functional coupling between object classes". You claim that I have only two choices for any association: (1) Have the two classes relinquish all responsibility over that association and give it to a third party, just so that the association can be encapsulated in one place; or (2) Let the two classes take on mutual responsibility for the association, but only if the two classes are encapsulated together as a single module, just so that the association can be encapsulated all in one place. The first choice means that we're giving up object-oriented encapsulation: It is no longer possible to say that all the code that relates *directly* to one particular class of object can be found encapsulated within the methods of that class -- you have to hunt through all the association modules, too. The second choice also gives up on object-oriented encapsulation: Object classes no longer act as the units of modularity/encapsulation/separate compilation at all! Two choices, both giving up on object-oriented encapsulation -- that's it, take it or leave it? I don't buy that. I think there is a third alternative: Distribute the responsibility -- we'll live with that -- but still keep the classes separately encapsulated. I say again: It *can* be done. True, it's not *built in* to Ada9X. But Ada (even Ada 83) has always had certain facilities for *extending* the language beyond its *built in* capabilities. That should be suggestive. If any of you folks out there think they have a solution -- in particular, if anyone thinks that exploiting inheritance is the answer -- then I challenge you to take the acid test: Show that your solution can handle even the situation of one class in a mutually-recursive association with one of its own subclasses. I fully intend to demonstrate that my own solution can satisfy this test ... but I don't want to spoil it for those of you who enjoy a challenge ... :-) -- John Volan -------------------------------------------------------------------------------- -- Me : Person := (Name => "John Volan", -- Company => "Raytheon Missile Systems Division", -- E_Mail_Address => "jgv@swl.msd.ray.com", -- Affiliation => "Enthusiastic member of Team Ada!", -- Humorous_Disclaimer => "These opinions are undefined " & -- "by my employer and therefore " & -- "any use of them would be " & -- "totally erroneous."); -------------------------------------------------------------------------------- ^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~1994-10-12 13:17 UTC | newest] Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- [not found] <1994Oct7.130100.26953@swlvx2.msd.ray.com> 1994-10-11 13:58 ` Decoupled Mutual Recursion Challenger [Was: Mut. Recurs. in Ada9X w/o Breaking Encaps.? (LONG)] Norman H. Cohen 1994-10-12 13:17 ` Decoupled Mutual Recursion Challenger John Volan [not found] ` <1994Oct7.165517@di.epfl.ch> [not found] ` <1994Oct7.222716.8690@swlvx2.msd.ray.com> [not found] ` <1994Oct11.122356@di.epfl.ch> 1994-10-11 23:34 ` John Volan
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox