I am looking for an elegant design for a problem analogous to the following:
A story can have a flexible hierarchical structure. It might consist of several books, each of which have chapters, each of which have sections, each of which contain text. Or, it might simply have sections (a short story for instance). Or it might just consist of chapters that have sections. In all situations, you mustn't be allowed to mix styles (so you couldn't add a chapter to a story based on books, you would have to add the chapter to the book itself).
I have come up with a couple of design solutions for this type of problem but they get messy. Is there a clean way to represent this so that, given a reference to a Story class I can get access to the content in a clear, systematic fashion?
Its kind of tricky situation because the concepts like "Books", "chapters", "sections", may have some common elements that suggest a class hierarchy or common interface implementation.
And at the same time, enough different to be handle as different classes / objects, at all, as the requirement of "so you couldn't add a chapter to a story based on books".
When dealing conceptualy with hierarchical objects, there are several approaches of how to turn them into into code, each one suits better to a particular situation.
1. Class Composition
There is a class or prototype for each concept, they may be related or not by inheritance or interfaces. There are internal collections of the elements, and their operations can be restricted, by methods.
// this one can be optional, not required,
// replaced by several parent classes,
// or replaced by interfaces
public class LibraryRootClassMaybe {
// members here
}
public class BookText extends LibraryRootClassMaybe {
// members here
} // class BookText
public class BookSection extends LibraryRootClassMaybe {
// element collection should not be public
List BookTexts;
public Book() {
this.BookTexts = new ArrayList();
}
public void addBookTest(BookText Item) {
// validation and casting from object to BookText
}
// members here
} // class BookSection
public class BookChapter extends LibraryRootClassMaybe {
// element collection should not be public
List BookSections;
public Book() {
this.BookSections = new ArrayList();
}
public void addBookTest(BookSection Item) {
// validation and casting from object to BookSection
}
// members here
} // class BookChapter
public class Book extends LibraryRootClassMaybe {
// element collection should not be public
List BookChapters;
public Book() {
this.BookChapters = new ArrayList();
}
public void addBookTest(BookText Item) {
// validation and casting from object to BookText
}
// members here
} // class Book
These approach is good when there are not many different classes, maybe 5.
2. The Tree Design Pattern.
These one applies when all elements will be equal if not similar, usually same class or interface, usually a lot of items.
These one does not apply to your case, but, I had to mention, to apply better.
Usually, a tree / hierarchical collection class, its used. It could be a subclass of a generic / template tree collection, or a subclass of a base tre collection, that is intended to be replaced by child classes with particular members.
public class FileSystemRootClass {
public bool AmISystemRoot() {
// more
}
public bool AmIAFolder() {
// more
}
public bool AmIAFile() {
// more
}
public void addSystemRoot(string FileSystemName) {
// more
}
public void addFolder(string FileSystemName) {
// more
}
public void addFile(string FileSystemName) {
// more
}
// members here
}
3. The Hybrid.
These one is a combination of the previous two, its used when there is a lot of related items, its more complex, may use or not the Factory & Abstract Factory Patterns, and its more common example are visual controls & widgets libraries.
import java.util.*;
public class WidgetsExample {
public static void main(String[] args) {
TreeSet <Widget> FormControls = new TreeSet<Widget>();
TextBoxWidget TextBox = new TextBoxWidget();
FormControls.add(TextBoxWidget);
ListBoxWidget ListBox = new ListBoxWidget();
FormControls.add(TextBoxWidget);
ButtonWidget Button = new ButtonWidget();
FormControls.add(Button);
} // class WidgetsExample
You may notice that I didn't use the "factory pattern" & "abstract factory", due to require more code.
Good Luck.
Good OO design starts with thinking about Use Cases and not class hierarchies. This is a common mistake and tends to produce over-complicated designs.
First consider what you are building and write out a problem statement a description of the problem you are solving using English prose in the language of the problem domain.
Then consider making a mockup if the product is a UI.
Then you can start writing out use cases and start thinking about how objects will interact with each other.
It's called Object-oriented programming, not Class-oriented programming. Classes are the specification in code to manage/create/run all the objects in the system. I'd be thinking about objects and what they are doing. Classes are simply an implementation detail.
If your goal is to perform operations on hierarchies, you might want to consider using the Composite pattern. You could do something like have a Story object that can contain a list of Story objects. Each Story object would also have it's own type (book-collection,book,chapter,sub-chapter,paragraph,essay), and it's own attributes and methods (depending on your use cases).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With