Jak unikać ogólnych nazw klas abstrakcyjnych?

10

Ogólnie dobrze jest unikać słów takich jak „uchwyt” lub „proces” jako część rutynowych nazw i nazw klas, chyba że mamy do czynienia z (np.) Uchwytami plików lub (np.) Procesami unixowymi. Jednak klasy abstrakcyjne często nie wiedzą, co zamierzają z czymś zrobić, poza, powiedzmy, przetworzeniem. W mojej obecnej sytuacji mam „EmailProcessor”, który loguje się do skrzynki odbiorczej użytkownika i przetwarza z niej wiadomości. Nie jest dla mnie jasne, jak nadać temu bardziej precyzyjną nazwę, chociaż zauważyłem, że pojawia się następująca kwestia stylu:

  • lepiej traktować klasy pochodne jako klientów i nazwać klasę podstawową częścią części, którą implementuje? Daje to więcej sensu, ale będzie naruszać is-a. Np. EmailAcquirer byłby rozsądną nazwą, ponieważ nabywa dla klasy pochodnej, ale klasa pochodna nie będzie pozyskiwać dla nikogo.
  • Lub po prostu bardzo niejasne imię, ponieważ kto wie, co zrobią klasy pochodne. Jednak „Procesor” jest nadal zbyt ogólny, ponieważ wykonuje wiele istotnych operacji, takich jak logowanie i korzystanie z IMAP.

Jakiś sposób na wyjście z tego dylematu?

Problem jest bardziej widoczny w przypadku metod abstrakcyjnych, w których tak naprawdę nie można odpowiedzieć na pytanie „co to robi?” ponieważ odpowiedź brzmi: „cokolwiek klient chce”.

djechlin
źródło
Czy masz źródło (i jakiś kontekst) dla tego „ogólnego” roszczenia? Ponadto dodatkowa reguła nazewnictwa dla listy - nie trać czasu na szukanie idealnej nazwy, która prawdopodobnie nie istnieje. W razie wątpliwości nadaj mu najlepsze imię, jakie możesz, dodaj dłuższy komentarz jako komentarz, w razie potrzeby zdefiniowany, ale zawsze możesz później dokonać korekty, jeśli ktoś nie lubi twojego wyboru lub idealne imię nagle pojawia się, gdy „ robię coś innego.
Steve314,
3
@ Steve314 - tak, Steve McConnell, Code Complete, wydanie pierwsze, rozdział 4 lub 5 dotyczący procedur. Obszerna dyskusja na temat nazewnictwa w tych rozdziałach, zasadniczo podsumowująca, jeśli nie masz dobrego imienia, prawdopodobnie nie masz dobrej funkcji.
djechlin
nie znalezienie dobrego imienia może wydawać się złym znakiem, ale IMO powinieneś wiedzieć, czy funkcja jest zła na podstawie samej funkcji. Refaktoryzacja tylko dlatego, że język angielski nie został specjalnie zaprojektowany dla twojego projektu, jest nieco głupia. Nie, to nie jest codzienny problem, ale już zdecydowałeś, że masz problem.
Steve314,
1
@ Steve314 Programowanie polega wyłącznie na zarządzaniu złożonością, a jeśli twój mózg nie potrafi znaleźć słowa na coś, istnieje całkiem spora szansa, że ​​nie będzie w stanie poradzić sobie ze złożonością, gdy musi robić takie rzeczy, jak użycie go w większe zdania lub jako część większych systemów. W rzeczywistości nie jest śmiałym twierdzeniem, że niemożność znalezienia dobrego imienia jest dużą przyczyną do zaniepokojenia. Ale jest to szeroko omówione w McConnell, zaleciłbym przeczytanie tych dwóch rozdziałów przynajmniej, chociaż cała książka jest warta przeczytania od deski do deski.
djechlin
roszczenia w Code Complete niekoniecznie muszą być zaufane. Jestem świadomy reputacji książek, ale zdaję sobie z tego sprawę . Poza tym od wieków doskonale sobie radzę ze złożonością - zdarzają się błędy, bo gdy coś pójdzie nie tak, czasem problem jest tak prosty, że oczywiście nie może się mylić, ale i tak jest .
Steve314,

Odpowiedzi:

10

Problemem nie jest nazwa, ale to, że wkładasz zbyt wiele w jedną klasę.

W twojej przykładowej klasie niektóre części są bardzo konkretne (w jaki sposób będą otrzymywane maile). Inne części są bardzo abstrakcyjne (co zrobisz z mailami).

Lepiej to zrobić z oddzielnymi klasami niż z dziedziczeniem.

Sugeruję:

  • abstrakcyjny MessageIterator, podklasowany przez POPMailBoxDownloader

  • kolejna klasa OWNS POPMailBoxDownloader i robi coś z wiadomościami.

Kris Van Bael
źródło
Brzmi dokładnie dobrze. Wygląda na to, że jeśli nazywasz coś „processEvent” lub klasą „MessageProcessor”, istnieje duża szansa, że ​​mógłbyś zaprojektować kompozycję zamiast wyprowadzania. W tym przypadku jest to kompromis, ponieważ dla opiekunów elastyczność może być użyteczna, ale klient byłby zirytowany, zastanawiając się dokładnie, co robi każda funkcja (więcej niż opiekun).
djechlin
6

