W miarę jak coraz więcej uczę się o OOP i zaczynam wdrażać różne wzorce projektowe, wracam do przypadków, w których ludzie nienawidzą Active Record .
Często ludzie mówią, że nie skaluje się dobrze (podając Twittera jako najlepszy przykład) - ale nikt tak naprawdę nie wyjaśnia, dlaczego nie skaluje się dobrze; i / lub jak osiągnąć zalety AR bez wad (w podobny, ale inny sposób?)
Mam nadzieję, że nie przerodzi się to w świętą wojnę dotyczącą wzorców projektowych - wszystko, co chcę wiedzieć, to **** **** konkretnie ****, co jest nie tak z Active Record.
Jeśli nie skaluje się dobrze, dlaczego nie?
Jakie ma inne problemy?
ruby-on-rails
design-patterns
oop
activerecord
Adam Tuttle
źródło
źródło
Odpowiedzi:
Istnieje ActiveRecord the Design Pattern i ActiveRecord the Rails ORM Library , a także mnóstwo podróbek dla .NET i innych języków.
To są różne rzeczy. Przeważnie stosują się do tego wzorca projektowego, ale rozszerzają i modyfikują go na wiele różnych sposobów, więc zanim ktokolwiek powie „ActiveRecord Sucks”, należy go zakwalifikować, mówiąc „który ActiveRecord, jest mnóstwo?”
Jestem zaznajomiony tylko z ActiveRecord Railsów, spróbuję odpowiedzieć na wszystkie skargi, które zostały podniesione w kontekście jego używania.
Kod:
To generuje SQL z
LEFT JOIN companies on companies.id = person.company_id
i automatycznie generuje powiązane obiekty firmy, więc możesz to zrobićpeople.first.company
i nie musi trafiać do bazy danych, ponieważ dane są już obecne.Kod:
Jest to odradzane, ponieważ jest brzydkie, ale w przypadkach, w których po prostu musisz napisać surowy kod SQL, jest to łatwe do zrobienia.
Kod:
Spowoduje to wybranie tylko kolumn nazwy i identyfikatora z bazy danych, wszystkie inne „atrybuty” w mapowanych obiektach będą po prostu zerowe, chyba że ręcznie przeładujesz ten obiekt i tak dalej.
źródło
Zawsze uważałem, że ActiveRecord jest dobry do szybkich aplikacji opartych na CRUD, w których Model jest stosunkowo płaski (jak w przypadku niezbyt wielu hierarchii klas). Jednak w przypadku aplikacji ze złożonymi hierarchiami obiektów obiektowych a prawdopodobnie lepszym rozwiązaniem jest DataMapper . Podczas gdy ActiveRecord zakłada stosunek 1: 1 między tabelami a obiektami danych, ten rodzaj relacji staje się nieporęczny w przypadku bardziej złożonych domen. W swojej książce o wzorcach Martin Fowler zwraca uwagę, że ActiveRecord ma tendencję do załamywania się w warunkach, w których twój Model jest dość złożony, i sugeruje DataMapper jako alternatywę.
Przekonałem się, że jest to prawdą w praktyce. W przypadkach, gdy masz dużo dziedziczenia w swojej domenie, trudniej jest zmapować dziedziczenie do RDBMS niż mapowanie powiązań lub kompozycji.
Sposób, w jaki to robię, to posiadanie obiektów „domeny”, do których mają dostęp kontrolery za pośrednictwem tych klas DataMapper (lub „warstwy usług”). Nie odzwierciedlają one bezpośrednio bazy danych, ale działają jako reprezentacja obiektu zewnętrznego dla jakiegoś obiektu ze świata rzeczywistego. Załóżmy, że masz klasę User w swojej domenie i potrzebujesz mieć odniesienia do lub kolekcje innych obiektów, które są już załadowane podczas pobierania tego obiektu User. Dane mogą pochodzić z wielu różnych tabel, a wzorzec ActiveRecord może sprawić, że będzie to naprawdę trudne.
Zamiast bezpośredniego ładowania obiektu User i uzyskiwania dostępu do danych za pomocą interfejsu API w stylu ActiveRecord, kod kontrolera pobiera obiekt User, na przykład wywołując interfejs API metody UserMapper.getUser (). To ten program odwzorowujący jest odpowiedzialny za ładowanie wszystkich powiązanych obiektów z odpowiednich tabel i zwracanie ukończonego obiektu „domeny” użytkownika do obiektu wywołującego.
Zasadniczo dodajesz tylko kolejną warstwę abstrakcji, aby kod był łatwiejszy w zarządzaniu. To, czy Twoje klasy DataMapper zawierają surowy niestandardowy SQL, czy wywołania API warstwy abstrakcji danych, czy nawet same uzyskują dostęp do wzorca ActiveRecord, nie ma tak naprawdę znaczenia dla kodu kontrolera, który otrzymuje ładny, wypełniony obiekt użytkownika.
W każdym razie tak to robię.
źródło
Myślę, że istnieje bardzo inny zestaw powodów między tym, dlaczego ludzie „nienawidzą” ActiveRecord, a tym, co jest z nim „nie tak”.
Jeśli chodzi o nienawiść, jest dużo jadu na wszystko, co dotyczy Railsów. Jeśli chodzi o to, co jest z nią nie tak, jest prawdopodobne, że jest jak każda technologia i są sytuacje, w których jest to dobry wybór i sytuacje, w których są lepsze możliwości. Sytuacja, w której nie możesz skorzystać z większości funkcji Rails ActiveRecord, z mojego doświadczenia, to sytuacja, w której baza danych jest źle skonstruowana. Jeśli uzyskujesz dostęp do danych bez kluczy podstawowych, z rzeczami, które naruszają pierwszą normalną formę, gdzie istnieje wiele procedur składowanych wymaganych do uzyskania dostępu do danych, lepiej jest użyć czegoś, co jest bardziej zwykłym opakowaniem SQL. Jeśli twoja baza danych jest stosunkowo dobrze zorganizowana, ActiveRecord pozwoli ci to wykorzystać.
Aby dodać do tematu odpowiadanie komentatorom, którzy twierdzą, że rzeczy są trudne w ActiveRecord, z powtórzeniem fragmentu kodu
Korzystając z opcji dołączania, ActiveRecord pozwala zastąpić domyślne zachowanie ładowania z opóźnieniem.
źródło
Moja długa i spóźniona odpowiedź, nawet niepełna, ale dobre wyjaśnienie DLACZEGO nienawidzę tego schematu, opinii, a nawet niektórych emocji:
1) wersja skrócona: moduł Active Record tworzy „ cienką warstwę ” „ silnego powiązania ” między bazą danych a kodem aplikacji. Co nie rozwiązuje żadnych problemów logicznych, żadnych problemów, żadnych problemów. IMHO, nie dostarcza ŻADNEJ WARTOŚCI, z wyjątkiem pewnej składniowej cukru dla programisty (który może następnie użyć „składni obiektowej”, aby uzyskać dostęp do niektórych danych, które istnieją w relacyjnej bazie danych). Wysiłek mający na celu zapewnienie programistom komfortu powinien (IMHO ...) lepiej zainwestować w niskopoziomowe narzędzia dostępu do baz danych, np. Niektóre odmiany prostego, łatwego, prostego
hash_map get_record( string id_value, string table_name, string id_column_name="id" )
i podobnych metod (oczywiście koncepcje i elegancja znacznie się różnią w zależności od używany język).2) wersja długa: W każdym projekcie opartym na bazach danych, w którym miałem „koncepcyjną kontrolę” nad rzeczami, unikałem AR i było dobrze. Zwykle buduję architekturę warstwową (prędzej czy później dzielisz oprogramowanie na warstwy, przynajmniej w projektach o średniej i dużej wielkości):
A1) sama baza danych, tabele, relacje, nawet jakaś logika, jeśli pozwala na to DBMS (MySQL również jest teraz dorosły)
A2) Bardzo często jest coś więcej niż tylko magazyn danych: system plików (bloby w bazie danych nie zawsze są dobrą decyzją ...), starsze systemy (wyobraź sobie "jak" będą one dostępne, możliwe jest wiele odmian ... ale to nie o to chodzi ...)
B) warstwa dostępu do bazy danych (na tym poziomie metody narzędziowe, pomoce w łatwym dostępie do danych w bazie danych są bardzo mile widziane, ale AR nie zapewnia tutaj żadnej wartości, z wyjątkiem cukru syntaktycznego)
C) Warstwa obiektów aplikacji: „obiekty aplikacji” czasami są prostymi wierszami tabeli w bazie danych, ale w większości przypadków są złożone obiekty i mają dołączoną wyższą logikę, więc inwestowanie czasu w obiekty AR na tym poziomie jest po prostu bezużyteczne , strata cennego czasu programistów, ponieważ „rzeczywista wartość”, „wyższa logika” tych obiektów i tak musi być zaimplementowana na obiektach AR - z i bez AR! I na przykład, dlaczego chcesz mieć abstrakcję „obiektów wpisów dziennika”? Zapisuje je kod logiki aplikacji, ale czy powinien mieć możliwość ich aktualizacji lub usunięcia? brzmi głupio, a
App::Log("I am a log message")
niektóre wielkości są łatwiejsze w użyciu niżle=new LogEntry(); le.time=now(); le.text="I am a log message"; le.Insert();
. Na przykład: użycie „obiektu wpisu dziennika” w widoku dziennika w aplikacji będzie działać dla 100, 1000 lub nawet 10000 wierszy dziennika, ale prędzej czy później będziesz musiał zoptymalizować - i założę się, że w większości przypadków po prostu użyj tej małej, pięknej instrukcji SQL SELECT w logice aplikacji (co całkowicie łamie ideę AR ...), zamiast zawijać tę małą instrukcję w sztywne, ustalone ramki pomysłów AR z dużą ilością zawijania kodu i ukrywania go. Czas spędzony na pisaniu i / lub budowaniu kodu AR mógł zostać zainwestowany w znacznie sprytniejszy interfejs do czytania list wpisów do dziennika (na wiele, wiele sposobów, nieograniczone są możliwości). Programiści powinni odważyć się wymyślić nowe abstrakcje, aby zrealizować swoją logikę aplikacji, która pasuje do zamierzonej aplikacji, a nie głupio ponownie wdrażać głupie wzorce, brzmi dobrze na pierwszy rzut oka!D) logika aplikacji - implementuje logikę interakcji obiektów oraz tworzenia, usuwania i wyświetlania (!) Obiektów logiki aplikacji (NIE, zadania te rzadko powinny być zakotwiczone w samych obiektach logiki aplikacji: czy mówi kartka papieru na biurku) masz nazwy i lokalizacje wszystkich innych arkuszy w twoim biurze? zapomnij o "statycznych" metodach wyliczania obiektów, to głupie, zły kompromis stworzony po to, by ludzki sposób myślenia pasował do [nie-całkowicie-AR-podobnych -] Myślenie AR)
E) interfejs użytkownika - cóż, to, co napiszę w kolejnych wierszach, jest bardzo, bardzo, bardzo subiektywne, ale z mojego doświadczenia wynika, że projekty oparte na AR często zaniedbywały część UI aplikacji - czas został zmarnowany na tworzenie niejasnych abstrakcji . W końcu takie aplikacje zmarnowały wiele czasu programistów i sprawiają wrażenie aplikacji tworzonych przez programistów dla programistów, zorientowanych technicznie wewnątrz i na zewnątrz. Koderzy czują się dobrze (ciężka praca w końcu wykonana, wszystko skończone i poprawne, zgodnie z koncepcją na papierze…), a klienci „muszą się tylko nauczyć, że tak musi być”, bo to „profesjonalnie” .. ok przepraszam, błądzę ;-)
Cóż, co prawda, to wszystko jest subiektywne, ale jest to moje doświadczenie (z wyłączeniem Ruby on Rails, może być inaczej i nie mam żadnego praktycznego doświadczenia z tym podejściem).
W płatnych projektach często słyszałem żądanie, aby rozpocząć od tworzenia obiektów „aktywnego rekordu” jako elementu składowego dla logiki aplikacji wyższego poziomu. Z mojego doświadczenia wynika, że częstobył swego rodzaju wymówką dla tego, że klient (w większości przypadków firma zajmująca się tworzeniem oprogramowania) nie miał dobrej koncepcji, szerokiego spojrzenia, przeglądu tego, jaki powinien być ostatecznie produkt. Ci klienci myślą w sztywnych ramach („w projekcie dziesięć lat temu to działało dobrze…”), mogą kształtować byty, mogą definiować relacje encji, mogą rozbijać relacje danych i definiować podstawową logikę aplikacji, ale potem przestają i przekaż ją tobie i pomyśl, że to wszystko, czego potrzebujesz ... często brakuje im pełnej koncepcji logiki aplikacji, interfejsu użytkownika, użyteczności itd., i tak dalej ... brakuje im dużego widoku i brakuje im miłości do szczegóły, i chcą, żebyś podążał tą drogą w rzeczywistości rozszerzonej, ponieważ ... no cóż, to działało w tym projekcie lata temu, dzięki czemu ludzie są zajęci i milczą? Nie wiem Ale „szczegóły” oddzielać mężczyzn od chłopców, czyli ... jak wyglądało oryginalne hasło reklamowe? ;-)
Po wielu latach (dziesięć lat aktywnego doświadczenia w programowaniu), gdy klient wspomina o „wzorcu aktywnego nagrywania”, dzwoni dzwonek alarmowy. Nauczyłem się próbować doprowadzić ich z powrotem do tej niezbędnej fazy koncepcyjnej , pozwolić im pomyśleć dwa razy, spróbować pokazać ich koncepcyjne słabości lub po prostu ich unikać, jeśli nie są rozeznani (w końcu wiesz, klient, który jeszcze nie wie, czego chce, może nawet myśli, że wie, ale nie, lub próbuje za darmo przekazać mi pracę koncepcyjną, kosztuje mnie wiele cennych godzin, dni, tygodni i miesięcy mojego czasu, życie jest zbyt krótkie ...).
W końcu: TO WSZYSTKO jest powodem, dla którego nienawidzę tego głupiego „wzorca aktywnego nagrywania” i robię to i będę tego unikać, kiedy tylko będzie to możliwe.
EDYCJA : nazwałbym to nawet bez wzoru. Nie rozwiązuje żadnego problemu (wzorce nie mają tworzyć cukru syntaktycznego). Stwarza wiele problemów: źródłem wszystkich problemów (wymienionych w wielu odpowiedziach tutaj ..) jest to, że po prostu ukrywa stary dobry, dobrze rozwinięty i potężny SQL za interfejsem, który jest z definicji bardzo ograniczony.
Ten wzór zastępuje elastyczność cukrem syntaktycznym!
Pomyśl o tym, jaki problem rozwiązuje dla Ciebie AR?
źródło
before_save
wywołań zwrotnych w celu zachowania spójności w rekordzie 4)after_commit
przechwycenia wyzwalaczy usług zewnętrznych. 5) Dobre DSL do organizowania zmian DDL w zestawy zmian (migracje). (Nadal jest ból, ale brak wzorca jest gorszy, gdy> 1 programista).Niektóre wiadomości wprawiają mnie w zakłopotanie. Niektóre odpowiedzi to „ORM” kontra „SQL” lub coś w tym rodzaju.
Faktem jest, że AR to tylko wzorzec programowania upraszczający, w którym wykorzystujesz obiekty domeny do pisania tam kodu dostępu do bazy danych.
Te obiekty zwykle mają atrybuty biznesowe (właściwości fasoli) i pewne zachowanie (metody, które zwykle działają na tych właściwościach).
AR po prostu mówi „dodaj kilka metod do tych obiektów domeny” do zadań związanych z bazą danych.
I muszę powiedzieć, z mojej opinii i doświadczenia, że wzór mi się nie podoba.
Na pierwszy rzut oka może brzmieć całkiem nieźle. Niektóre nowoczesne narzędzia Java, takie jak Spring Roo, używają tego wzorca.
Dla mnie prawdziwy problem dotyczy tylko OOP. Wzorzec AR zmusza cię w jakiś sposób do dodania zależności od obiektu do obiektów infrastruktury. Te obiekty infraestruktury pozwalają obiektowi domeny na wysyłanie zapytań do bazy danych za pomocą metod sugerowanych przez AR.
Zawsze mówiłem, że dwie warstwy są kluczem do sukcesu projektu. Warstwa usług (w której znajduje się logika biznesowa lub może być wyeksportowana za pomocą jakiejś technologii zdalnej, na przykład usług internetowych) i warstwa domeny. Moim zdaniem, jeśli dodamy pewne zależności (nie są naprawdę potrzebne) do obiektów warstwy domeny w celu rozwiązania wzorca AR, nasze obiekty domeny będą trudniejsze do udostępnienia innym warstwom lub (rzadkim) aplikacjom zewnętrznym.
Implementacja AR w Spring Roo jest interesująca, ponieważ nie opiera się na samym obiekcie, ale na niektórych plikach AspectJ. Ale jeśli później nie będziesz chciał pracować z Roo i będziesz musiał refaktoryzować projekt, metody AR zostaną zaimplementowane bezpośrednio w twoich obiektach domeny.
Inny punkt widzenia. Wyobraź sobie, że nie używamy relacyjnej bazy danych do przechowywania naszych obiektów. Wyobraź sobie, że aplikacja przechowuje nasze obiekty domeny na przykład w bazie danych NoSQL lub po prostu w plikach XML. Czy zaimplementowalibyśmy metody, które wykonują te zadania w naszych obiektach domeny? Nie sądzę (na przykład w przypadku XM dodalibyśmy zależności związane z XML do naszych obiektów domeny ... Naprawdę smutne, myślę). Dlaczego więc musimy implementować metody relacyjnej bazy danych w obiektach domeny, jak mówi wzorzec Ar?
Podsumowując, wzorzec AR może brzmieć prościej i dobrze dla małych i prostych aplikacji. Ale kiedy mamy złożone i duże aplikacje, myślę, że klasyczna architektura warstwowa jest lepszym podejściem.
źródło
Oryginalne pytanie jest oznaczone tagami rails i odnosi się do Twittera zbudowanego w Ruby on Rails. Framework ActiveRecord w Railsach jest implementacją wzorca projektowego Fowlera Active Record.
źródło
Główną rzeczą, jaką widziałem w odniesieniu do skarg dotyczących modułu Active Record, jest to, że kiedy tworzysz model wokół stołu i wybierasz kilka instancji modelu, w zasadzie robisz „wybierz * z ...”. Jest to dobre w przypadku edytowania rekordu lub wyświetlania rekordu, ale jeśli chcesz, powiedzmy, wyświetlić listę miast dla wszystkich kontaktów w Twojej bazie danych, możesz zrobić „wybierz miasto z ...” i uzyskać tylko te miasta . Zrobienie tego z Active Record wymagałoby wybrania wszystkich kolumn, ale tylko użycia City.
Oczywiście różne implementacje radzą sobie z tym inaczej. Niemniej jednak jest to jeden problem.
Teraz możesz to obejść, tworząc nowy model dla konkretnej rzeczy, którą próbujesz zrobić, ale niektórzy ludzie twierdzą, że to więcej wysiłku niż korzyści.
Ja kopie „Active Record”. :-)
HTH
źródło
Uwielbiam sposób, w jaki SubSonic robi tylko jedną kolumnę.
Zarówno
lub:
Ale Linq nadal jest królem, jeśli chodzi o leniwe ładowanie.
źródło
@BlaM: Czasami po prostu zaimplementowałem aktywny rekord wyniku połączenia. Nie zawsze musi to być relacja Tabela <--> Rekord aktywny. Dlaczego nie „Wynik instrukcji Join” <--> Active Record?
źródło
Opowiem o Active Record jako wzorcu projektowym, nie widziałem ROR.
Niektórzy programiści nienawidzą Active Record, ponieważ czytają inteligentne książki o pisaniu czystego i schludnego kodu, a te książki stwierdzają, że aktywny zapis narusza zasadę pojedynczej odpowiedzialności, narusza zasadę DDD, że obiekt domeny powinien być upartym ignorantem i wiele innych zasad z tego rodzaju książek .
Po drugie, obiekty domenowe w module Active Record są zazwyczaj 1-do-1 z bazą danych, co może być uważane za ograniczenie w niektórych systemach (głównie n-warstwowych).
To tylko abstrakcyjne rzeczy, nie widziałem rzeczywistej implementacji tego wzorca w Ruby on Rails.
źródło
Problem, który widzę z Active Records polega na tym, że zawsze jest to tylko jedna tabela. To jest w porządku, o ile naprawdę pracujesz tylko z tą jedną tabelą, ale kiedy pracujesz z danymi, w większości przypadków będziesz mieć gdzieś jakieś sprzężenie.
Tak, łączenie jest zwykle gorsze niż brak łączeń w ogóle, jeśli chodzi o wydajność, ale łączenie zwykle jest lepsze niż „fałszywe” łączenie , najpierw czytając całą tabelę A, a następnie wykorzystując zdobyte informacje do czytania i filtrowania tabeli B.
źródło
Problem z ActiveRecord polega na tym, że zapytania, które generuje automatycznie, mogą powodować problemy z wydajnością.
W końcu wykonujesz kilka nieintuicyjnych sztuczek, aby zoptymalizować zapytania, które sprawiają, że zastanawiasz się, czy pisanie zapytania ręcznie nie byłoby bardziej efektywne czasowo.
źródło
Chociaż wszystkie inne komentarze dotyczące optymalizacji SQL są z pewnością słuszne, moim głównym zarzutem dotyczącym wzorca aktywnego rekordu jest to, że zwykle prowadzi on do niedopasowania impedancji . Lubię utrzymywać moją domenę w czystości i odpowiednio hermetyzowanej, co zwykle niszczy wszelkie nadzieje związane z aktywnym rekordem.
źródło
Spróbuj wykonać wiele do wielu relacji polimorficznych. Nie takie proste. Zwłaszcza, gdy nie używasz chorób przenoszonych drogą płciową.
źródło