1) W przypadku, który podałeś trzeba rozróżnić jedną rzecz. Czym innym jest zarządzanie kilkoma klasami (wywoływanie ich metod) a czym innym implementacja tych metod. Klasa łamie zasade jednej odpowiedzialności jeśli za dużo implementuje. Nie łamie tej zasady jeśli jedynie zarządza klasami implementującymi poszczególne moduły funkcjonalności. W myśl tej zasady wyodrębnij z klasy biblioteka grupy metod i składowych odpowiedzialnych za konkretne funkcjonalności i zrób z nich oddzielne klasy. Później te klasy zawrzyj w klasie biblioteka, która będzie jedynie wywoływać ich metody. Wtedy co prawda Biblioteka odpowiada za wiele funkcjonalności, bo jest to logiczne i rozbicie jej na kilka odrębnych klas pogorszyłoby czytelność, ale biblioteka nie będzie implementować tych funkcjonalności. Zasada SRP spełniona.
Mówię o takiej sytuacji:
class Library{
void printAll(){
// dluga implementacja ladnego sformatowania
// ksiazek do stringa i ich wypisanie
}
void print(int index){
//implementacja...
}
void add(Book b) {
//implementacja
}
// i cala reszta metod
}
Taką klase powinno się zrefactorować do czegoś takiego:
class LibraryContentPrinter {
void printAll(){
//implementacja
}
void print(int index){
//implementacja
}
}
class Library{
LibraryContentPrinter printer;
void printAll(){
// delegacja
printer.printAll();
}
void print(int index){
//delegacja
printer.print(index);
}
void add(Book b) {
//implementacja
}
}
Widzisz, że klasa Biblioteka logicznie pozbyła się części implementacji zastępując ją oddelegowaniem zadania do swoich składowych.
Ten przykład nie jest najlepszy na wytlumaczenie tej zasady, ale mam nadzieje, że coś przybliżyłem. Generalnie wypisanie ksiazki czy jej stringowa reprezentacja to odpowiedzialność samej klasy Book, ale na potrzeby przytoczonego przykładu zrobiłem tak jak chciałeś.
2) Jest w solidzie taka zasada:
interface-segregation principle (ISP) states that no client should be forced to depend on methods it does not use.
która odpowiada na Twoje pytanie. Generalnie interfejsy powinne być porozdzielane tak, żeby:
- zminimalizować zależności między klasami, co daży jednego wielkiego interfejsu ze wszystkim, bo wtedy nie ma żadnych zależnośći :D
- zmaksymalizować spójność interfejsów, co daży do interfejsów z jednymi metodami, bo kilka metod musi mieć jakiś logiczny powód żeby trafić do interfesu, a co za tym idzie im jest ich wiecej tym powód jest bardziej naciągany i spójność spada.
Musisz znaleźć złoty środek. Tak, żeby nie natworzyć milona zależności pomiędzy malutkimi klasami / interfejsami z pojedyńczymi metodami, ale jednocześnie żeby interfejsy nie były za duże; żeby metody, które trafią do jednego interfejsu łączyło logicznie jak najwięcej.
Wyczucie w kwesti segregacji interfejsów przychodzi z czasem. Ciężko nauczyć się tego z teorii