5.1. Class Relationships
Classes do not live in a vacuumthey work together using different types of relationships. Relationships between classes come in different strengths, as shown in Figure 5-1.
The strength of a class relationship is based on how dependent the classes involved in the relationship are on each other. Two classes that are strongly dependent on one another are said to be tightly coupled ; changes to one class will most likely affect the other class. Tight coupling is usually, but not always, a bad thing; therefore, the stronger the relationship, the more careful you need to be.
Figure 5-1. UML offers five different types of class relationship
A dependency between two classes declares that a class needs to know about another class to use objects of that class. If the UserInterface class of the CMS needed to work with a BlogEntry class's object, then this dependency would be drawn using the dependency arrow, as shown in Figure 5-2.
Figure 5-2. The UserInterface is dependent on the BlogEntry class because it will need to read the contents of a blog's entries to display them to the user
The UserInterface and BlogEntry classes simply work together at the times when the user interface wants to display the contents of a blog entry. In class diagram terms, the two classes of object are dependent on each other to ensure they work together at runtime.
A dependency implies only that objects of a class can work together; therefore, it is considered to be the weakest direct relationship that can exist between two classes.
Although dependency simply allows one class to use objects of another class, association means that a class will actually contain a reference to an object, or objects, of the other class in the form of an attribute. If you find yourself saying that a class works with an object of another class, then the relationship between those classes is a great candidate for association rather than just a dependency. Association is shown using a simple line connecting two classes, as shown in Figure 5-3.
Figure 5-3. The BlogAccount class is optionally associated with zero or more objects of the BlogEntry class; the BlogEntry is also associated with one and only one BlogAccount
Navigability is often applied to an association relationship to describe which class contains the attribute that supports the relationship. If you take Figure 5-3 as it currently stands and implement the association between the two classes in Java, then you would get something like that shown in Example 5-1.
Example 5-1. The BlogAccount and BlogEntry classes without navigability applied to their association relationship
Without more information about the association between the BlogAccount and BlogEntry classes, it is impossible to decide which class should contain the association introduced attribute; in this case, both classes have an attribute added. If this was intentional, then there might not be a problem; however, it is more common to have only one class referencing the other in an association.
In our system, it makes more sense to be able to ask a blog account what entries it contains, rather than asking the entry what blog account it belongs to. In this case, we use navigability to ensure that the BlogAccount class gets the association introduced attribute, as shown in Figure 5-4.
Figure 5-4. If we change Figure 5-3 to incorporate the navigability arrow, then we can declare that you should be able to navigate from the blog to its entries
Updating the association between the BlogAccount class and the BlogEntry class as shown in Figure 5-4 would result in the code shown in Example 5-2.
Example 5-2. With navigability applied, only the BlogAccount class contains an association introduced attribute
126.96.36.199. Association classes
Sometimes an association itself introduces new classes. Association classes are particularly useful in complex cases when you want to show that a class is related to two classes because those two classes have a relationship with each other, as shown in Figure 5-5.
In Figure 5-5, the BlogEntry class is associated with a BlogAccount. However, depending on the categories that the account contains, the blog entry is also associated with any number of categories. In short, the association relationship between a blog account and a blog entry results in an association relationship with a set of categories (whew!).
Figure 5-5. A BlogEntry is associated with an Author by virtue of the fact that it is associated with a particular BlogAccount
There are no hard and fast rules for exactly how an association class is implemented in code, but, for example, the relationships shown in Figure 5-5 could be implemented in Java, as shown in Example 5-3.
Example 5-3. One method of implementing the BlogEntry to BlogAccount relationship and the associated Category class in Java
Moving one step on from association, we encounter the aggregation relationship. Aggregation is really just a stronger version of association and is used to indicate that a class actually owns but may share objects of another class.
Aggregation is shown by using an empty diamond arrowhead next to the owning class, as shown in Figure 5-6.
Figure 5-6. An aggregation relationship can show that an Author owns a collection of blogs
The relationship between an author and his blogs, as shown in Figure 5-6, is much stronger than just association. An author owns his blogs, and even though he might share them with other authors, in the end, his blogs are his own, and if he decides to remove one of his blogs, then he can!
Moving one step further down the class relationship line, composition is an even stronger relationship than aggregation, although they work in very similar ways. Composition is shown using a closed, or filled, diamond arrowhead, as shown in Figure 5-7.
Figure 5-7. A BlogEntry is made up of an Introduction and a MainBody
A blog entry's introduction and main body sections are actually parts of the blog entry itself and won't usually be shared with other parts of the system. If the blog entry is deleted, then its corresponding parts are also deleted. This is exactly what composition is all about: you are modeling the internal parts that make up a class.
5.1.5. Generalization (Otherwise Known as Inheritance)
Generalization and inheritance are used to describe a class that is a type of another class. The terms has a and is a type of have become an accepted way of deciding whether a relationship between two classes is aggregation or generalization for many years now. If you find yourself stating that a class has a part that is an object of another class, then the relationship is likely to be one of association, aggregation, or composition. If you find yourself saying that the class is a type of another class, then you might want to consider using generalization instead.
In UML, the generalization arrow is used to show that a class is a type of another class, as shown in Figure 5-8.
Figure 5-8. Showing that a BlogEntry and WikiPage are both types of Article
The more generalized class that is inherited fromat the arrow end of the generalization relationship, Article in this caseis often referred to as the parent, base, or superclass. The more specialized classes that do the inheritingBlogEntry and WikiPage in this caseare often referred to as the children or derived classes. The specialized class inherits all of the attributes and methods that are declared in the generalized class and may add operations and attributes that are only applicable in specialized cases.
The key to why inheritance is called generalization in UML is in the difference between what a parent class and a child class each represents. Parent classes describe a more general type, which is then made more specialized in child classes .
188.8.131.52. Generalization and implementation reuse
A child class inherits and reuses all of the attributes and methods that the parent contains and that have public, protected, or default visibility. So, generalization offers a great way of expressing that one class is a type of another class, and it offers a way of reusing attributes and behavior between the two classes. That makes generalization look like the answer to your reuse prayers, doesn't it?
Just hold on a second! If you are thinking of using generalization just so you can reuse some behavior in a particular class, then you probably need to think again. Since a child class can see most of the internals of its parent, it becomes tightly coupled to its parent's implementation.
One of the principles of good object-oriented design is to avoid tightly coupling classes so that when one class changes, you don't end up having to change a bunch of other classes as well. Generalization is the strongest form of class relationship because it creates a tight coupling between classes. Therefore, it's a good rule of thumb to use generalization only when a class really is a more specialized type of another class and not just as a convenience to support reuse.
184.108.40.206. Multiple inheritance
Multiple inheritanceor multiple generalization in the official UML terminologyoccurs when a class inherits from two or more parent classes, as shown in Figure 5-9.
Figure 5-9. The DualPurposeAccount is a BlogAccount and a WikiAccount all combined into one
Although multiple inheritance is supported in UML, it is still not considered to be the best practice in most cases. This is mainly due to the fact that multiple inheritance presents a complicated problem when the two parent classes have overlapping attributes or behavior.
So, why the complication? In Figure 5-9, the DualPurposeAccount class inherits all of the behavior and attributes from the BlogAccount and WikiAccount classes, but there is quite a bit of duplication between the two parent classes. For example, both BlogAccount and WikiAccount contain a copy of the name attribute that they in turn inherited from the Account class. Which copy of this attribute does the DualPurposeAccount class get, or does it get two copies of the same attribute? The situation becomes even more complicated when the two parent classes contain the same operation. The BlogAccount class has an operation called getEnTRies( ) and so does the WikiAccount.
Although the BlogAccount and WikiAccount classes are kept separate, the fact that they both have a getEntries( ) operation is not a problem. However, when both of these classes become the parent to another class through inheritance, a conflict is created. When DualPurposeAccount inherits from both of these classes, which version of the getEntries( ) method does it get? If the DualPurposeAccount's getEnTRies( ) operation is invoked, which method should be executed to get the Wiki entries or the blog entries?
The answers to these question are unfortunately often hidden in implementation details. For example, if you were using the C++ programming language, which supports multiple inheritance, you would use the C++ language's own set of rules about how to resolve these conflicts. Another implementation language may use a different set of rules completely. Because of these complications, multiple inheritance has become something of a taboo subject in object-oriented software developmentto the point where the current popular development languages, such as Java and C#, do not even support it. However, the fact remains that there are situations where multiple inheritance can make sense and be implementedin languages such as C++, for exampleso UML still needs to support it.