16

For example, is it better to do:

Pdf pdf = new Pdf();
pdf.Print();

or:

Pdf pdf = new Pdf();
PdfPrinter printer = new PdfPrinter();
printer.Print(pdf);

Another example:

Country m = new Country("Mexico");
double ratio = m.GetDebtToGDPRatio();

or:

Country m = new Country("Mexico");
Country us = new Country("US");
DebtStatistics ds = new DebtStatistics();
double usRatio = ds.GetDebtToGDPRatio(us);
double mRatio = ds.GetDebtToGDPRatio(m);    

My concern in the last example is there are potentially endless statistics (but let's say even just 10) you might want to know about a country; do they all belong on the country object?

e.g.

Country m = new Country("Mexico");
double ratio = m.GetGDPToMedianIncomeRatio();

These are simple ratios but lets assume the statistics are complicated enough to warrant a method.

Where is that line between operations that are intrinsic to an object vs operations that can be performed on an object but are not part of it?

0

2 Answers 2

17

Taking your PDF examples as starting point, let's look at this.

http://en.wikipedia.org/wiki/Single_responsibility_principle

The Single Responsibility Principle suggests that an object should have one and only one objective. Keep this in mind.

http://en.wikipedia.org/wiki/Separation_of_concerns

Separation of Concerns principle tells us that classes should not have overlapping functions.

When you look at these two, they suggest that logic should go in a class only if it makes sense, only if that class is responsible for doing that.

Now, in your PDF example, the question is, who is responsible for printing? What makes sense?

First code snippet:

Pdf pdf = new Pdf();
pdf.Print();

This is not good. A PDF document does not print itself. It gets printed by... ta da!.. a printer. So you second code snippet is much better:

Pdf pdf = new Pdf();
PdfPrinter printer = new PdfPrinter();
printer.Print(pdf);

This makes sense. A Pdf Printer prints a pdf document. Better yet, a printer shouldn't be a PDF printer, or a photo printer. It should just be a printer capable to print stuff sent to it to the best of its capabilities.

Pdf pdf = new Pdf();
Printer printer = new Printer();
printer.Print(pdf);

So that's simple. Put methods where they make sense. Obviously, it is not always that simple. Take your country statistics for example:

Country m = new Country("Mexico");
double ratio = m.GetDebtToGDPRatio();

Your concern is that there might be n number of statistics, and that they shouldn't be in a Country class. That is true. However, if your model only calls for that particular statistics, this modeling example might actually be fine.

In this case, you could say quite logically that a country should be able to compute its own statistics, specific to your model and the requirements at hand.

And therein lies the thing: what are your requirements? Your requirements will drive the way you model the world, the context, in which these requirements are to be satisfied.

If you indeed have a multitude/variable number of statistics, then your second example makes more sense:

Country m = new Country("Mexico");
DebtStatistics ds = new DebtStatistics();
double usRatio = ds.GetDebtToGDPRatio(m);

Better yet, have an abstract superclass or interface called Statistics that take a country as parameter:

interface StatisticsCalculator // or a pure abstract class if doing C++
{
   double getStatistics(Country country); // or a pure virtual function if in C++
}

class DebtToGDPRatioStatisticsCalculator implements StatisticsCalculator ....

class InfantMortalityStatisticsCalculator implements StatisticsCalculator ...

And so on and so on. Which leads to the following: generalization, delegation, abstraction. Statistical gathering gets delegated to specific instances that generalize a specific abstraction (a statistics collection API).

I don't know if this answers your question 100%. After all, we don't have infallible models ground on inviolable laws (like EE folks do.) All you can do is put things were they make sense. And that's an engineering decision you need to make. The best thing to do is to really get acquainted with OO principles (and good software modeling principles in general.)

8
  • 1
    +1 for the StatisticsCalculator Interface (and subsequent use of the Strategy Pattern). And the thorough well thought out answer May 19, 2011 at 22:39
  • 3
    insufficient time to deconstruct this thoroughly at the moment, but must point out that the Printer class will become a God class over time, tightly coupled to all kinds of document classes. Pdf.Print would be preferable - but it all depends on how you define the 'single responsibility' ;-) May 20, 2011 at 2:41
  • @Steve - what you are proposing is a horrible idea (having Pdf implement print()). It doesn't reflect how printing is implemented in real life. Every operating system and printing API that I'm aware off provides an abstraction for Printer. Look at the printers list in your XP/Vista machine (or under /var/spool or equivalent in *nix.) Each application serializes a document object to one of its printers. There is no Word printer, or text Printer or PDF printer. There are just printers specific to the printing device and not specific to the document type. May 20, 2011 at 15:06
  • 2
    +1 I like it I'm pondering what you've said.. @Steve and luis: I think the missing piece of the debate about God objects is a generic Printer object should accept a few standard formats like ASCII or bitmap (although pdf is probably reasonable too) and it should be the responsibility of some 3rd class to convert a particular document type (say a ms word document) to one of these standard formats.
    – User
    May 20, 2011 at 15:51
  • 2
    It seems to me that perhaps a PDF should be able to render itself either onto a Canvas interface or into an Image object which could then be processed by a Printer object. May 21, 2011 at 2:31
4

I think neither is definitely better than the other. Using pdf.Print() is tighter, but having a PdfPrinter class might be better if:

  • You need to manage instances of the printers
  • There are a wide range of options and actions that would blow out the pdf.Print(...) complexity (e.g. print cancellation, extra formatting, etc)

I wouldn't get hung up on it otherwise.

2
  • a good, practical answer; time will tell how this needs to evolve May 20, 2011 at 2:41
  • 1
    The short suggestion is to look at both logic and data when applying SRP, in order to decide whether we will regret not decoupling them sooner. The problem with storing per-printer settings in the Pdf class is that they are not supposed to be stored together - Pdf is stored in a file but per-printer settings should be stored with the user/machine profile.
    – rwong
    May 20, 2011 at 6:15

Your Answer

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

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