Dokumentacja w OOP powinna unikać określania, czy „osoba pobierająca” wykonuje jakieś obliczenia?

39

Program CS mojej szkoły unika jakiejkolwiek wzmianki o programowaniu obiektowym, więc czytam sam, aby go uzupełnić - w szczególności Object Oriented Software Construction autorstwa Bertrand Meyer.

Meyer wielokrotnie podkreśla, że ​​klasy powinny ukrywać jak najwięcej informacji o ich implementacji, co ma sens. W szczególności wielokrotnie argumentuje, że atrybuty (tj. Statyczne, niepoliczone właściwości klas) i procedury (właściwości klas, które odpowiadają wywołaniom funkcji / procedur) powinny być od siebie nierozróżnialne.

Na przykład, jeśli klasa Personma atrybut age, twierdzi, że z notacji nie powinno być możliwe stwierdzenie, czy Person.ageodpowiada on wewnętrznie czemuś podobnemu, return current_year - self.birth_dateczy po prostu return self.age, gdzie self.agezostał zdefiniowany jako atrybut stały. To ma dla mnie sens. Jednak dalej twierdzi, że:

Zostanie opracowana standardowa dokumentacja klienta dla klasy, zwana krótką formą klasy, aby nie ujawniać, czy dana funkcja jest atrybutem, czy funkcją (w przypadkach, w których może to być albo).

tzn. twierdzi, że nawet dokumentacja dla klasy powinna unikać określania, czy „moduł pobierający” wykonuje jakiekolwiek obliczenia.

Nie podążam za tym. Czy dokumentacja nie jest jedynym miejscem, w którym ważne byłoby poinformowanie użytkowników o tym rozróżnieniu? Gdybym miał zaprojektować bazę danych wypełnioną Personobiektami, czy nie byłoby ważne, aby wiedzieć, czy Person.agejest to kosztowne wywołanie, więc mogłem zdecydować, czy zaimplementować dla niego jakąś pamięć podręczną? Czy źle zrozumiałem to, co mówi, czy jest to szczególnie ekstremalny przykład filozofii projektowania OOP?

Patrick Collins
źródło
1
Interesujące pytanie. Niedawno zapytałem o coś bardzo podobnego: jak zaprojektować interfejs tak, aby było jasne, które właściwości mogą zmienić ich wartość, a które pozostaną stałe? . I otrzymałem dobrą odpowiedź wskazującą na dokumentację, czyli dokładnie to, przeciwko czemu wydaje się argumentować Bertrand Meyer.
stakx
Nie przeczytałem książki. Czy Meyer podaje jakieś przykłady stylu dokumentacji, który poleca? Trudno mi wyobrazić sobie, co opisałeś, pracując dla dowolnego języka.
user16764,
1
@PatrickCollins Sugeruję przeczytanie „wykonania w królestwie rzeczowników” i zajmijcie się pojęciem czasowników i rzeczowników tutaj. Po drugie, OOP NIE JEST o programach
pobierających
@AndreasScheinert - masz na myśli to ? Zachichotałem z powodu „wszystkiego z powodu braku podkowy”, ale wydaje się, że jest to rada na temat zła programowania obiektowego.
Patrick Collins
1
@PatrickCollins tak to: steve-yegge.blogspot.com/2006/03/... ! Daje to pewne punkty do rozważenia, inne to: powinieneś zamienić swoje obiekty w struktury danych przez (ab) za pomocą setterów.
AndreasScheinert

Odpowiedzi:

58

Nie sądzę, aby punkt Meyera polegał na tym, że nie powinieneś informować użytkownika o kosztownej operacji. Jeśli twoja funkcja ma trafić do bazy danych lub poprosić serwer WWW i poświęcić kilka godzin na przetwarzanie, inny kod będzie musiał o tym wiedzieć.

Ale programista używający twojej klasy nie musi wiedzieć, czy zaimplementowałeś:

return currentAge;

lub:

return getCurrentYear() - yearBorn;

