Prenant vos exemples PDF comme point de départ, regardons cela.
http://en.wikipedia.org/wiki/Single_responsibility_principle
Le principe de responsabilité unique suggère qu'un objet devrait avoir un et un seul objectif. Garde ça en tête.
http://en.wikipedia.org/wiki/Separation_of_concerns
Le principe de séparation des préoccupations nous dit que les classes ne devraient pas avoir de fonctions qui se chevauchent.
Lorsque vous regardez ces deux, ils suggèrent que la logique ne devrait aller dans une classe que si elle a du sens, seulement si cette classe est responsable de le faire.
Maintenant, dans votre exemple PDF, la question est, qui est responsable de l'impression? Qu'est-ce qui a du sens?
Premier extrait de code:
Pdf pdf = new Pdf();
pdf.Print();
Ce n'est pas bien. Un document PDF ne s'imprime pas. Il est imprimé par ... ta da! .. une imprimante. Donc, votre deuxième extrait de code est bien meilleur:
Pdf pdf = new Pdf();
PdfPrinter printer = new PdfPrinter();
printer.Print(pdf);
C'est logique. Une imprimante Pdf imprime un document pdf. Mieux encore, une imprimante ne devrait pas être une imprimante PDF ou une imprimante photo. Il devrait simplement s'agir d'une imprimante capable d'imprimer au mieux de ses capacités.
Pdf pdf = new Pdf();
Printer printer = new Printer();
printer.Print(pdf);
C'est donc simple. Mettez les méthodes là où elles ont du sens. Évidemment, ce n'est pas toujours aussi simple. Prenez par exemple les statistiques de votre pays:
Country m = new Country("Mexico");
double ratio = m.GetDebtToGDPRatio();
Votre préoccupation est qu'il pourrait y avoir n nombre de statistiques, et qu'ils ne devraient pas être dans une classe de pays. C'est vrai. Cependant, si votre modèle n'appelle que ces statistiques particulières, cet exemple de modélisation peut en fait être correct.
Dans ce cas, vous pourriez dire de façon assez logique qu'un pays devrait être en mesure de calculer ses propres statistiques, spécifiques à votre modèle et aux exigences à portée de main.
Et c'est là que réside la chose: quelles sont vos exigences? Vos exigences guideront la façon dont vous modélisez le monde, le contexte dans lequel ces exigences doivent être satisfaites.
Si vous avez en effet une multitude / nombre variable de statistiques, alors votre deuxième exemple a plus de sens:
Country m = new Country("Mexico");
DebtStatistics ds = new DebtStatistics();
double usRatio = ds.GetDebtToGDPRatio(m);
Mieux encore, ayez une superclasse abstraite ou une interface appelée Statistiques qui prend un pays comme paramètre:
interface StatisticsCalculator // or a pure abstract class if doing C++
{
double getStatistics(Country country); // or a pure virtual function if in C++
}
La classe DebtToGDPRatioStatisticsCalculator implémente StatisticsCalculator ....
classe InfantMortalityStatisticsCalculator implémente StatisticsCalculator ...
Ainsi de suite. Ce qui conduit à ce qui suit: généralisation, délégation, abstraction. La collecte statistique est déléguée à des instances spécifiques qui généralisent une abstraction spécifique (une API de collecte de statistiques).
Je ne sais pas si cela répond à 100% à votre question. Après tout, nous n'avons pas de modèles infaillibles fondés sur des lois inviolables (comme le font les gens d'EE.) Tout ce que vous pouvez faire est de mettre les choses au bon sens. Et c'est une décision d'ingénierie que vous devez prendre. La meilleure chose à faire est de vraiment se familiariser avec les principes OO (et les bons principes de modélisation logicielle en général).