The following text is a set of recommendations for the implementation of the UML structures required for mini-project 3 (MP3).
Before proceeding with the implementation, you should review the syntax and semantics of the constructs required for implementation:
PRI lectures in English (Public folder)
PRI lectures in Polish (Public folder)
PRI lectures in Polish (website)
1. In accordance with the requirements described in point 3 of the requirements document, each structure should have a separate business example (do not combine the structures listed in the mini-project requirements).
2. Each structure should have a separate business example and implementation. The structures should not be joined together (e.g., overlapping and dynamic inheritance).
3. Abstract class / polymorphism.
3.1. A meaningful business case should be proposed. The polymorphism of the methods should include different algorithms for business method, depending on the subclass. Simple solutions, e.g. returning a different constant depending on subtype will not be accepted.
4. Overlapping inheritance.
4.1 An object can have multiple roles simultaneously among the subtypes listed in inheritance.
4.2. During the construction of the object, it is possible to set any combination of roles defined in inheritance. This can be done with a universal constructor, or with a static object creation method.
4.3. The role set assignment is static - it is not possible to add / remove / change a role after the object has been created.
4.4. There are two main ways to implement overlapping inheritance:
4.4.1. Implementation by flattening the class hierarchy.
4.4.1.1. All members (attributes, methods, associations) will be transferred to the base class.
4.4.1.2. The base class object should be allowed to be created - it cannot be abstract.
4.4.1.3. Introduce a discriminator to indicate the roles of the object (e.g. an EnumSet).
4.4.1.4. It is possible to use only those methods of the object that are appropriate for the allocated role set. Attempting to use the wrong method should result in an exception being thrown.
4.4.1.5. While creating the object take into account the validation of mandatory attributes for a given set of roles.
4.4.1.6. It is worth introducing interfaces for each role containing methods that support the members of a given role. The base class will then implement all of these interfaces. This will make the code more readable by easier identification of the affiliation of class members to particular roles.
4.4.2. Implementation by replacing inheritance with composition.
4.4.2.1. For each role, a separate class should be created containing the members characteristic for a given role.
4.4.2.2. These classes should not inherit from the base class, as this will result in duplicate members base class.
4.4.2.3. The base class object will be composed of objects defining its roles.
4.4.2.4. The base class object should be allowed to be created - it cannot be abstract.
4.4.2.5. After the root object is initialized, it is not possible to add / remove the objects representing the roles.
4.4.2.6. If you have a reference to the main object, there should be allowed to use members placed in subtypes. This can be accomplished with method delegation (using relevant interfaces), or methods to retrieve role objects.
5. Dynamic inheritance.
5.1. An object can have one role among the subtypes listed in inheritance.
5.2. The role of an object can be changed to a different role during its lifetime.
5.3. There are three main ways to implement dynamic inheritance:
5.3.1. Implementation using “normal” inheritance and a copy constructor.
5.3.1.1. Classes representing subtypes have a constructor whose argument is reference to the base class object. It represents the “old” role of the object.
5.3.1.2. Initialization should involve rewriting the values characteristic for the base class to new object. This includes attributes and associations of the base class.
5.3.1.3. When the role is changed, the “old” object ceases to exist. It is therefore necessary to “clean up” after it - remove it from the class extent and remove its associations.
5.3.1.4. A minimal implementation using this approach should consider removing the “old” object from the class extent.
5.3.2. Implementation by flattening the class hierarchy.
5.3.2.1. Implementation similar to implementing an overlapping inheritance by flattening the hierarchy classes. The discriminator is a single Enum value, since object may have only one role at a given moment.
5.3.2.2. Provide methods for changing the role of an object.
5.3.2.3. The attributes and associations required in the new role should be properly initialized. “Old” attributes and associations that do not match the new role should be removed.
5.3.3. Implementation by replacing inheritance with composition.
5.3.3.1. Implementation similar to the implementation of overlapping inheritance with composition.
5.3.3.2. Only one “part” object representing the object role can be assigned at a time.
5.3.3.3. Provide methods for changing the role of an object.
6. Multi-inheritance.
6.1. Impossible for direct implementation in Java and similar languages.
6.2. One of the branches of inheritance should be replaced with an interface implementation as shown in the diagram below.
6.3. Note that both the Student and WorkingStudent class in the example should implement the interface IStudent. Otherwise, the listed classes will not have a common type, which is contrary to the principle of inheritance.
7. Multi-aspect inheritance
7.1. By default, an object has exactly one role from each inheritance hierarchy associated with that construct.
7.2. These roles cannot be changed over the of the object’s lifetime.
7.3. One branch of inheritance can be implemented as “normal” inheritance, the other as composition or flattening the hierarchy in the base class.