Object-Oriented Design – Object Method vs Separate Class’s Method

class-designfunctionsmethodsobject-oriented-designstatistics

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?

Best Answer

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.)

Related Topic