Jeśli nie mogę znaleźć dobrej nazwy dla klasy, piszę dokumentację kodu wbudowanego dla klasy. Opisuje cel zajęć jednym zapachem. Zwykle z tego opisu wywodzę dobre imię dla klasy. Jest to również pomocne w przypadku klas abstrakcyjnych.

Jeśli opis klasy to „Loguje się do skrzynki odbiorczej użytkownika i przetwarza wiadomości z niej”, sugerowałbym „InboxProcessor” jako nazwę klasy. Jeśli klasa pochodna ma „Loguje się do skrzynki odbiorczej użytkownika i przenosi spam e-mail do folderu ze spamem” jako opis, wybrałbym „InboxSpamMover” jako nazwę.

Nie widzę problemu podczas używania nazw ogólnych, takich jak „procesor”, jeśli odzwierciedla to ogólny cel klasy abstrakcyjnej.

Jeśli masz problemy z opisaniem celu klasy w jednym lub dwóch zapachach, możesz mieć problem z projektem. Może klasa robi za dużo i narusza zasadę pojedynczej odpowiedzialności . Dotyczy to również klas abstrakcyjnych.

Theo Lenndorff
źródło
2

Parafrazując Franka Zappę, jest tym, czym jest i należy go tak nazwać. Twój przykład po prostu nie wnika wystarczająco głęboko w to, jaki rodzaj przetwarzania się dzieje. Czy to an EmailToTroubleTicketProcessor, an EmailGrammarCorrectorczy EmailSpamDetector?

Problem jest bardziej widoczny w przypadku metod abstrakcyjnych, w których tak naprawdę nie można odpowiedzieć na pytanie „co to robi?” ponieważ odpowiedź brzmi: „cokolwiek klient chce”.

To nie do końca prawda; pytanie, na które nie można odpowiedzieć na coś abstrakcyjnego, brzmi „w jaki sposób robi to, co robi?” ponieważ jest to specyficzne dla implementacji. Jeśli EmailSenderklasa ma DeliveryStatus deliver(Email e)metodę, istnieje domniemana umowa, że ​​implementacja pobierze wiadomość e-mail, spróbuj ją dostarczyć i zwróć trochę statusu. Nie obchodzi Cię, czy łączy się on z serwerem SMTP, czy drukuje go w celu przywiązania do gołębia pocztowego, o ile implementacja spełnia obietnicę. Streszczenia jedynie określają ilościowo tę obietnicę, aby wdrożenie mogło powiedzieć „tak, robię to”.

Blrfl
źródło
Co powiesz na to, kiedy funkcja końcowa jest w pewnym sensie terminalem, więc klasa abstrakcyjna mówi: „a tutaj, zrób co chcesz?”. np. metoda nie ma dostępu do stanu, typu void return, no-throw.
djechlin
To wciąż to samo. Nawet jeśli twoja metoda jest void doWhatYouDoWith(Email e), nadal możesz wywołać klasę an EmailDisposer, która jest wystarczająco szczegółowa, aby powiedzieć, co robi, ale wystarczająco ogólna, jak to zależy od implementacji. Chociaż myślę, że @KrisVanBael go przybił: jeśli uciekłeś się do czegoś tak niejednoznacznego, może być zbyt wiele pod jednym dachem.
Blrfl
0

Zastanów się, dlaczego tak źle jest używać takich słów. Czy są opisowe? Jakie są słowa, aby opisać klasy? EmailBaseClass? Czy może to być bardziej opisowe niż powiedzenie EmailManager lub coś podobnego? Kluczem jest wgląd w daną klasę, więc znajdź odpowiednie czasowniki lub rzeczowniki. Traktuj kod jak poezję.

zxcdw
źródło
„EmailBaseClass” byłoby jak nazywanie int age„ageInteger”, tylko gorzej, ponieważ spodziewałbym się z niego czerpać zwykłe wiadomości tekstowe, wiadomości MIME itp. W abstractpierwszej kolejności nie trzeba opisywać tego, co poprzedza słowo (jest to Java). Czy masz jakiś szczególny wgląd w podstawową część tej klasy? I więcej wglądu nadal nie rozwiązuje problemu nazwania czegoś, co będzie krzyżować związek, mogę mieć coś zbyt niejasnego lub zbyt ogólnego, nie widzę wyjścia z tego, chyba że miałbym więcej wglądu w posiadanie większego wglądu.
djechlin
@djechlin - zdarzają się rzadkie przypadki, w których ageIntegercoś takiego jest właściwe. Notacja węgierska to skrócona wersja z kontekstu, w którym wiele się wydarzyło. Oczywiście, są chwile, kiedy może trzeba coś wzdłuż linii ageNumerici ageStringw takim samym zakresie, wraz ze wskazaniem rodzaju w nazwie będącej najprostszy i najczystszy sposób dwuznaczności.
Steve314,
@djechlin Osobiście uważam, że najłatwiej jest pracować z kodem, który mogę po prostu zajrzeć, aby uzyskać zrozumienie. To znaczy, imiona mówią mi, z czym pracuję. Nie trzeba wiedzieć, czy systemControllerjest to abstrakcyjna klasa bazowa czy liść w ogromnej hierarchii. Oczywiście można temu zaradzić za pomocą IDE (jak przypuszczam, że tak jest w przypadku większości programów Java?), Które mogą natychmiast poinformować użytkownika o zawartości i strukturze klasy, tak że tak naprawdę wnikliwe nazwy nie mogą być uzasadnione w podobny sposób.
zxcdw