Navigation  without Java Scripts

Friends Interfaces

Sometimes 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
  
predicates
        show : ()
  
         setMenu : (menu Menu)
end interface window

interface menuItem
    predicates

  
     setText : (string Text)
            ...

end interface
menuItem

interface menu supports menuItem
   predicates
        addMenuItem : (menuItem Item)
  
     ...
end interface
menu

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
  
Window = cWindow::new(),
   MenuBar = cMenu::new(),
  
FileMenu = cMenu::new(),
   FileMenu:setText("&File"),
   FileNew = cMenuItem::new(),
   FileNew:setText("&New"),
   FileMenu:addMenuItem(FileNew),
   MenuBar
:addMenuItem(FileMenu),
   Window
:addMenu(MenuBar),
   Window:show().

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
  
predicates
  
     getHMenu : () -> MSWindows::hMenu HMenu
end interface mswMenu

interface mswMenuItem
  
predicates
        getHMenuItem : () -> MSWindows::hMenuItem HMenuItem
end 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
  
predicates
  
     setText : (string Text)
  
     ...
  
predicates
  
     getMswMenuItem : () -> mswMenuItem MSWMenuItem
end interface
menuItem

 interface menu supports menuItem
  
predicates
  
     addMenuItem : (menuItem Item)
  
     ...
  
predicates
  
     getMswMenu : () -> mswMenu MSWMenu
end interface
menu

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
  
facts
  
     menu : (menu Menu) determ
  
clauses
  
     show() :-
  
         ...
  
        menu(Menu),
      
     MSWMenu = Menu:getMswMenu(),
      
     HMenu = MSWMenu:getHMenu(),
  
         ...
  
     ...
end 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.