Załóżmy na przykład, że masz program do gier konsolowych, który ma wszystkie metody wejścia / wyjścia do iz konsoli. Byłoby inteligentny, aby utrzymać je wszystkie w jednej inputOutput
klasie lub przerwać je do większej liczby klas konkretnych jak startMenuIO
, inGameIO
, playerIO
, gameBoardIO
, itd. Tak, że każda klasa ma około 1-5 metodami?
Z tego samego powodu, jeśli lepiej je rozbić, czy mądrze byłoby umieścić je w IO
przestrzeni nazw, powodując, że nazwanie ich byłoby nieco bardziej szczegółowe, np .: IO.inGame
itd.?
Odpowiedzi:
Aktualizacja (podsumowanie)
Ponieważ napisałem dość pełną odpowiedź, oto, co sprowadza się do:
inGameIO
iplayerIO
klasy prawdopodobnie stanowiłyby naruszenie SRP. Prawdopodobnie oznacza to, że łączysz sposób obsługi IO z logiką aplikacji.Myślę, że patrzysz na to w niewłaściwy sposób. Oddzielasz IO w zależności od komponentów aplikacji, podczas gdy - dla mnie - bardziej sensowne jest posiadanie oddzielnych klas IO opartych na źródle i „typie” IO.
Posiadanie pewnych podstawowych / ogólnych
KeyboardIO
klasMouseIO
na początek, a następnie opartych na tym, kiedy i gdzie ich potrzebujesz, mają podklasy, które inaczej obsługują wspomniane IO.Na przykład wprowadzanie tekstu jest czymś, co prawdopodobnie chcesz obsługiwać inaczej niż sterowanie w grze. Przekonasz się, że chcesz mapować niektóre klucze w różny sposób w zależności od każdego przypadku użycia, ale to mapowanie nie jest częścią samego IO, to sposób, w jaki obsługujesz IO.
Trzymając się SRP, miałbym kilka klas, których mogę użyć do operacji na klawiaturze. W zależności od sytuacji prawdopodobnie będę chciał wchodzić w interakcje z tymi klasami w różny sposób, ale ich jedynym zadaniem jest powiedzenie mi, co robi użytkownik.
Następnie wstrzyknąłem te obiekty do obiektu obsługi, który albo odwzorowałby surowe IO na coś, z czym mogłaby współpracować moja logika aplikacji (np. Użytkownik naciska „w” , program obsługi odwzorowuje to na
MOVE_FORWARD
).Te uchwyty z kolei służą do poruszania postaci i odpowiednio rysują ekran. Rażące uproszczenie, ale istotą tego jest taka struktura:
To, co mamy teraz, to klasa odpowiedzialna za IO klawiatury w jej surowej formie. Kolejna klasa, która tłumaczy te dane na coś, co silnik gry może właściwie zrozumieć, dane te są następnie wykorzystywane do aktualizacji stanu wszystkich zaangażowanych komponentów, a na koniec osobna klasa zajmie się wyjściem na ekran.
Każda klasa ma jedno zadanie: obsługa wprowadzania z klawiatury odbywa się przez klasę, która nie wie / nie obchodzi / musi wiedzieć, co oznacza przetwarzanie, które przetwarza. Wystarczy wiedzieć, jak uzyskać dane wejściowe (buforowane, niebuforowane, ...).
Program obsługi tłumaczy to na wewnętrzną reprezentację reszty aplikacji, aby zrozumieć te informacje.
Silnik gry pobiera przetłumaczone dane i wykorzystuje je do powiadomienia wszystkich odpowiednich komponentów, że coś się dzieje. Każdy z tych elementów wykonuje tylko jedną rzecz, czy to kontrole kolizji, czy zmiany animacji postaci, to nie ma znaczenia, to zależy od każdego obiektu.
Obiekty te następnie przekazują swój stan z powrotem, a dane są przekazywane do
Game.Screen
, co w istocie jest odwrotnym modułem obsługi We / Wy. Odwzorowuje wewnętrzną reprezentację na coś, coIO.Screen
składnik może wykorzystać do wygenerowania rzeczywistych wyników.źródło
IO
i orazgame
przestrzenie nazw lub klasy z podklasami?Zasada pojedynczej odpowiedzialności może być trudna do zrozumienia. Za użyteczne uważam myślenie o tym, jak piszesz zdania. Nie próbujesz wcisnąć wielu pomysłów w jedno zdanie. Każde zdanie powinno jasno określać jeden pomysł i odkładać szczegóły. Na przykład, jeśli chcesz zdefiniować samochód, powiedziałbyś:
Następnie zdefiniowałbyś osobno takie rzeczy jak „pojazd”, „droga”, „koła” itp. Nie próbowałbyś powiedzieć:
Podobnie, powinieneś postarać się, aby twoje klasy, metody itp. Określały centralną koncepcję tak prosto, jak to możliwe, i odkładały szczegóły na inne metody i klasy. Podobnie jak przy pisaniu zdań, nie ma twardej zasady, jak duże powinny być.
źródło
Powiedziałbym, że najlepszym sposobem jest trzymanie ich w oddzielnych klasach. Małe klasy nie są złe, w rzeczywistości przez większość czasu są dobrym pomysłem.
Jeśli chodzi o konkretny przypadek, myślę, że oddzielenie może pomóc zmienić logikę jednego z tych specyficznych programów obsługi bez wpływu na inne i, jeśli to konieczne, łatwiej byłoby ci dodać nową metodę wejścia / wyjścia, jeśli do tego dojdzie.
źródło
Dyrektor ds. Jednolitej Odpowiedzialności stwierdza, że klasa powinna mieć tylko jeden powód do zmiany. Jeśli twoja klasa ma wiele powodów do zmiany, możesz podzielić ją na inne klasy i wykorzystać kompozycję, aby wyeliminować ten problem.
Aby odpowiedzieć na twoje pytanie, muszę zadać ci pytanie: czy twoja klasa ma tylko jeden powód do zmiany? Jeśli nie, nie bój się dodawać bardziej specjalistycznych klas, dopóki każda z nich nie będzie miała tylko jednego powodu do zmiany.
źródło