0

I'm developing a full-stack Rest application following a narrative description of a working context. There is a class Job with two subclasses Job_A and Job_B. Job_A produces Report_A while Job_B produces Report_B. I guess there is an abstract superclass Report even if it is not stated explicitly. Each report must be examined and goes through a verification chain built depending on the type of the report itself.

uml-1


I'm tempted to add an abstract class Report and draw a relationship between Job and Report. With this design (UML below) it seems possible to assign any document to any job, which is wrong. I'm also afraid of ending up in a spiral of if document_type to manage documents. If this is the way, is there a design pattern or idiom to formally define constraints on subtypes while keeping a reference to a super-type ?

uml modified


Maybe i should merge the previous diagrams by adding the abstract Report and keeping the relationships of the first design.

3
  • "I guess there is an abstract superclass Report" - not necessarily; even though both of these types are conceptually reports, abstract classes (or interfaces) do not model arbitrary conceptual hierarchies - it has to be about behavior, where there's a piece of logic that can somehow treat both kinds of reports the same without doing type checks. E.g., maybe your verification chain is the same for all in the abstract? It would help if you provided more info on how you expect these classes to be used w/ respect to the considerations above. 1/2 Mar 26 at 16:34
  • Now, there is a creational pattern that connects parallel hierarchies, but it introduces a lot of complexity, so I would make sure that it's really needed before applying it - it's called Abstract Factory (1, 2); the abstract Products would correspond to your Job and Report classes. You can also compose Jobs and Reports through the factory (get them out bundled). The assumption is that you can make some use of Job and Report through the base class interfaces. 2/2 Mar 26 at 16:34
  • From a business process perspective, does a job really produce the report? Or is the job the subject of the report? Mar 26 at 20:10

2 Answers 2

1

I'm not quite sure how to visualize this in UML, but in code (I picked C# here) this can be achieved using a generic type:

public abstract class Job<TReport> where TReport : Report
{
    public abstract TReport GetReport();
}

Each derived job can then define its own concrete report type.

public class JobA : Job<ReportA>
{
    // ...
}

This ensures that the specific report type is known when interacting with the report:

var myJob = new JobA();
var myReport = myJob.GetReport();

myReport will be of type ReportA, no casting or guessing necessary.

1

Good news: it's easy to model

If you want to specify that Job A produces Report A,and Job B Report B, you could specify some kind of "covariance" using for example UML subsetting or redefginition:

enter image description here

You could also use association specialization, which is the better approach imho (yes! an association can inherit from another association, even if this is not well known!):

enter image description here

I used simple associations instead of composition, as I assume that reports shall survive the jobs that created them. You'll find more explanations about the suggested alternatives in this fascinating research paper. You may also see some practical illustrations in this question on SO (it's about association classes, but the arguments apply as well for associations in general).

Bad news: it's difficult to implement nicely

Unfortunately, once you have specified the rule in your UML model, you'll have some serious challenges for the implementation: Many languages do not support covariance.

Even worse, in the kind of covariance you have, there's no way to deduce covariance of Report A from Job A except the name of the classes. How would this work if you'd opted for Job A with Report 1? So unless you can find some construct with generics (e.g. Job<Report X>) you might be obliged to use some ugly casting.

Some more thoughts about the design

A first question that comes to my mind is whether it is a good idea that the Report X has also the responsibility to drive the workflow to check and validate it.

A second point to raise, is that a report is a report. Instead of an abstract Report that is specialized, you could instead prefer composition over inheritance. In this case, every Job would produce a Report, but each report would itself be composed on different (eventually several) contents/components:

enter image description here

This will make a much more uniform handling of the reports and will simplify the design. If deemed useful, you could consider a GoF builder pattern in each Job to assemble the reports and their component. But it is not certain imho that you'll really need the sophistication of this pattern.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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