From mboxrd@z Thu Jan 1 00:00:00 1970 X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on polar.synack.me X-Spam-Level: X-Spam-Status: No, score=-0.8 required=5.0 tests=BAYES_00,INVALID_DATE autolearn=no autolearn_force=no version=3.4.4 X-Google-Language: ENGLISH,ASCII-7-bit X-Google-Thread: 103376,93fa00d728cc528e X-Google-Attributes: gid103376,public X-Google-Thread: 1108a1,93fa00d728cc528e X-Google-Attributes: gid1108a1,public X-Google-ArrivalTime: 1994-10-26 22:55:51 PST Newsgroups: comp.lang.ada,comp.object Path: nntp.gmd.de!newsserver.jvnc.net!howland.reston.ans.net!noc.near.net!ray.com!news.ray.com!news.ed.ray.com!swlvx2!jgv From: jgv@swl.msd.ray.com (John Volan) Subject: Re: SOLVED! Decoupled Mutual Recursion Challenger Date: Thu, 27 Oct 1994 02:15:41 GMT Message-ID: <1994Oct27.021541.1696@swlvx2.msd.ray.com> References: <1994Oct12.224944.25566@swlvx2.msd.ray.com> <1994Oct17.205244.17450@swlvx2.msd.ray.com> <1994Oct18.221751.15457@swlvx2.msd.ray.com> <38289r$79m@oahu.cs.ucla.edu> <1994Oct20.121408@di.epfl.ch> <1994Oct26.134825.1@rapnet.sanders.lockheed.com> Sender: news@swlvx2.msd.ray.com (NEWS USER) Organization: Raytheon Company, Tewksbury, MA Xref: nntp.gmd.de comp.lang.ada:16220 comp.object:16657 Date: 1994-10-27T02:15:41+00:00 List-Id: gamache@rapnet.sanders.lockheed.com writes: [Objections to the use of the term "Decoupled" in this thread.] I fully agree, my use of the term "decoupled" was unfortunate. In a binary association, the two classes are indeed coupled, one way or another, and I really wasn't trying suggest that we can eliminate that coupling. On better reflection, I think I should have used a term more like "Separately-Encapsulated Mutual Recursion". The challenge I was proposing was how one could achieve the coupling necessary for a mutually-recursive association while still being able to encapsulate each class in its own separate package. Some folks have insisted that the "cleanest" way to achieve the necessary coupling is to give up on separate encapsulation; both types "must" be encapsulated in the same package. The problem I saw with this was that the effect would be transitive: Every time some other class had to be associated, it would wind up being dumped into the same package. In the extreme case, *all* the classes in a large semantic network would wind up inside of one, huge, monolithic package. This, I think, would defeat the whole purpose of packages as a way of modularizing software designs. "Gamache" (I'm sorry, you didn't sign your post, so I don't know your full name) points out that there are various degrees of coupling, and that, in particular, "content" coupling is the worst: >In software engineering, it has become widely accepted that low coupling (such >as data coupling) is good, whereas high coupling (such as content coupling) is >bad. If "decoupled mutual recursion" is moving on this scale, from where to >where? I couldn't make this out from the proposed solution. In fact, this >reasoning solidifies my initial opposition to it. To see why, let me review the >definition of one of the types of coupling listed above. This definition is >from Page-Jones, but similar definitions are available from a variety of >authors, sometimes the name of the specific type of coupling is different, but >the end result is almost the same. > Two modules exhibit content (or pathological) coupling if > one refers to the inside of the other in any way; for > instance,... if one module refers to (or changes) data > within another.... Such coupling makes nonsense of the > concept of black-box modules.... >Now I'm not saying that the above was the *intent* of the proposed solution; Ironically enough, I think my original intent was to find a way to *avoid* "content coupling." If we're forced to declare two types within the same package simply because they're associated to each other, then the operations for either of the types would be able to directly access the internal structure of the other type. With respect to each other, the two types would cease to be "black boxes". No, I would rather see the two types encapsulated as private types in separate packages, with no *direct* access to each other's structures. >however, if I evaluate the type of coupling that the proposed solution >*implements* (on the basis of xyz_Identity.Value being System.Address *and* the >very existance of To_Pointer operation), I am forced to conclude that many >modules *MAY* modify the data of one another ==> content coupling. No, no! You're misinterpreting what my generic solution is doing! Each "Xyz_Identity.Value" type is only going to be allowed *one* Pointer type that it can be translated into. I agree, if "Xyz_Identity.Translation" could be instantiated more than once, then we'd break type safety and general insanity would ensue. But note that each Xyz_Identity package has a guard that causes Program_Error to be raised if its Translation sub-package is instantiated more than once. Properly speaking, only the package that actually implements the "Xyz" class should do an instantiation of "Xyz_Identity.Translation", and only for its "Xyz.Pointer" type. Don't be confused by the use of System.Address as one of the possible *implementations* of Identity.Value. There are many reasons why System.Address is a poor choice, but they have more to do with portability, not type safety. In fact, it's precisely because Identity.Value *is* a "black box" that System.Address could be even a half-way reasonable implementation for it. There are better ways to implement Identity.Value, but regardless of how it's done, it is still going to be a "black box." Remember, only valid Xyz.Pointers can go into an Xyz_Identity.Value, and only valid Xyz.Pointers can come out. I think the best way to understand what my Identity package does is to focus on the term I used when I first introduced this solution: "Deferred Coupling". The Identity package makes it possible for the inevitable coupling between two mutually-recursive classes to be *deferred*, so that we can get on with establishing their interfaces (package specs). This is achieved by allowing an Xyz_Identity.Value type to act as an "opaque forward declaration" for some Xyz.Pointer type -- if you will, a "surrogate" that can stand in the place of that Xyz.Pointer type, even at a point in time before it's possible to declare the Xyz.Pointer type. Eventually, we will establish the coupling between Xyz_Identity.Value and Xyz.Pointer (by instantiating Xyz_Identity.Translation), but, at least for a while, that coupling can be *deferred.* Also, don't be confused by the fact that two classes like Employee and Office would contain pointers to each other (whether those pointers are hidden in opaque identity values or not). Yes, these pointers mean that the two classes are coupled -- that's inevitable, and it's the whole point to mutual recursion. But it does *not* necessarily mean that they are *content* coupled. If the pointer's designated type (e.g. Xyz.Object) is private, then there is no way that you can use that pointer to *directly* manipulate the structure of the designated object. As you would expect, you have to go through the public subprograms from the package spec where that designated private type was originally declared. >A Solution >---------- ["Gamache" goes on to suggest off-loading responsibility for the association to another class of object that would represent the association itself.] Yes, several people have already suggested this solution. I have absolutely no trouble with this kind of scheme, and I fully accept the fact that it is quite appropriate for some applications. However, that scheme is based on the premise that the associated classes do not need to "know" about the association -- i.e., they do not need to bear any direct responsibility for the association. Well, as long as that premise holds, then it's okay. Having said that, I still insist that there may be some applications, or perhaps some styles of design, where it is not appropriate to make that assumption. Throughout this thread, I have taken it as a premise that the two classes in an association would know about their association and would share responsibility for it. Essentially, I've been asking the participants in this newsgroup to simply accept that as an assumption for the sake of argument, and to see where that assumption would lead. Some people have vehemently refuse to do so, even for the sake of argument. Well, I can't force people to explore ideas that they don't want to explore. Nevertheless, I will be the first to thank those folks for opening up my own mind to other possibilities, even if those alternatives are based on different assumptions. ["Gamache" also points out that Entity/Relationship analysis (such as that of Shlaer/Mellor) may discover data attributes that pertain not to either of the classes in an association, but actually to the association itself. This even more strongly suggests that the association should be a class of objects itself.] I wholeheartedly agree with this, but I don't think it changes the nature of the problem. Indeed, I think any such situation simply brings up the same issues. If, during analysis, a proposed "association" becomes a class of object instead, then it is conceivable that this new object class could end up in mutually-recursive relationships with the two other classes. Let me illustrate with an example: Suppose you were considering this association: +-------+ +-------+ | | Is_Now_Or_Has_Ever_Beem_Married_To | | | MAN |(0..many)--------------------------(0..many)| WOMAN | | | | | +-------+ +-------+ If we were to implement a mutually-recursive design to meet this analysis, all we would have to do would be to endow the Man class with a set of pointers to Woman objects, and vice versa. However, on further consideration, we might revise our analysis to allow the association itself to have attributes, by making it a class of its own: +-------+ +------------+ +-------+ | | | | | | | MAN |------(0..many)| MARRIAGE |(0..many)------| WOMAN | | | +------------+ | | +-------+ | Start:Date | +-------+ | End:Date | | ... etc... | +------------+ Each Marriage object represents the particular association of one Man and one Woman for some given period of time. But now we effectively have two associations between three classes of object. My mutual recursion question can still apply to this, as long as you accept my basic premise. We could distribute the responsibility for the associations Man--has partaken of--Marriage and Woman--has partaken of--Marriage to the classes involved. Each Marriage object could hold a pointer to a Man object and a Woman object, but each Man object and each Woman object also could hold a set of pointers to Marriages. >Summary >------- >(Maybe I should have put this at the beginning?) Anyway, I felt: >o the proposed approach is an attempt to package content-coupling as a > feature rather than a liability. Not true. Indeed, the exact opposite is true, as I have argued. >o the problem statement is not new or rare, nor does it have much to do > with subclasses, rather it is a problem concerning associative objects. I fully agree, as long as you allow the possibility of substituting the phrase "associations between objects" for the phrase "associative objects." I concur that there should not be any need to resort to abstract classes and inheritance just to achieve "separate encapsulation". >o existing techniques are available and in place for dealing with > associative object implementations in a low-coupled fashion. Fully agree -- but this is only one possible style of design. Your experience may have encouraged you that this is the best style for the kind of applications you have tackled, but please do not assume that it's perfect for all applications. As evidence, please consider the fact that "Separately Encapsulated Mutual Recursion" is effectively supported by other languages, including C++ and Eiffel. I think it would be a good idea if Ada9X could support it as well, in some reasonable way. >While its certainly possible I've been misreading this thread, to get >realigned I would need identification of what type of coupling one is >"decoupling" and what type one is ending up with. If anything, I think my challenge (and my solution to it) attempted to promote the best possible form of coupling ("data" coupling?) and to discourage the worst form, by preserving the separate encapsulation of classes. -------------------------------------------------------------------------------- -- 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."); --------------------------------------------------------------------------------