How do you apply HandleBodyPattern to cyclic object graphs? Suppose in version 1.0, we have this code as part of our file-system browser:
interface File { byte[] get_Data(); } interface Folder { string get_Name(); Enumeration<Folder> get_SubFolders(); Enumeration<File> get_Files(); Folder FindSubFolderByName(string name); File FindFileByName(string name); }In version 2.0, we add a spiffy feature so that you can have your file and folder names automatically translated from your native language. This is implemented using HandleBodyPattern as follows:
class LanguageFolderWrapper implements Folder { LanguageFolderWrapper(Folder wrapped,LanguageTranslator translator) { _wrapped=wrapped; _translator=translator; } string get_Name() { return _translator.TranslateName(_wrapped.get_Name()); } Enumeration<Folder> get_SubFolders() { // we have to recursively apply wrappers to sub-folders to maintain the language illusion List<Folder> wrappedSubFolders=new List<Folder>; foreach(Folder folder in _wrapped.get_SubFolders()) wrappedSubFolders.Add(new LanguageFolderWrapper(folder)); return wrappedSubFolders; } Enumeration<File> get_Files() { return _wrapped.get_Files(); } Folder FindSubFolderByName(string name) { string translatedName=_translator.TranslateName(name); return _wrapped.FindSubFolderByName(translatedName); } File FindFileByName(string name) { string translatedName=_translator.TranslateName(name); return _wrapped.FindFileByName(translatedName); } }Language translation is a huge success. But it works so well that it is soon forgotten. It is relegated to the dusty confines of SCM. In version 3.0, we realized a couple limitations of the original File interface. So, we add new features for this version as follows:
interface File { string get_Name(); Folder get_Parent(); byte[] get_Data(); }Looks good, so we ship it. Soon, our customers start using these interfaces on their own. It is brought to our attention that language translation breaks if you traverse down to a File, and then back up to its parent Folder. Seems that we forgot something.
What is the cause of this problem we face? Let's look at each of these possibilities one by one.
This isn't specific to cyclic object graphs. Suppose that the File abstraction was changed so that a File could "contain" folders (for example think of archive files). Even if the resulting structure was restricted to a tree, the contained folders would not be wrapped. In this case the interface dependencies are cyclic, but not the object graph.
In this example, when the language translation was recognised to work well but before it was forgotten, the code probably should have been refactored to integrate the translation into the base implementations of files and folders. This doesn't invalidate the main point of the example.