Przyjmując przykłady PDF jako punkt wyjścia, spójrzmy na to.
http://en.wikipedia.org/wiki/Single_responsibility_principle
Zasada jednolitej odpowiedzialności sugeruje, że obiekt powinien mieć jeden i tylko jeden cel. Pamiętaj o tym.
http://en.wikipedia.org/wiki/Separation_of_concerns
Zasada Separation of Concerns mówi nam, że klasy nie powinny mieć nakładających się funkcji.
Kiedy spojrzysz na te dwa, sugerują, że logika powinna iść w klasie tylko wtedy, gdy ma to sens, tylko jeśli ta klasa jest za to odpowiedzialna.
Teraz w twoim przykładzie PDF pytanie brzmi: kto jest odpowiedzialny za drukowanie? Co ma sens?
Pierwszy fragment kodu:
Pdf pdf = new Pdf();
pdf.Print();
To nie jest dobre. Dokument PDF nie drukuje się sam. Drukuje go ... ta da! ... drukarka. Drugi fragment kodu jest więc znacznie lepszy:
Pdf pdf = new Pdf();
PdfPrinter printer = new PdfPrinter();
printer.Print(pdf);
To ma sens. Drukarka PDF drukuje dokument pdf. Co więcej, drukarka nie powinna być drukarką PDF ani drukarką fotograficzną. Powinna to być po prostu drukarka zdolna do drukowania przesyłanych do niej materiałów z najlepszymi z możliwych możliwości.
Pdf pdf = new Pdf();
Printer printer = new Printer();
printer.Print(pdf);
To takie proste. Umieść metody tam, gdzie mają sens. Oczywiście nie zawsze jest to takie proste. Weźmy na przykład statystyki swojego kraju:
Country m = new Country("Mexico");
double ratio = m.GetDebtToGDPRatio();
Martwisz się, że może nie być n statystyk i że nie powinny one należeć do klasy Country. To prawda. Jeśli jednak Twój model wymaga tylko tych konkretnych statystyk, ten przykład modelowania może być w porządku.
W takim przypadku można powiedzieć całkiem logicznie, że kraj powinien być w stanie obliczyć własne statystyki, specyficzne dla twojego modelu i obowiązujących wymagań.
I na tym polega rzecz: jakie są twoje wymagania? Twoje wymagania będą wpływać na sposób modelowania świata, kontekst, w którym te wymagania będą spełnione.
Jeśli rzeczywiście masz wiele / zmienną liczbę statystyk, wtedy twój drugi przykład ma większy sens:
Country m = new Country("Mexico");
DebtStatistics ds = new DebtStatistics();
double usRatio = ds.GetDebtToGDPRatio(m);
Jeszcze lepiej, dysponuj abstrakcyjną nadklasą lub interfejsem o nazwie Statystyka, które przyjmują kraj jako parametr:
interface StatisticsCalculator // or a pure abstract class if doing C++
{
double getStatistics(Country country); // or a pure virtual function if in C++
}
klasa DebtToGDPRatioStatisticsCalculator implementuje StatisticsCalculator ....
klasa InfantMortalityStatisticsCalculator implementuje StatisticsCalculator ...
I tak dalej i tak dalej. Co prowadzi do: uogólnienia, delegacji, abstrakcji. Gromadzenie danych statystycznych jest delegowane do określonych instancji, które uogólniają określoną abstrakcję (interfejs API do zbierania statystyk).
Nie wiem, czy to odpowiada 100% na twoje pytanie. W końcu nie mamy nieomylnych modeli opartych na nienaruszalnych prawach (tak jak robią to ludzie EE). Wszystko, co możesz zrobić, to położyć rzeczy, które mają sens. I to jest decyzja inżynierska, którą musisz podjąć. Najlepiej jest naprawdę zapoznać się z zasadami OO (i ogólnie dobrymi zasadami modelowania oprogramowania).