Czy obiekt musi reprezentować byt?
Przez podmiot mam na myśli coś takiego jak Product
, Motor
, A ParkingLot
itd, fizyczne, a nawet jednoznaczne niefizyczne koncepcyjne przedmiot - coś, co jest dobrze zdefiniowany, a niektóre podstawowe dane wyraźnie należący do obiektu, a niektóre funkcje / metody które wyraźnie działają na podstawowych danych.
Na przykład, mogę mieć obiekt Demon
, byt sam w sobie, być może wyimaginowany i nie fizyczny, ale byt jednak
Czy obiekt może być tylko zbiorem metod , wspólnym zestawem procedur, które wiążą się ze wspólnym celem?
Przykład: można wywołać klasę MotorOperations
lub MotorActions
, gdy nie ma encji, ale metody wewnątrz klasy robią takie rzeczy
- getMotorDataFromHTMLForm ()
- getMotorManufacts ()
- selectMotorFromUserRequirements (wymagania $)
- canMotorCanHandleOperatingConditions (warunki $)
- computePowerConsumptionForMotor ($ id)
Klasa jest zazwyczaj definiowana jako centralna dla obiektu + operacje na danych. Na przykład Motor
mogą istnieć pewne zmienne motoryczne dotyczące specyfikacji silnika i mogą istnieć operacje, które łączą te dane, aby coś wytworzyć.
W moim przypadku bardziej przypomina to, że mam klasę z operacjami na danych + dane przekazywane przez klasę, nie ma danych skoncentrowanych na „operacjach motorycznych”, innych niż tymczasowe przekazywanie danych przez klasę.
Pytanie
Czy klasy mogą reprezentować obiekty bez bytów? Jeśli nie, dlaczego są złe / niepełne / nie koncentrują się na OOP? Czy istnieją sposoby, które należy zmienić / ulepszyć koncepcyjnie, aby były zgodne z OOP?
MotorActions
ani nieMotorOperations
są dobre pomysły, czy nie.Odpowiedzi:
Nie , obiekt nie musi reprezentować bytu.
W rzeczywistości argumentowałbym, że kiedy przestajesz myśleć o przedmiotach jako o fizycznych bytach, to w końcu dostajesz korzyści, które obiecuje OOP.
To nie jest najlepszy przykład, ale projekt ekspresu do kawy jest prawdopodobnie miejscem, w którym zaczęło mi się świecić światło.
Obiekty dotyczą wiadomości. Chodzi o obowiązki. Nie dotyczą samochodów, użytkowników ani zamówień.
Wiem, że uczymy OO w ten sposób, ale po kilku próbach staje się oczywiste, jak frustrujące jest zastanawianie się, dokąd zmierzają rzeczy, gdy próbujesz robić MVC, MVVM lub MVW cokolwiek. Albo twoje modele staną się śmiesznie rozdęte, albo zrobią to twoje kontrolery. Jeśli chodzi o nawigację, dobrze jest wiedzieć, że wszystko, co dotyczy pojazdów, znajduje się w pliku Vehicle.ext, ale gdy twoja aplikacja dotyczy pojazdów, nieuchronnie kończy się na 3000 linii spaghetti w tym pliku.
Kiedy masz nową wiadomość do wysłania, masz co najmniej jeden nowy obiekt i być może ich parę. Więc w twoim pytaniu o pakiet metod argumentowałbym, że potencjalnie mówisz o pakiecie wiadomości. I każdy z nich może być swoim własnym przedmiotem, z własną pracą do wykonania. I to jest w porządku. Stanie się oczywiste, gdy rozdzielisz różne rzeczy, które naprawdę naprawdę muszą być razem. I złożyłeś je razem. Ale nie od razu umieszczasz każdą metodę w niejasnej szufladzie dla wygody, jeśli chcesz cieszyć się OO.
Porozmawiajmy o workach funkcji
Obiekt może być tylko zbiorem metod i nadal być OO, ale moje „reguły” są dość surowe.
Kolekcja powinna ponosić jedną odpowiedzialność, a odpowiedzialność ta nie może być tak ogólna, jak „Robi rzeczy dla silników”. Mogę zrobić coś takiego jak fasada warstwy usługowej, ale jestem świadomy, że jestem leniwy z powodów związanych z nawigacją / odkryciem, a nie dlatego, że próbuję napisać kod OO.
Wszystkie metody powinny mieć spójną warstwę abstrakcji. Jeśli jedna metoda pobiera obiekty Motorowe, a druga zwraca Moc, to prawdopodobnie jest to zbyt daleko od siebie.
Obiekt powinien działać na tym samym „rodzaju” danych. Ten obiekt robi rzeczy dla silników (start / stop), ten robi rzeczy z długością korby, ten obsługuje sekwencjonowanie zapłonu, ten ma postać HTML. Dane te mogą być polami na obiekcie i wydają się spójne.
Generalnie buduję tego typu obiekty, kiedy wykonuję transformacje, kompozycje lub po prostu nie chcę się martwić o zmienność.
Uważam, że skupienie się na odpowiedzialności za przedmiot prowadzi mnie do spójności. Aby być przedmiotem, musi istnieć pewna spójność, ale nie musi być żadnych pól ani bardzo dużego zachowania, aby mógł on być przedmiotem. Gdybym budował system, który potrzebowałby 5 metod motorycznych, zaczynałbym od 5 różnych obiektów, które robią te rzeczy. Gdy znalazłem podobieństwo, zacząłem łączyć rzeczy ze sobą lub używać wspólnych obiektów „pomocniczych”. To porusza mnie do otwartych / zamkniętych problemów - jak mogę wyodrębnić ten kawałek funkcjonalności, aby nigdy więcej nie musiałem modyfikować tego konkretnego pliku, ale nadal go używać w razie potrzeby?
Obiekty dotyczą wiadomości
Pola prawie nie mają znaczenia dla obiektu - pobieranie i ustawianie rejestrów nie zmienia świata poza programem. Współpraca z innymi obiektami kończy pracę. Jednak siłą OO jest to, że możemy tworzyć abstrakcje, abyśmy nie musieli myśleć o wszystkich pojedynczych szczegółach jednocześnie. Abstrakcje, które przeciekają lub nie mają sensu, są problematyczne, dlatego głęboko (prawdopodobnie za dużo) myślimy o tworzeniu obiektów pasujących do naszych modeli mentalnych.
Kluczowe pytanie: dlaczego te dwa obiekty muszą ze sobą rozmawiać?
Pomyśl o obiekcie jako o organie w człowieku - ma on domyślny cel i zmienia zachowanie tylko wtedy, gdy otrzyma określony komunikat, na którym mu zależy.
Wyobraź sobie scenariusz, w którym jesteś na przejściu dla pieszych i samochód jedzie szybko. Jako obiekt mózgu wykrywam stresor. Mówię podwzgórzowi, aby wysłał hormon uwalniający kortykotrofinę. Przysadka mózgowa otrzymuje tę wiadomość i uwalnia hormon kortykotroficzny nadnerczy. Nadnercza otrzymują tę wiadomość i wytwarzają adrenalinę. Kiedy obiekt mięśniowy otrzymuje ten komunikat adrenaliny, kurczy się. Kiedy serce otrzymuje tę samą wiadomość, bije szybciej. Cały łańcuch graczy jest zaangażowany w rozpoczęcie złożonego zachowania podczas sprintu po drugiej stronie ulicy i ważne są przesłania. Obiekt mózgu wie, jak doprowadzić podwzgórze do wysłania ostrzeżenia, ale nie zna łańcucha obiektów, które ostatecznie spowodują zachowanie. Podobnie serce nie ma pojęcia, skąd pochodzi adrenalina,
Zatem w tym ( uproszczonym ) przykładzie obiekt nadnerczy musi tylko wiedzieć, jak przyjmować ACTH i wytwarzać adrenalinę. Nie potrzebuje do tego żadnych pól, ale nadal wydaje mi się, że to jakiś przedmiot.
Teraz, jeśli nasza aplikacja jest przeznaczona tylko do sprintu po drugiej stronie ulicy, może nie potrzebuję przysadki mózgowej i obiektów nadnerczy. Albo potrzebuję tylko obiektu przysadki mózgowej, który wykonuje tylko niewielką część tego, co możemy koncepcyjnie postrzegać jako „model przysadki mózgowej”. Wszystkie te koncepcje istnieją jako byty koncepcyjne, ale jest to oprogramowanie i możemy zrobić AdrenalineSender lub MuscleContractor lub cokolwiek innego i nie martwić się zbytnio o „niekompletność” naszego modelu.
źródło
MotorOperations
lubMotorActions
”. Z tego jestem pewien, że zarówno oryginalne, jak i końcowe projekty klas z przykładu CoffeeMaker mieszczą się w definicji OP „reprezentującej byt”Krótko mówiąc, możesz zrobić wszystko, ale ten konkretny scenariusz byłby niezgodny z zasadami OOP :)
To, co opisujesz, nazywane jest czasem klasą „użytkową” - zwykle jest to znak zapachu kodu. Chcesz uniknąć tworzenia klas w celu utrzymania niektórych metod razem.
Nie każdy obiekt w twojej aplikacji musi być powiązany z prawdziwym bytem, takim jak silnik lub samochód; są to obiekty biznesowe. W swojej aplikacji prawdopodobnie będziesz używać wielu swoich obiektów biznesowych, ale będziesz potrzebować nieco większej segregacji w przypadku innych operacji, które nie są częścią podmiotów biznesowych, takich jak te wymienione przez Ciebie.
Powinieneś rzucić okiem na typowe wzory architektoniczne - jednym z nich jest MVC (Model-View-Controller) .
Gdybym miał rozpowszechniać metody wymienione za pomocą MVC, oto co bym zrobił:
Wyświetl klasę:
Klasa modelu (silnik zasadniczo):
Klasa kontrolera (MotorsController):
Wygląda na to, że używasz PHP; dobrym frameworkiem MVC do obejrzenia jest Laravel . W swoich przykładach dobrze ilustrują, do jakich metod należą.
Innym, bardziej ogólnym źródłem informacji o tym, jak zdecydować, gdzie należą metody, są zasady GRASP i SOLID .
źródło
MotorOperations
, abyMotorsController
i oczyścić go trochę, mój zbiór funkcji będą zgodne z OOP?Utils
klasa byłaby przykładem zapachu kodu? Ciekawe, ponieważ podobało mi się to, że losowe funkcje są zamknięte wUtils
klasie, a nie tylko samodzielne funkcje ... z mojego doświadczenia wynika, że kod jest nieco bardziej czytelny, ale moje doświadczenie jest ograniczone.Mogą? Tak. Powinien? Prawdopodobnie nie - a przynajmniej nie tak, jak frazujesz rzeczy.
Obiekty faktycznie są najlepsze, gdy nie reprezentują bezpośrednio obiektu fizycznego, ponieważ rzeczywistość tak rzadko ładnie odwzorowuje kod. Ale muszą reprezentować spójną koncepcję lub obiekt kodowy. Muszą reprezentować jedną spójną odpowiedzialność.
Samo wiązanie kilku stycznie powiązanych funkcji razem to przestrzeń nazw lub moduł - a nie obiekt w języku OOP. Mogą być przydatne, ale nie są takie same i wymagają innego sposobu użycia / myślenia, aby efektywnie z nimi pracować.
źródło
bundling a bunch of tangentially related functions together is a namespace
: to bardziej przypomina paradygmat proceduralny niż obiektowy.Klasa powinna coś wymodelować - w przeciwnym razie jest to bezcelowe. To, co jest modelowane, może nie być fizyczną „rzeczą”; zamiast tego może to być przedstawienie czegoś, co nie jest „rzeczywiste”, ale które jest potrzebne do kontrolowania modelowanego systemu. Na przykład w systemie sterowania sygnalizacją świetlną można bardzo dobrze mieć jakąś klasę ControlSignal, reprezentującą sygnał wysyłany z jednej części systemu do drugiej, wskazujący, że należy wykonać pewne działanie. W „świecie rzeczywistym” sygnały te mogą składać się z impulsów elektrycznych przekazywanych przez przewody z jednej skrzynki do drugiej. Proces modelowania czegoś, co nie jest „rzeczywiste” jako konkretny obiekt, nazywa się „reifikacją”.
Powodzenia.
źródło
Nie. (Zredagowano tytuł, aby zadać przeciwne pytanie!)
na przykład:
lub
Jednak ogólnie rzecz biorąc, ideałem OOP jest odejście
do
źródło
Myślę, że wizualizacja czegoś w prawdziwym świecie jest pomocna podczas kodowania klas, ale nie jest konieczna.
W rzeczywistości, w zależności od tego, jak dosłownie chcesz być, wiele obiektów, z którymi pracujemy, w ogóle nie ma fizycznych reprezentacji. Na przykład - rozważ architekturę MVC. W prawdziwym świecie nie ma modelu ani kontrolera, mimo że generalnie są reprezentowane przez klasy. Te trzy razem często reprezentują coś, ale nie zawsze - ale, jak powiedziałem, prawdopodobnie możesz wymusić dopasowanie do czegoś, jeśli naprawdę chcesz (i wyobrażenie sobie, że COŚ pomaga! Wizualizuję większość obiektów w jakiś sposób - po prostu nic widziałbyś kiedykolwiek w prawdziwym świecie).
Przeważnie te „Zasady”, których dowiesz się o OO, są tylko wskazówkami mającymi na celu nakierowanie Cię na myślenie w OO niekoniecznie o rzeczy, o które martwisz się, gdy używasz go do codziennego pisania kodu.
Nawiasem mówiąc, OO sam w sobie nie jest jakimś panaceum i nie pomaga ci wcale podczas pierwszego przejścia do kodowania jakiegoś rozwiązania, ale myślę, że jeśli zrobione dobrze, to naprawdę fantastyczny sposób na uporządkowanie kodu, aby mógł być bardziej łatwo zrozumiałe później. Pisanie łatwego do utrzymania kodu jest prawdopodobnie jednym z najtrudniejszych zadań w rozwoju oprogramowania iw wielu przypadkach (wszystko, co wymaga ciągłego debugowania / konserwacji) jest zdecydowanie najważniejsze.
źródło
Pytanie: Który podmiot
Logger
reprezentuje?Nie metaforycznie - dosłownie.
Wyjaśniając studentom OOP, pomocne jest wyjaśnienie obiektów o analogiach do świata fizycznego.
Alan Kay - jeden z ojców OOP - napisał:
źródło
Z tego obiekty najlepiej rozumieć jako
obiekty autarkiczne enkapsulujące / ukrywające dane
komunikowanie się z innymi obiektami za pomocą wiadomości
Zdecydowanie: Pomyśl o tym
Math
Jeśli chodzi o twój przykład:
Aby zdecydować, gdzie umieścić te metody, musisz spojrzeć na
responsibilities
: kto jest odpowiedzialny za co .Kontekstem takiej metody byłoby zbudowanie obiektu motorycznego . Silnik nie jest odpowiedzialny za tworzenie się na podstawie danych pobranych za pośrednictwem formularza . W grę wchodzą co najmniej dwa przedmioty; jeden jest silnikiem, a drugi twórcą silnika .
Typowy kontekst, w którym można by napisać taki sposób jest
service
.Byłoby to również użyte w
service
kontekścieTo jest pytanie do
motor
obiektuTo też pytanie najlepiej zadać sam silnik.
tl; dr
Metafora
objects
jak fizycznie obiektów jest bardziej irytujące niż pomocne.źródło
thing
z danymi, w których dane wyraźnie należą do rzeczy i funkcje, które wyraźnie działają na danych, które należą do rzeczy”. Logger w tym przypadku, choć nie jest rzeczą fizyczną, jest pojęciem podobnym do „fizycznego dziennika”. Czy posiada dane, które są rdzeniem do jej stanu, a nie jest tylko przejściowy do niego, że może polegać na realizacji i interpretacji .. i jest bardziej więc moje pytanie, gdzie się dzieje ..Tak, możesz to zrobić. Ale zasadniczo tworzysz klasę, która zapewnia taką samą funkcjonalność jak biblioteka proceduralna. Jedynym powodem jest to, że w twoim języku brakuje modułów proceduralnych (np. Java lub Ruby).
W języku z modułami proceduralnymi (myślę, że PHP ma moduły proceduralne), możesz rozważyć użycie modułu proceduralnego.
Możesz również rozważyć przeprojektowanie swojej klasy, aby instancja klasy reprezentowała spójny obiekt.
Ale używaj notacji OO tylko wtedy, gdy daje ci ona dodatkową moc, nie tylko ze względu na bycie OO!
źródło
Słowami laika
Takie klasy istnieją, na przykład Java SDK ma klasę Collections, która składa się wyłącznie z metod statycznych, które działają na kolekcjach lub zwracają kolekcje.
Odpowiedź brzmiałaby: nie, nie wszystkie klasy muszą być modelowane z encji domeny.
Z drugiej strony, takich klas jest tylko kilka lub powinno być tylko kilka w programie wykonanym w języku OOP, ponieważ prawdziwa moc OOP polega na polimorfizmie i enkapsulacji.
To tak, jakbyś podróżował samolotem z miejsca na miejsce, nie latając, lecz jeżdżąc autostradą. Samoloty mają koła jak samochody, ale są przeznaczone do latania.
źródło
Nie, klasa reprezentuje jedynie coś, co można utworzyć, ma członków i może być dziedziczone.
W rzeczywistości Twój język może mieć klasy statyczne, które nie są tworzone nawet kilka razy.
.NET
Math
to świetny przykład „profesjonalnej” klasy, która nie reprezentuje żadnego bytu (chyba że chcesz filozoficznie zrozumieć, czym jest matematyka ...), a także ilustruje uzasadniony przypadek użycia takiej klasy. Ma tylko metody i 2 pola (oba reprezentują indywidualne stałe).Hierarchia klas podobnej biblioteki
Math.Net
grupuje metody matematyczne / numeryczne w klasy według typu. Tutaj klasy reprezentują nie byt, ale logiczną kategorię.Sam często kończę tworzenie (statycznych) klas, które są niczym więcej niż zbiorem powiązanych (statycznych) funkcji lub grupą stałych wygodnie pogrupowanych w jednym centralnym miejscu. Nie twierdziłbym, że to dobry projekt, ale czasem jest to najprostsze rozwiązanie.
źródło
Odpowiedzi na twoje pytania ostatecznie zależą od tego, jak dokładnie zdefiniowane są terminy takie jak „klasa” i „byt”. Wykonałeś dobrą robotę, definiując to drugie, dopuszczając zarówno fizyczne, jak i koncepcyjne byty. Ale termin „klasa” dla purystów zawsze będzie „fabryką do tworzenia instancji obiektów o szczególnej wewnętrznej reprezentacji”, a dla pragmatyków termin ten będzie obejmował rzeczy Java lub C ++ pogardzane przez purystów. To samo dotyczy terminu „OOP”, co pragmatycy mogliby powiedzieć, że robią to Java i C ++, ale który purysta miałby znaczyć to, co Alan Kay miał na myśli, gdy zauważył [„Wynalazłem termin zorientowany obiektowo i mogę powiedzieć, że nie mieć na myśli C ++ ”] ( Co więc * naprawdę * Alan Kay miał na myśli, mówiąc„ obiektowo ”? ).
Jeśli przyjrzysz się pragmatystycznemu poglądowi, możesz zaakceptować klasy narzędziowe bez instancji. Ale purystyczny pogląd jest bardziej interesujący. OOP, według Kay, zawsze bardziej dotyczyła wiadomości niż klas. Więc na twoje pytania:
Nie. Klasy klasyfikują obiekty. Klasy są z definicji jednostkami, do których wysyłasz wiadomość, na przykład w
new
celu wytworzenia obiektu. Obiekt jest bytem wykonanym z klasy. Istnieją klasy służące do wytwarzania i klasyfikacji przedmiotów. Tak robią klasy. Klasy tworzą przedmioty. Używamy klas do klasyfikowania obiektów. Więc jeśli C jest tylko pakietem metod, które nie pozwalają na tworzenie instancji lub podklasowanie, nie może być żadnych obiektów sklasyfikowanych przez C, więc C nie jest klasą.Nie są źli. Tylko nie nazywaj ich klasami. Pakiet metod to OOPable: coś, do czego można wysłać wiadomość w celu wywołania metody, ale nie jest to klasa, chyba że może utworzyć instancję obiektu. Czy znasz Ruby? W Rubim moduł jest pakietem metod. Klasa jest modułem, który może wystąpień obiektów. W module nie ma nic złego . Ale rzeczą, która odróżnia klasę od modułu (w kategoriach czystego OOP) jest to, że klasa może mieć instancje. Moduł to tylko niektóre metody. Pomyłką jest to, że C ++, Java i inne języki używają terminu „klasa” zarówno dla klasy, jak i modułu .
Teraz, aby spojrzeć na jeden ze swoich konkretnych przykładów, pytasz o
MotorOperations
klasę, do której możesz wysłać wiadomośćcomputePowerConsumptionForMotor
z argumentemid
. Czy to złe? Przynajmniej nie narusza to zasady Tell Don't Ask . Czy możemy mieć do klasy silnik i wysłać mupowerConsumption
wiadomość? Może nie w 100% przypadków, ale może próba zrobienia takich rzeczy doprowadzi do ciekawych informacji na temat twojego kodu. Lub może prowadzić do eksplozji małych klas, co byłoby złe.Jeśli „wykonywanie OOP” jest dla Ciebie ważne, powinieneś poszukać sposobów na architekturę systemu w komponenty, które komunikują się, wysyłając do siebie wiadomości. To jest właśnie to, co OOP jest , albo przynajmniej co fałszerz pieniędzy terminu miał na myśli. Klasy narzędzi nie są OOP w tym widoku. Ale dla niektórych mogą być.
Próba stworzenia całego dużego systemu z milionów żywych, oddychających obiektów, z których wszystkie powstały w wyniku zajęć, może działać w przypadku niektórych projektów. Ale jeśli odkryjesz, że tworzysz sztuczne, źle brzmiące klasy, należy wprowadzić moduły. Staraj się podkreślać klasy i używaj modułów, gdy możliwe do odtworzenia klasy byłyby śmieszne.
TL; DR - pakiety metod nie zawsze są złe. Najpierw spróbuj użyć klas, które można utworzyć. Wróć do „modułów” tylko w razie potrzeby.
źródło
Biorąc podane przykłady:
Wyglądają jak metody „fabryczne”, które zwracałyby
Motor
obiekt. Drugi oznacza, że albo tworzysz nowy obiekt w celu spełnienia wymagań, albo masz bazę danych lub kolekcję silników w pamięci, które sprawdzasz. W takim przypadku powinna to być metoda. Lub może to być metoda obiektu wymagań.Skąd je bierzesz? To właśnie powinna być metoda.
Wyglądają, jakby to były metody
Motor
.źródło
getMotorManufacturers
jest zwrócenie wszystkich producentów silników z bazy danych. Nie jestem pewien, dokąd to idzie. Zajmie mi trochę czasu, aby rozwikłać ten BOPUM, aby umieścić rzeczy tam, gdzie muszą iść ...getMotorManufacturers
z bazy danych. Baza danych nie może mieć metody ... Obecnie mam wzorzec DataMapper, który wysyła zapytanie do bazy danych, inicjuje tablicę i zapełnia ją obiektami motorycznymi (zawierającymi również informacje o producencie), a następnie zwraca tę tablicę wywołującemu. Dzwoniącym jest mojaMotorOperations
klasa, która z kolei jest wywoływana zProductSelection
klasy (która zawiera algorytm używany do wybierania Silników, a załadowane informacje o silniku to dane dla tego algorytmu)Obiekt to zbiór metod i danych o wysokiej spójności. Należą do klasy, ponieważ są ze sobą ściśle powiązane, a stan zostaje zachowany.
Dzieje się tak niezależnie od tego, czy używasz dużego projektu z góry i próbujesz modelować wszystko, czy też wschodzącego projektu, w którym iterujesz i ewoluujesz obiekt w dowolny sposób odpowiadający potrzebom systemu w danym czasie.
Jeśli nie masz powodu do zachowania stanu, prawdopodobnie nie masz żadnego powodu do tworzenia instancji obiektu, co oznacza, że prawdopodobnie nie ma powodu, aby mieć klasę. O ile język, którego używasz, nie ma możliwości zdefiniowania przestrzeni nazw innej niż użycie klasy. W którym używanie klasy powinno być w porządku, ponieważ byłoby idiomatyczne.
Nie mogę wymyślić żadnych negatywnych aspektów używania klas w ten sposób, poza typowymi. Zapraszanie niepotrzebnych kosztów i złożoności poprzez konieczność utworzenia „przestrzeni nazw”. Każdy, kto obsługuje ten kod, zastanawia się, gdzie dane kosztują dodatkowe obciążenie poznawcze (najprawdopodobniej jednorazowy koszt na osobę). To niezwykłe zastosowanie bez dodatkowej wartości.
źródło