Search code examples
c++ooppdfclass-design

Design of a class hierarchy for generating a PDF


I am basically having to make a program that will generate a PDF. The PDF will have 3 different page types: a front cover sheet, a general sheet, and a last cover sheet. The header contains a fair amount of information but the header of the front cover sheet and the general sheet only differ by 1 item, however, that one items requires me to shift the others down in coordinates. I have a picture attached to show what I mean.

Also the only purpose the classes are really serving is holding values to represent rectangles that will be used as targets to print text in the pdf. So they really have no need of any functionality aside from the constructor with only initializes the values from a file of constants.

I am trying to use "good" design practice but I am uncertain of what a more efficient method is. It doesn't seem like I can use inheritance that shares the common elements as I will always end up with something I don't need in one of the classes. I thought about just using composition and making a class for each element in the header, that would solve the problem, but then I would have a lot more classes and it would be a class with just to hold one data member which doesn't seem efficient. So I would just appreciate any suggestions on how to make this a more cohesive and sensible design.

The picture is NOT what I have currently but it is to represent that the data I need seems to be coupled awkwardly or perhaps I am just over complicating this.

The general IDea


Solution

  • Front sheet, general sheets and back sheet have in common that they ARE sheets. A good candidate for your class hierarchy would therefore be:

    class sheet {  .... }; 
    class front_sheet : public sheet { ...}; 
    class back_sheet : public sheet { ...}; 
    class general_sheet : public sheet { ...}; 
    

    In sheet, you should put all the common elements, and of course the common behaviour (e.g. print(), save(), get_size(), ...).

    There should be a member function that calculates the position of an element in a page. As the rule depends on the kind of page, it would be a virtual function of sheet(), and front and back sheets could overide the default function. This approach will help you to easily manage the different layout of the different pages.

    class sheet { 
    public: 
        virtual void get_position (int item_no, int& x, int&y) { x=5; y=14*item_no; } 
        ...
    }; 
    
    class back_sheet : public sheet { 
    public: 
        void get_position (int item_no, int& x, int&y) { x=5; y = (item==5 ? 14:0); } 
        ...
    }; 
    

    As inheritance really corresponds to a "is-a" relationship, you'll have get a very robust design.

    You should however think about the content of your sheet. I see here two main orientations:

    • you could manage items in a container (e.g. vectors) : it's easier to organise your output in loops instead of copy pasting similar lines of codes for every item
    • you should ask yourself if the items could have subitems (e.g. a region could contain several subregions, like a print layout with a box and boxes in the box). In this case, i'd recommend the use of the composite pattern

    Edit; After having thought about the content, it could be worth coming back again to the sheets, and ask yourself how much different their behaviour really is:

    • do they have different behaviour thorughout their lifecycle (different way to acquire data for PDF generation or to use dynamically the layout ? In which case the class hierarchy would be ok.
    • or, after having adopted a dynamic structure such as one suggested above, does it turn out that the only difference is the way you create/construct them ? In this case, it could be worth thinking to keep only one sheet class after all, but having three construction "engines", using either the prototype pattern or the builder pattern. This would however be a significant shift in your application's architecture.