Od dłuższego czasu programuję w językach proceduralnych, a moją pierwszą reakcją na problem jest rozpoczęcie dzielenia go na zadania do wykonania zamiast rozważania różnych istniejących bytów (obiektów) i ich relacji.
Mam kurs uniwersytecki w zakresie OOP i rozumiem podstawy enkapsulacji, abstrakcji danych, polimorfizmu, modułowości i dziedziczenia.
Przeczytałem /programming/2688910/learning-to-think-in-the-object-oriented-way i /programming/1157847/learning-object-oriented-thinking , i przyjrzymy się niektórym książkom wskazanym w tych odpowiedziach.
Myślę, że kilka moich średnich i dużych projektów skorzysta na efektywnym wykorzystaniu OOP, ale jako nowicjusz chciałbym uniknąć czasochłonnych, powszechnych błędów.
W oparciu o twoje doświadczenia, jakie są te pułapki i jakie są rozsądne sposoby ich rozwiązania? Jeśli potrafisz wyjaśnić, dlaczego są to pułapki i jak twoja sugestia skutecznie rozwiązuje ten problem, będzie to mile widziane.
Zastanawiam się nad czymś w rodzaju: „Czy często stosuje się sporo metod obserwatora i modyfikatora i używa zmiennych prywatnych, czy też istnieją techniki ich konsolidacji / redukcji?”
Nie martwię się o używanie C ++ jako czystego języka OO, jeśli istnieją dobre powody, aby mieszać metody. (Przypomina powody używania GOTO, aczkolwiek oszczędnie.)
Dziękuję Ci!
źródło
Odpowiedzi:
Jedną wielką rzeczą, której się nauczyłem, jest projektowanie klas z zewnątrz. Zaprojektuj interfejs, zanim zaczniesz myśleć o implementacji. To sprawi, że klasa będzie o wiele bardziej intuicyjna dla użytkowników (osób korzystających z klasy) niż pisanie podstawowych algorytmów i budowanie klasy oraz pisanie nowych funkcji członka publicznego, jeśli będą potrzebne.
źródło
Po pierwsze, pułapką jest ujawnianie zbyt dużej ilości informacji. Domyślnie powinno być
private
, niepublic
.Potem przychodzi zbyt wielu pobierających / ustawiających. Powiedzmy, że mam członka danych. Czy naprawdę potrzebuję tych danych w innej klasie? Ok, zrób gettera. Czy naprawdę muszę naprawdę zmieniać te dane w trakcie użytkowania obiektu? Następnie zrób setera.
Większość początkujących programistów ma domyślne ustawienie getter / setter dla każdego elementu danych. To zakłóca interfejsy i często jest złym wyborem projektu.
źródło
Kiedy przekroczyłem tę przepaść, zdecydowałem się na następujące podejście:
0) Zacząłem powoli, z małymi / średnimi aplikacjami proceduralnymi i bez pracy o znaczeniu krytycznym.
1) prostym mapowaniem pierwszego przejścia z tego, jak napisałbym program od zera w stylu OO - co najważniejsze dla mnie w tym czasie, a to JEST subiektywne - miało na celu wykrycie wszystkich klas podstawowych. Moim celem było kapsułkowanie jak największej liczby klas podstawowych. Czyste metody wirtualne dla wszystkiego, co możliwe w klasach podstawowych.
2) Następnie następnym krokiem było utworzenie pochodnych.
3) ostatnim krokiem było - w oryginalnym kodzie proceduralnym oddzielić struktury danych od kodu obserwatora / modyfikatora. Następnie użyj ukrywania danych i zamapuj wszystkie wspólne dane na klasy podstawowe, aw podklasach poszły dane, które nie były wspólne w programie. I to samo traktowanie kodu obserwatora / modyfikatora proceduralnego - cała logika „wszędzie używana” trafiła do klas podstawowych. I zobacz / zmodyfikuj logikę, która działała tylko na podzbiorze danych, trafiła do klas pochodnych.
Jest to subiektywne, ale jest SZYBKIE, jeśli dobrze znasz kod proceduralny i struktury danych. Błąd w przeglądzie kodu występuje wtedy, gdy odrobina danych lub logiki nie pojawia się w klasach podstawowych, ale jest używana wszędzie.
źródło
Spójrz na inne udane projekty, które wykorzystują OOP, aby uzyskać poczucie dobrego stylu. Polecam przyjrzeć się projektowi Qt , do którego zawsze podchodzę, podejmując własne decyzje projektowe.
źródło
W zależności od tego, z kim rozmawiasz, wszystkie dane w OOP powinny być prywatne lub chronione i dostępne tylko za pośrednictwem akcesoriów i mutatorów. Ogólnie uważam, że jest to dobra praktyka, ale zdarzają się sytuacje, w których odstępuję od tej normy. Na przykład, jeśli masz klasę (powiedzmy w Javie), której jedynym celem jest powiązanie niektórych danych w jednostkę logiczną, warto pozostawić pola publiczne. Jeśli jest to niezmienna kapsuła, po prostu zaznacz je jako ostateczne i zainicjuj je w konstruktorze. Sprowadza to klasę (w tym przypadku) do niewiele więcej niż struktury (w rzeczywistości przy użyciu C ++ powinieneś nazwać ją strukturą. Działa tak jak klasa, ale domyślna widoczność jest publiczna, a twoja intencja jest wyraźniejsza), ale Myślę, że przekonasz się, że korzystanie z niego jest w tych przypadkach znacznie wygodniejsze.
Jedną rzeczą, której zdecydowanie nie chcesz robić, jest posiadanie pola, które musi być sprawdzone pod kątem spójności w mutatorze i pozostawienie go publicznego.
źródło
struct
s - nie ma to znaczenia semantycznego, ale wyjaśnia intencję i sprawia, że ich użycie wydaje się bardziej naturalne, szczególnie dla programistów C.Książka Kenta Becka Implementation Patterns stanowi doskonałą podstawę do używania, a nie niewłaściwego używania mechanizmów obiektowych.
źródło
Nie martwię się o używanie C ++ jako czystego języka OO, jeśli istnieją dobre powody, aby mieszać metody. (Przypomina powody używania GOTO, aczkolwiek oszczędnie.)
Tak naprawdę nie sądziłem, że mam dużo do zaoferowania, dopóki nie zobaczyłem tego fragmentu. Muszę się nie zgodzić z tym sentymentem. OOP jest tylko jednym z paradygmatów, które mogą i powinny być używane w C ++. Szczerze mówiąc, moim zdaniem nie jest to jedna z jego najsilniejszych cech.
Z punktu widzenia OO wydaje mi się, że C ++ jest trochę za krótki. Na przykład pomysł posiadania funkcji nie-wirtualnych jest pod tym względem kleszczem. Kłóciłem się z tymi, którzy się ze mną nie zgadzają, ale nie-wirtualni członkowie po prostu nie pasują do tego paradygmatu. Polimorfizm jest kluczowym składnikiem OO, a klasy z funkcjami innymi niż wirtualne nie są polimorficzne w sensie OO. Więc jako język OO uważam, że C ++ jest raczej słaby w porównaniu do języków takich jak Java czy Objective-C.
Z drugiej strony programowanie ogólne, C ++ ma to całkiem dobre. Słyszałem, że są też lepsze języki do tego, ale połączenie obiektów i funkcji ogólnych jest dość potężne i wyraziste. Co więcej, może być cholernie szybki zarówno w czasie programowania ORAZ czasu przetwarzania. Wydaje mi się, że to właśnie w tym obszarze C ++ świeci, choć trzeba przyznać, że mogłoby być lepiej (na przykład obsługa języka dla koncepcji). Ktoś, kto myśli, że powinien trzymać się paradygmatu OO i traktować innych zgodnie z instrukcją goto na poziomach niemoralności, tak naprawdę nie dostrzega tego paradygmatu.
Imponująca jest także zdolność metaprogramowania szablonów. Sprawdź na przykład bibliotekę Boost.Units. Ta biblioteka zapewnia obsługę typów dla wielkości wymiarowych. Szeroko wykorzystałem tę bibliotekę w firmie inżynierskiej, w której obecnie pracuję. Zapewnia tylko o wiele bardziej natychmiastową informację zwrotną dla jednego aspektu możliwego programisty, a nawet błędu specyfikacji. Niemożliwe jest skompilowanie programu, który używa formuły, w której obie strony operatora „=” nie są równoważne wymiarowo bez jawnego rzutowania. Osobiście nie mam doświadczenia z żadnym innym językiem, w którym jest to możliwe, a na pewno nie z takim, który ma moc i szybkość C ++.
Metaprogramowanie jest paradygmatem czysto funkcjonalnym.
Tak naprawdę myślę, że już wkraczasz do C ++ z kilkoma niefortunnymi błędami. Innych paradygmatów oprócz OO nie należy unikać, należy je ZWOLNIĆ. Użyj paradygmatu, który jest naturalny dla aspektu problemu, nad którym pracujesz. Nie zmuszaj obiektów do tego, co zasadniczo nie jest problemem podatnym na obiekty. Jeśli o mnie chodzi, OO nie jest nawet połową historii C ++.
źródło
Chciałem zaakceptować odpowiedź na to pytanie, ale nie mogłem zdecydować się na jedną odpowiedź, aby nadać znacznik wyboru. W związku z tym głosowałem za oryginalnymi autorami i stworzyłem to jako odpowiedź podsumowującą. Dzięki wszystkim, którzy poświęcili kilka minut, odkryłem, że wgląd, który dostarczyłeś, dał mi dobry kierunek i zapewnił mnie, że nie jestem poza torami.
@nightcracker
Czułem, że w przeszłości obserwowałem ten problem w działaniu. Wasze komentarze przypomniały mi również, że ukrywając zmienne leżące u ich podstaw i ich implementację, mogę zmienić ich implementację, nie niszcząc niczego od nich zależnego.
Dominic Gurto
Myślałem, że komentarz Dominika był świetnym ideałem, do którego można dążyć, ale myślę, że komentarz Gene'a naprawdę pasuje do rzeczywistości. Do tej pory widziałem to w akcji ... i czuję się trochę lepiej, że nie jest to rzadkie. Myślę, że gdy dojrzewam jako programista, skłaniam się ku bardziej kompletnym projektom, ale teraz wciąż cierpię z powodu skoku i otrzymywania kodu.
wantTheBest
To ma sens ... Podobał mi się pomysł utrzymywania pracy w pracy, ale refaktoryzacja niektórych niekrytycznych rzeczy za pomocą klas.
jpm
Od dłuższego czasu wiedziałem, że jest to jedna z zalet enkapsulacji danych ... możliwość wymuszenia spójności, a co za tym idzie, warunków / zakresów itp.
Szalony Eddie
Myślę, że początkowo bardzo brakowało mi odpowiedzi Szalonego Eddiego, ponieważ nie przeczytałem niektórych z wymienionych tematów ... takich jak metaprogramowanie. Myślę, że świetnym przesłaniem w poście CE było to, że C ++ jest tak mieszanką możliwości i stylów, że każdy z nich powinien być wykorzystany do ich najlepszego potencjału ... łącznie z koniecznością, jeśli to ma sens.
Jeszcze raz dziękuję wszystkim, którzy odpowiedzieli!
źródło
Największą pułapką jest przekonanie, że OOP to srebrna kula lub „jeden idealny paradygmat”.
źródło