0

I have a training management and tracking system, with a high level structure as follows:

We have a Role1, e.g. Manager, Shift-boss, miner, etc. and a Candidate, training for that Role. The role has a list of courses and their subjects the candidate needs to complete to qualify for the role. Candidate has a TrainingHistory attribute, containing the courses and subjects they have completed, their results, and the date completed.

Now I see it as a TrainingHistoryCourse is-a Course, extended to add DateCompleted etc. but something is nagging at me to rather use something like a TrainingHistoryRecord that has-a Course. How can I further analyse this to determine which pattern to use?

Then, a Role has a list of RoleTask definitions that the Candidate must be observed practising, and a Candidate has a history of RoleTaskObservation objects recording their performance at these tasks. This is very similar to the course/subject requirement and history pattern for the candidate, except for one less hierarchical level, but, a RoleTaskObservation clearly does not have an is-a relationship with RoleTask, unless I block my nose and rather use ObservedRoleTask.

I would prefer to use the same pattern for both subject/course and task/observation structures, but I think that would force me to adopt a composition pattern for TrainingHistoryCourse. What is the wisdom here? Always inherit where possible and validated by a solid is-a association, or always favour composition wherever possible?

1 Client specified this to be called JobTitle, but he isn't writing the app, and a JobTitle is only one attribute of a Role. Authorization roles are handled by the DevExpress framework and its customization hooks, so there would be very little little confusion between a business Role in my domain objects and an authorization role in lower level, framework code.

0

2 Answers 2

3

Apologies for the verbosity. Skip down to What is the wisdom here? if you like.

How can I further analyse this to determine which pattern to use?

Keep identifying Use Cases. And make sure your requirements analysis is comprehensive. For example imagine how TrainingHistoryCourse is used for reporting of various detail (specific course work) and summary (final training reports, management progress reports). This could inform the structure of a training record for example.

I get a sense that you're trying to make stuff up just a bit to get a "OO design warm fuzzy" (Look - Inheritance & composition!) - and you may have gaps in requirements analysis.

subject/course

Curriculum is an idea that may coalesce your subject/course concepts. curriculum can be our general container/Class for all training requirements, customized for each specific Role

The ShiftBossCurriculum then is a composition of course*s and *roleTask*s that, when completed, qualifies a candidate as a Shift-Boss,

task/observation

The instructor observes the candidate performing a RoleTask and makes an entry in the candidate's TrainingHistoryCourse Training Record

A candidate completes a given course in a Curriculum. The completion date is entered in the training record. The instructor enters an relevant observations for that course. (hay! I tawt I taw a use case!).

TrainingHistoryCourse

This concept does not make sense. If it is primarily a course completion date, that seems to be a logical extension of a Training record, not THE course itself.

Training Record

From the above discussions, it looks like a training record is a composition of course work, roleTasks, and observations.

Potential Inheritance Bases

Role, Task, Course, curriculum, training record, candidate... In fact any of your general actors and objects. It depends on any identified (or future/potential) specialization/customization required of these general things. So far we have identified specialized curriculum, Roles, courses, and Tasks. But even then this does not guarantee we will use inheritance; the devil is in the details.

Potential Composition

Clearly curriculum and training record are composites. But not because of the simplistic observation that they contain things (what object doesn't?). Read on, Macduff.


What is the wisdom here?

Always inherit where possible and validated by a solid is-a association, or always favour composition wherever possible?

That is wrong. The maxim is favor composition over inheritance. "Always" ain't in there. Following this heuristic means your software tends to be more flexible, easier to maintain, extend, and reuse.

Don't go inventing inheritance hierarchy where it does not exist. In your business model, is there really a need to handle courses as "math courses" or "shift-boss courses", etc.? Or are they just courses that are logically associated in a "shift boss curriculum"? The course name, course department, etc./whatever properties will be filled in at runtime to give us a particular course.

Likewise a curriculum is also just a curriculum. What makes it a "shift boss" curriculum? Simply a different set of courses vis-a-vis the "manager" courses.

So we have identified general "curriculum", and that by composing it with different instances of courses makes a different curriculum. Thus we have composition to make a "manager" curriculum, not a ManagerCurriculum sub-class of curriculum.

Having identified a general concept it can be coded as an interface (interfaces can be literally interfaces or abstract classes). What you do to any specific course you do with any course. NOW we have flexible, re-usable, extensible, maintainable code.

0

You should feel nagged! Inheritance should only be used if you are introducing a specialization of the parent class. Aggregation should be used when 1 object consists of another (different) objet to make it whole.

Here is an example of inheritance using your course, as we move right to left we get more specialized.

Course <-- MathCourse <-- Geometry

In this example TrainingHistoryCourse does not make sense because it is not itself a class, rather a notation that someone took a class.

What if we tried to use inheritance?

Course<--MathCourse<--Geometry<--TrainingHistryGeometry

With this approach we would be adding a new TrainingHistory specilization of each class.

Course<--TrainingHistoryCourse<--MathCourse<--Geometry

This approach would make us create a new instance of the Geometry class everytime it is offered. This could work but we would need a seperate instance of the GeometryClass evertime it is offered. Which is weird because they are the same class just offerred at different times.

Skip to the chase

In this example it is better to use your TrainingHistoryRecord approach because each student is taking the same class. You just need to record when it was taken and what their score was.

Some More Examples

You can usually "sound it out" to figure out the best approach

A car is-a vehicle NOT a car has-a vehicle

A sedan is-a car NOT a sedan has-a car

A car has-a engine NOT a car is-a engine

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.