|
Friends InterfacesSometimes you design a number of classes so that they can work together on some task. In this case it is often the case that the classes need functionality from each other that have little or no interest to the users of the classes. In fact, it might even be problematic if the users use that "inter-class" functionality, because it is used to maintain some "inter-class" invariant which the user might end-up breaking. In C++ you can define such classes as "friends". A C++ friend classes have unlimited access to your objects; they can manipulate the internal structure of the object. This is used to make such "inter-class" functionality work, without giving the user any extra access . The C++ solution is not very preferable, because you do not really want to provide unlimited access. What you would like to provide is some extra but clearly defined access. In fact you would like to support additional interfaces for this inter-class operation. My suggestion is to provide additional functionality by returning an interface (i.e. an object supporting an interface) in a predicate. Let me illustrate the idea with an example: I have in mind a world with three entities: windows, menus and menu items. Represented by the three interfaces window, menu and menuItem. We can make deeply nested menus because menus are themselves menuItems. So the interfaces look like: interface
window interface
menuItem interface
menu supports menuItem I have corresponding classes cWindow, cMenu and cMenuItem. In this example the classes just have default constructors. In this set up can create a window with a menu by the following code: goal Now comes the "friend" stuff. The problem is that when I say Window:show() I actually have to construct a MS Windows menu and a MS Windows window. Therefore, Window:show() will ask the menu to construct the MS Windows menu, this menu will in turn ask each of its menu items to construct a MS Windows menu item, and so forth. This construction of the MS Windows structures are only relevant for the interaction of cWindow, cMenu and cMenuItem, it has no relevance to the user. So MS Windows structure construction is considered "friends" stuff. We will put the friends stuff in to special interfaces: interface
mswMenu interface
mswMenuItem For menus and menu items to work together with window, the classes will have to support these interfaces somehow. But since these interfaces are not of primary concern we will let the support be indirect: we will add a predicate to the menu interface that returns a mswMenu supporting object, and likewise for menuItem and mswMenuItem: interface
menuItem interface
menu supports menuItem In another mail I have described how you can return This as such an object. The implementation of window::show() in cWindow will look like: implement
cWindow That is, window::show() will first obtain the friend interface from the menu, and then use this to construct the "hMenu". The advantages of this approach are: · There is a clear distinction between user interfaces and friend interfaces (a single class can provide several friend interfaces). · The description of the interfaces are not mixed because none supports the other. · It is more unlikely that a user unintended calls a predicate in a friends interface, because the extra indirection will automatically alarm the user. Compared to C++ this friend notion differs in two ways: · In C++ you have full control of who your friends are, we have no such control · In C++ you have no control of what you reveal to your friends, we have full control on that matter. I think that our "control" is much better than C++, because providing full control "locks" your code: every aspect of your code can be used by the friend classes, and having full control over who your friends are prevent others from re-implement some of the classes but keeping others, because the "new" classes are not friends of the old ones. |