Eval Criteria # 12
Can types be formed through inheritance or composition?
Content models sometimes get large and unwieldy over time – lots of types, with lots of attributes. Remember, content models need to be manageable. I’ve seen models with multiple dozen content types, each with dozens of attributes many of which are repeated on multiple other types, and the whole thing is so complex that editors have little idea of what content goes where and types are poorly differentiated from each other.
Two features can help you manage this by centralizing attributes into manageable units which can be combined to form types.
Here’s a simple fact: a lot of your content types will be very similar. Look at the content model behind the average website, and you’ll find a lot of types with a Title and a Body attribute. In fact, this is often the built-in model, and types are differentiated only in the ways they differ from this built-in model.
- Add a Date Published and an and you have an Article or Blog Post
- Add an Industry selector and you have a Case Study
- Add a File Upload you have a White Paper
It could be said, then, that every content type inherits the built-in model. A content type isn’t built from scratch. It starts off with the attributes from the built-in model, and adds additional attributes to differentiate itself. This is helpful because you often don’t need to duplicate Title and Body on every content type.
What if you could further inherit types from your own custom types? This is a concept called type inheritance.
Say you decide that every page on your website – whether it be a Text Page, a Press Release, or an Employee Bio – should have an additional tab of attributes for Description and Keywords to represent information that will be rendered as META tags on the page.
You could, of course, just add these to every content type. However, what if you wanted to add some additional SEO attributes later on – like a ROBOTS meta value, or some Open Graph or Twitter Card meta? You’d then have to manually add these to all the types as well.
A more manageable way to handle this would be to create a type for Web Page which includes all these attributes, then inherit all the other types from that. So, Web Page type inherits the built-in model, and Simple Text Page, Press Release, and Employee Bio inherit from Web Page, getting all the attributes of both before they even add any attributes of their own.
An inheritance relationship is “active,” meaning the attributes are not just copied onto the child type at the time of creation, but rather the child type has a real-time link back to the parent type from which it is inherited, and anything added to the parent type is also instantly available in the child type.
Using inheritance, you can drastically reduce the number of attributes you need on each type. You can “lift out” common attributes and move them “up” to parent types which are inherited, and maintain these common attributes from the top down.
Your content types now exist in a tree of increasing specificity. At the top is the built-in model which represents the most general content (and which might never actually be used directly; more on that below), and below that are types which incrementally add attributes. The type tree branches out to provide specific attribute inheritance to different groups of types.
Limitations of Hierarchical Inheritance
While inheritance is absolutely helpful, it does have drawbacks. Logically, types can only have one parent. It can be tricky if you have attributes that need to inherit down multiple branches of a type tree.
Consider our example from above. What if you decided that a lot of content has a set of Contact attributes – a Contact Name, Phone Number, etc. You want this for our Press Release type, but also for other types like our Case Study and Legislative Action types.
Great, just add an interstitial type under Web Page for … Content with Contact Information … and then you can put a bunch of types under that.
Later, you decide that a bunch of types are date- and location-specific, meaning they have a Start Date and an optional End Date, along with a Location Name and Location Coordinates. So you decide to create… Content with Location Information … and put other types under that.
However, then you realize that Press Release really needs both of those things. How can you maneuver it to inherit from both of them?
The underlying problem is this: with type inheritance, you can only have one path back up the tree, and your type will “collect” all the attributes it finds on that path. This is sometimes exactly what you want, but sometimes it isn’t and you can get locked into a weird place.
What can often be more helpful than inheritance, is the concept of type composition.
Content types are often composed of pre-defined sets of attributes. Think of it like a buffet – you walk around with your plate, and you take a little from here, and a little from here, and eventually you have a full plate. Your plate hasn’t inherited from anything – it starts out completely empty. You’ve just “composed” a full plate out of different things you find on the buffet line.
Composition encourages you to group your attributes into sets that serve a specific purpose, then compose a type from (1) its own, unique attributes, and (2) additional sets of attributes that serve additional purposes.
Sets of related attributes are effectively “free floating” and not locked into a tree. They can be injected into any type when necessary.
Type Dependencies and Abstract Types
When dealing with inheritance or composition, you’re expanding the boundaries of your types to encompass other types and groups of attributes. This can cause problems if not monitored carefully.
It’s helpful for a system to differentiate between “public types” and “support types.” There will be some types that editors are expected to create objects from (Press Release, for example), and others that exist only to enhance other types (Location Info, for example).
An editor shouldn’t be able to directly create the latter. Some of this can be handled through model access permissions, but a system might have another method to specify that a type is solely meant to support inheritance or composition, and is never intended to be the source type for an object.
You also need to beware of circular references. If B inherits from A, you need to somehow make sure that A didn’t inherit from B, or else you have a circular reference and when the system tries to crawl up the type tree, it’s going to run around in circles. The same thing could theoretically happen in composition. If B incorporates A, then A cannot incorporate B. Hopefully, a system has some method for preventing this.
Finally, you need to be more careful with type changes, because they can have vastly magnified effects. If you’ve injected SEO Info into 90% of your other types, and someone decides Press Releases don’t need Keywords anymore, then deletes that attribute… well, Very Bad Things might happen. When every type has its own attributes, then type changes are localized. When you start inheriting or compositing types, type changes become systemic, sometimes with disastrous results.
Type inheritance and composition can vastly improve the manageability of any content model. Inheritance is absolutely better than no inheritance. However, in my experience, composition is the more useful tool overall.
- Can custom content types be inherited or extended from existing types?
- Can custom content types be composed by active inclusion of pre-configured sets of attributes?
- Can content types or attribute sets be configured in such a way that they exist only for inheritance and composition, and cannot be the direct source for a new object?
- Are there any safeguards against circular references?