Charakterystyka wydajności między tymi dwoma podejściami jest tak minimalna, że ​​nie powinna mieć znaczenia. Koder używający twojej klasy naprawdę nie powinien dbać o to, co masz. Taki jest punkt Meyera.

Ale nie zawsze tak jest, na przykład załóżmy, że masz metodę rozmiaru na kontenerze. Można to wdrożyć:

return size;

lub

return end_pointer - start_pointer;

lub może to być:

count = 0
for(Node * node = firstNode; node; node = node->next)
{
    count++
}
return count

Różnica między dwoma pierwszymi naprawdę nie powinna mieć znaczenia. Ale ostatni może mieć poważne konsekwencje w zakresie wydajności. Właśnie dlatego STL mówi, że tak .size()jest O(1). Nie dokumentuje dokładnie, w jaki sposób obliczany jest rozmiar, ale daje mi cechy wydajności.

Więc : problemy z wydajnością dokumentów. Nie dokumentuj szczegółów implementacji. Nie obchodzi mnie, w jaki sposób std :: sort sortuje moje rzeczy, o ile robi to odpowiednio i wydajnie. Twoja klasa nie powinna także dokumentować, jak to oblicza, ale jeśli coś ma nieoczekiwany profil wydajności, udokumentuj to.

Winston Ewert
źródło
4
Ponadto: najpierw należy udokumentować złożoność czasu / przestrzeni, a następnie wyjaśnić, dlaczego funkcja ma te właściwości. Np .:// O(n) Traverses the entire user list.
Jon Purdy
2
= (Coś tak trywialnego jak Python lennie robi tego ... (W niektórych sytuacjach jest O(n)tak, jak dowiedzieliśmy się w projekcie na studiach, kiedy zasugerowałem przechowywanie długości zamiast ponownego obliczania jej przy każdej iteracji pętli)
Izkata
@Izkata, ciekawy. Czy pamiętasz, jaka była struktura O(n)?
Winston Ewert
@WinstonEwert Niestety nie. To było ponad 4 lata temu w projekcie Data Mining i zasugerowałem to mojemu przyjacielowi tylko na przeczucie, ponieważ pracowałem z C w innej klasie.
Izkata
1
@JonPurdy Dodałbym, że w normalnym kodzie biznesowym prawdopodobnie nie ma sensu określać złożoności wielkiej-O. Na przykład dostęp do bazy danych O (1) najprawdopodobniej będzie znacznie wolniejszy niż przechodzenie przez listę O (n) w pamięci, więc udokumentuj, co jest ważne. Ale są z pewnością przypadki, w których dokumentowanie złożoności jest bardzo ważne (zbiory lub inny kod obciążający algorytm).
svick,
16

Z punktu widzenia purystów akademickich lub CS, jest to oczywiście brak opisania w dokumentacji czegokolwiek na temat wewnętrznych aspektów implementacji funkcji. Jest tak, ponieważ użytkownik klasy idealnie nie powinien przyjmować żadnych założeń dotyczących wewnętrznej implementacji klasy. Jeśli implementacja ulegnie zmianie, idealnie żaden użytkownik tego nie zauważy - funkcja tworzy abstrakcję, a elementy wewnętrzne powinny być całkowicie ukryte.

Jednak większość programów w świecie rzeczywistym cierpi na „Prawo nieszczelnych abstrakcji” Joela Spolsky'ego , które mówi

„Wszystkie nietrywialne abstrakcje są do pewnego stopnia nieszczelne”.

Oznacza to, że praktycznie niemożliwe jest stworzenie pełnej czarnej skrzynki abstrakcyjnej złożonej funkcji. Typowym objawem tego są problemy z wydajnością. Dlatego w przypadku programów z prawdziwego świata może okazać się bardzo ważne, które połączenia są drogie, a które nie, a dobra dokumentacja powinna zawierać te informacje (lub powinna wskazywać, gdzie użytkownik klasy może przyjmować założenia dotyczące wydajności, a gdzie nie ).

Tak więc moja rada: dołącz informacje o potencjalnych kosztownych rozmowach, jeśli piszesz dokumenty do programu w świecie rzeczywistym, i wykluczaj je dla programu, który piszesz wyłącznie w celach edukacyjnych swojego kursu CS, biorąc pod uwagę, że należy zachować wszelkie względy dotyczące wydajności celowo poza zakresem.

Doktor Brown
źródło
+1, a większość tworzonej dokumentacji jest przeznaczona dla następnego programisty do utrzymania projektu, a nie do korzystania z niego przez kolejnego programistę .
jmoreno
12

Możesz pisać, jeśli dane połączenie jest drogie, czy nie. Lepiej skorzystaj z konwencji nazewnictwa, takiej jak getAgeszybki dostęp i / loadAgelub fetchAgekosztowne wyszukiwanie. Zdecydowanie chcesz poinformować użytkownika, czy metoda wykonuje jakieś IO.

Każdy szczegół, który podajesz w dokumentacji, jest jak umowa, którą klasa musi uszanować. Powinien informować o ważnych zachowaniach. Często zobaczysz wskazanie złożoności z dużą notacją O. Ale zwykle chcesz być krótki i do rzeczy.

Simon Bergot
źródło
1
+1 za wspomnienie, że dokumentacja jest tak samo częścią umowy klasy, jak jej interfejs.
Bart van Ingen Schenau
Popieram to. Co więcej, ogólnie starając się zminimalizować potrzebę użytkowników pobierających, zapewniając metody z zachowaniem.
sevenforce
9

Gdybym miał zaprojektować bazę danych wypełnioną obiektami Person, czy nie byłoby ważne, aby wiedzieć, czy Person.age to kosztowne połączenie?

Tak.

Dlatego czasami używam Find()funkcji, aby wskazać, że wywołanie tego może chwilę potrwać. To bardziej konwencja niż cokolwiek innego. Czas potrzebny na zwrócenie funkcji lub atrybutu nie ma znaczenia dla programu (chociaż może to być dla użytkownika), chociaż wśród programistów oczekuje się, że jeśli zostanie zadeklarowany jako atrybut, koszt wywołania powinien wynosić Niska.

W każdym razie w samym kodzie powinno być wystarczająco dużo informacji, aby wywnioskować, czy coś jest funkcją lub atrybutem, więc tak naprawdę nie widzę potrzeby mówienia tego w dokumentacji.

Robert Harvey
źródło
4
+1: konwencja ta jest idiomatyczna w wielu miejscach. Ponadto dokumentacja powinna być wykonana na poziomie interfejsu - w tym momencie nie wiesz, jak wdrażany jest Person.Age.
Telastyn
@Telastyn: Nigdy nie myślałem o dokumentacji w ten sposób; to znaczy, że należy to zrobić na poziomie interfejsu. Teraz wydaje się to oczywiste. +1 za ten cenny komentarz.
stakx
Bardzo podoba mi się ta odpowiedź. Doskonałym przykładem tego, co opisujesz, że wydajność nie dotyczy samego programu, byłby, gdyby Osoba była jednostką pobraną z usługi RESTful. GET jest nieodłączny, ale nie wiadomo, czy będzie to tanie czy drogie. To oczywiście niekoniecznie OOP, ale sprawa jest taka sama.
wałek klonowy
+1 za użycie Getmetod nad atrybutami w celu wskazania operacji o większej wadze. Widziałem wystarczająco dużo kodu, w którym programiści zakładają, że właściwość jest tylko akcesorium i używają jej wiele razy zamiast zapisywania wartości w zmiennej lokalnej, a tym samym wykonują bardzo złożony algorytm więcej niż raz. Jeśli nie ma konwencji, aby nie implementować takich właściwości, a dokumentacja nie wskazuje na złożoność, to życzę powodzenia każdemu, kto musi utrzymywać taką aplikację.
enzi
Skąd ta konwencja? Myśląc o Javie, spodziewałbym się, że jest odwrotnie: getmetoda jest równoważna z dostępem do atrybutu i dlatego nie jest droga.
sevenforce
3

Ważne jest, aby pamiętać, że pierwsze wydanie tej książki zostało napisane w 1988 roku, na początku OOP. Ci ludzie pracowali z bardziej czysto obiektowymi językami, które są dziś powszechnie używane. Nasze najpopularniejsze obecnie języki OO - C ++, C # i Java - mają pewne dość znaczące różnice w sposobie działania wczesnych języków, bardziej czysto OO.

W języku takim jak C ++ i Java należy rozróżnić dostęp do atrybutu i wywołanie metody. Istnieje świat różnic między instance.getter_methodi instance.getter_method(). Jeden faktycznie dostaje twoją wartość, a drugi nie.

Kiedy pracujesz z bardziej czysto językiem OO, perswazji Smalltalk lub Ruby (która wydaje się, że jest to język Eiffla użyty w tej książce), staje się ona całkowicie prawidłową radą. Te języki będą domyślnie wywoływać dla ciebie metody. Nie ma różnicy między instance.attributei instance.getter_method.

Nie przejmowałbym się tym punktem ani nie przyjmowałbym go zbyt dogmatycznie. Cel jest dobry - nie chcesz, aby użytkownicy twojej klasy martwili się o nieistotne szczegóły implementacji - ale nie przekłada się to na składnię wielu współczesnych języków.

Sean McSomething
źródło
1
Bardzo ważny punkt dotyczący rozważenia roku, w którym zgłoszono sugestię. Nit: Smalltalk i Simula pochodzą z lat 60. i 70., więc 88 nie jest „początkiem”.
luser droog
2

Jako użytkownik nie musisz wiedzieć, jak coś jest implementowane.

Jeśli wydajność stanowi problem, należy coś zrobić wewnątrz implementacji klasy, a nie wokół niej. Dlatego poprawnym działaniem jest naprawienie implementacji klasy lub zgłoszenie błędu do opiekuna.

mouviciel
źródło
3
Czy jednak zawsze tak jest, że kosztownie obliczeniowa metoda jest błędem? Jako trywialny przykład powiedzmy, że mam na myśli sumowanie długości tablicy ciągów. Wewnętrznie nie wiem, czy ciągi w moim języku są w stylu Pascala czy w stylu C. W pierwszym przypadku, ponieważ łańcuchy „znają” swoją długość, mogę oczekiwać, że moja pętla sumowania długości zajmie czas liniowy zależny od liczby łańcuchów. Powinienem również wiedzieć, że operacje zmieniające długość łańcuchów będą miały z nimi związane obciążenie, ponieważ string.lengthbędą one przeliczane za każdym razem, gdy się zmieni.
Patrick Collins,
3
W tym drugim przypadku, ponieważ łańcuch nie „zna” swojej długości, mogę oczekiwać, że moja pętla sumowania długości zajmie kwadratowy czas (zależy to zarówno od liczby łańcuchów, jak i ich długości), ale od operacji zmieniających długość łańcuchów będzie tańsze. Żadna z tych implementacji nie jest błędna i żadna nie zasługuje na zgłoszenie błędu, ale wymagają nieco innych stylów kodowania, aby uniknąć nieoczekiwanych problemów. Czy nie byłoby łatwiej, gdyby użytkownik miał co najmniej niejasne pojęcie, co się dzieje?
Patrick Collins,
Więc jeśli wiesz, że klasa string implementuje styl C, wybierzesz sposób kodowania, biorąc pod uwagę ten fakt. Ale co, jeśli następna wersja klasy ciągów implementuje nową reprezentację w stylu Foo? Czy odpowiednio zmienisz kod, czy zaakceptujesz spadek wydajności spowodowany fałszywymi założeniami w kodzie?
mouviciel
Widzę. Więc odpowiedź OO na „Jak mogę wycisnąć dodatkową wydajność z mojego kodu, polegając na konkretnej implementacji?” to „nie możesz”. A odpowiedź na „Mój kod jest wolniejszy niż się spodziewałbym, dlaczego?” to „Należy go przepisać”. Czy to mniej więcej taki pomysł?
Patrick Collins,
2
@PatrickCollins Odpowiedź OO opiera się na interfejsach, a nie implementacjach. Nie używaj interfejsu, który nie zawiera gwarancji wydajności jako części definicji interfejsu (np. Przykład C ++ 11 List.size ma gwarantowaną wartość O (1)). Nie wymaga uwzględnienia szczegółów implementacji w definicji interfejsu. Jeśli twój kod jest wolniejszy, niż byś chciał, czy jest jakaś inna odpowiedź niż będziesz musiał zmienić, aby był szybszy (po profilowaniu w celu ustalenia wąskich gardeł)?
stonemetal
2

Każda dokumentacja zorientowana na programistę, która nie informuje programistów o złożoności kosztów procedur / metod, jest wadliwa.

  • Szukamy metod wolnych od skutków ubocznych.

  • Jeśli wykonanie sposobu prowadzi złożoność czasową i / lub złożoności pamięci inne niż O(1), na kartach pamięci lub czasowo ograniczone środowiskach można uznać mieć skutki uboczne .

  • Zasada najmniejszego zaskoczenia jest naruszona, jeśli metoda robi coś zupełnie nieoczekiwanego - w tym przypadku, wyginanie pamięci lub marnowania czasu procesora.

Łowca jeleni
źródło
1

Myślę, że dobrze go zrozumiałeś, ale myślę, że masz rację. jeśli Person.agejest implementowany z kosztownymi obliczeniami, to myślę, że chciałbym zobaczyć to również w dokumentacji. Może mieć znaczenie między wielokrotnym wywoływaniem go (jeśli jest to niedroga operacja) lub wywoływaniem go raz a buforowaniem wartości (jeśli jest kosztowne). Nie wiem na pewno, ale myślę, że w tym przypadku Meyer może zgodzić się na dołączenie ostrzeżenia do dokumentacji.

Innym sposobem na poradzenie sobie z tym może być wprowadzenie nowego atrybutu, którego nazwa sugeruje, że może nastąpić długie obliczenie (np. Person.ageCalculatedFromDB), A następnie Person.agezwrócić wartość zapisaną w pamięci podręcznej w klasie, ale nie zawsze może to być właściwe i wydaje się, że jest nadmiernie skomplikowane rzeczy, moim zdaniem.

FrustratedWithFormsDesigner
źródło
3
Można również wysunąć argument, że jeśli musisz znać agea Person, powinieneś wywołać metodę, aby uzyskać ją niezależnie. Jeśli dzwoniący zaczną robić zbyt sprytne rzeczy o połowę, aby uniknąć konieczności wykonywania obliczeń, ryzykują, że ich implementacje nie będą działać poprawnie, ponieważ przekroczyli granicę urodzin. Drogie implementacje w klasie przejawiają się jako problemy z wydajnością, które można wykorzenić przez profilowanie, a ulepszenia, takie jak buforowanie, można wykonać w klasie, w której wszyscy dzwoniący zobaczą korzyści (i poprawne wyniki).
Blrfl,
1
@Blrfl: cóż, tak, buforowanie powinno odbywać się w Personklasie, ale myślę, że pytanie było zamierzone jako bardziej ogólne i to Person.agebył tylko przykład. Prawdopodobnie w niektórych przypadkach wybór dzwoniącego miałby większy sens - być może odbiorca ma dwa różne algorytmy do obliczania tej samej wartości: jeden szybki, ale niedokładny, jeden znacznie wolniejszy, ale dokładniejszy (renderowanie 3D przychodzi na myśl jako jedno miejsce gdzie może się to zdarzyć), a dokumentacja powinna o tym wspomnieć.
FrustratedWithFormsDesigner
Dwie metody, które zapewniają różne wyniki, to inny przypadek użycia niż wtedy, gdy oczekujesz tej samej odpowiedzi za każdym razem.
Blrfl,
0

Dokumentacja dla klas obiektowych często wiąże się z kompromisem między zapewnieniem opiekunom klasy elastyczności w zakresie zmiany projektu, a umożliwieniem konsumentom klasy pełnego wykorzystania jej potencjału. Jeśli niezmienne klasa ma szereg właściwości, które mają pewną dokładnie związek ze sobą (na przykład Left, RightiWidthwłaściwości prostokąta z wyrównanymi do siatki prostokątami), można zaprojektować klasę do przechowywania dowolnej kombinacji dwóch właściwości i obliczyć trzecią, lub można zaprojektować ją do przechowywania wszystkich trzech. Jeśli nic w interfejsie nie wyjaśnia, które właściwości są przechowywane, programista klasy może być w stanie zmienić projekt w przypadku, gdy byłoby to z jakiegoś powodu pomocne. Dla kontrastu, jeśli np. Dwie właściwości są ujawnione jako finalpola, a trzecia nie, wówczas przyszłe wersje klasy zawsze będą musiały używać tych samych dwóch właściwości jako „podstawy”.

Jeśli właściwości nie mają ścisłego związku (np. Ponieważ są floatlub doubleraczej nie int), może być konieczne udokumentowanie, które właściwości „definiują” wartość klasy. Na przykład, mimo że Leftplus Widthma być równy Right, matematyka zmiennoprzecinkowa jest często niedokładna. Na przykład załóżmy, że Rectanglektóry używa typu Floatprzyjmuje Lefti Widthjako parametry konstruktora są konstruowane z Leftpodanymi jako 1234567fi Widthjako 1.1f. Najlepsza floatreprezentacja sumy to 1234568.125 [która może być wyświetlana jako 1234568.13]; następny mniejszy floatto 1234568.0. Jeśli klasa faktycznie przechowuje LeftiWidth, może zgłosić wartość szerokości, tak jak została podana. Jeśli jednak konstruktor obliczane Rightw oparciu o przekazany w Lefti Width, a później obliczane Widthw oparciu Lefti Right, byłoby zgłosić szerokość jak 1.25fzamiast jak przekazany w 1.1f.

W przypadku klas, które można modyfikować, rzeczy mogą być jeszcze bardziej interesujące, ponieważ zmiana jednej z powiązanych ze sobą wartości spowoduje zmianę co najmniej jednej innej, ale nie zawsze będzie jasne, która z nich. W niektórych przypadkach może to być najlepiej, aby uniknąć konieczności metod, które „zestaw” pojedyncza nieruchomość jako taka, lecz obaj mają metody do np SetLeftAndWidthalbo SetLeftAndRight, albo wyjaśnić, jakie właściwości są określone i które zmieniają się (np MoveRightEdgeToSetWidth, ChangeWidthToSetLeftEdgelub MoveShapeToSetRightEdge) .

Czasami przydatne może być posiadanie klasy, która śledzi, które wartości właściwości zostały określone, a które obliczone na podstawie innych. Na przykład klasa „moment w czasie” może obejmować czas bezwzględny, czas lokalny i przesunięcie strefy czasowej. Podobnie jak w przypadku wielu takich typów, biorąc pod uwagę dowolne dwie informacje, można obliczyć trzeci. Wiedząc któryczęść informacji została obliczona, jednak czasami może być ważna. Załóżmy na przykład, że zdarzenie zostało zarejestrowane jako „o 17:00 UTC, strefa czasowa -5, godzina lokalna 12:00 pm”, a jeden później odkrywa, że ​​strefa czasowa powinna mieć wartość -6. Jeśli wiadomo, że UTC został zarejestrowany poza serwerem, rekord należy poprawić do „18:00 UTC, strefa czasowa -6, czas lokalny 12:00”; jeśli ktoś wprowadził czas lokalny poza zegarem, powinien to być „17:00 UTC, strefa czasowa -6, czas lokalny 11:00”. Nie wiedząc jednak, czy czas globalny czy lokalny należy uznać za „bardziej wiarygodny”, nie można jednak ustalić, którą korektę należy zastosować. Gdyby jednak zapis śledził, który czas został określony, zmiany strefy czasowej mogłyby pozostawić tę jedną w spokoju, zmieniając drugą.

supercat
źródło
0

Wszystkie te zasady ukrywania informacji w klasach mają sens, zakładając, że trzeba chronić się przed kimś spośród użytkowników klasy, który popełni błąd, tworząc zależność od wewnętrznej implementacji.

Dobrze jest wbudować taką ochronę, jeśli klasa ma taką widownię. Ale kiedy użytkownik pisze wywołanie funkcji w twojej klasie, ufa ci swoim kontem bankowym w czasie wykonywania.

Oto coś, co często widzę:

  1. Obiekty mają „zmodyfikowany” bit, mówiąc, że są w pewnym sensie nieaktualne. Jest to dość proste, ale mają obiekty podrzędne, więc łatwo jest „zmodyfikować” funkcję, która sumuje wszystkie obiekty podrzędne. Następnie, jeśli istnieje wiele warstw podległych obiektów (czasami współużytkujących ten sam obiekt więcej niż jeden raz), proste „pobranie” właściwości „zmodyfikowanej” może w końcu zająć znaczną część czasu wykonania.

  2. Kiedy obiekt jest w jakiś sposób modyfikowany, zakłada się, że inne obiekty rozrzucone po oprogramowaniu wymagają „powiadomienia”. Może się to odbywać na wielu warstwach struktury danych, okien itp. Napisanych przez różnych programistów, a czasem powtarzających się w nieskończonych rekurencjach, przed którymi należy się zabezpieczyć. Nawet jeśli wszyscy autorzy tych procedur obsługi powiadomień są dość ostrożni, aby nie marnować czasu, cała złożona interakcja może skończyć się nieprzewidzianym i boleśnie dużym ułamkiem czasu wykonania, a założenie, że jest to po prostu „konieczne”, jest beztroskie.

SO, lubię widzieć klasy, które prezentują ładny, abstrakcyjny interfejs dla świata zewnętrznego, ale lubię mieć pojęcie o tym, jak działają, choćby po to, aby zrozumieć, jaką pracę oszczędzają. Ale poza tym wydaje mi się, że „mniej znaczy więcej”. Ludzie są tak zachwyceni strukturą danych, że myślą, że im więcej, tym lepiej, a kiedy robię dostrajanie wydajności, powszechnym ogromnym powodem problemów z wydajnością jest niewolnicze przestrzeganie rozdętych struktur danych zbudowanych w sposób, w jaki ludzie są uczeni.

Więc idź.

Mike Dunlavey
źródło
0

Dodanie szczegółów implementacji, takich jak „oblicz lub nie” lub „informacje o wydajności”, utrudnia synchronizację kodu i dokumentu .

Przykład:

Jeśli masz metodę „kosztowną”, czy chcesz udokumentować „kosztowną” także dla wszystkich klas, które korzystają z tej metody? co jeśli zmienisz implementację, aby nie była już droga. Czy chcesz zaktualizować te informacje również dla wszystkich konsumentów?

Oczywiście miło jest, aby opiekun kodu pobierał wszystkie ważne informacje z dokumentacji kodu, ale nie podoba mi się dokumentacja, która twierdzi, że coś jest już nieważne (niesynchronizowane z kodem)

k3b
źródło
0

Po zaakceptowaniu odpowiedzi dochodzi do wniosku:

Więc: problemy z wydajnością dokumentów.

a samodokumentujący się kod jest uważany za lepszy niż dokumentacja , stąd nazwa metody powinna określać wszelkie nietypowe wyniki wydajności.

Więc nadal Person.agena return current_year - self.birth_dateale jeśli metoda wykorzystuje pętlę obliczyć wiek (tak):Person.calculateAge()

sevenforce
źródło