Bardzo fat model

Walidacje railsowe pogrupuj przy pomocy nowej składni po atrybutach, np tak:

validates :terms, :acceptance => true
validates :password, :confirmation => true, :presence => true

Do tego napisz własne walidatory pogrupowane w klasy http://www.perfectline.ee/blog/building-ruby-on-rails-3-custom-validators

Scopes bym zostawił.

[quote=paneq]Jakkolwiek porozbijasz na mniejsze pliki to wciąż będą tylko mniejsze pliki prawda ? Ta klasa pewnie ma za dużo odpowiedzialności i trzeba rozbić na mniejsze. Andrzej Krzywda kiedyś pokazał na wykładzie fajny trik. 2 klasy do jednej tabeli. Akurat miał przykład w którym to było bardzo sensowne. Elementy koszyka i elementy zamówienia.

......

Może na początek mogłoby pomóc, a może nie. To tylko technika, use at your own risk.

Ogólnie ja bym szedł w stronę rozbijania, wyciągania jej odpowiedzialności niż tylko rozdzielania plików.[/quote]
Wszystko prawda:) Ja tylko dorzucę od siebie jedną wskazówkę/metodę, którą kieruję się kiedy wydaje mi się, że klasę należy rozbić. W dużym uproszczeniu chodzi o sytuację, kiedy w klasie są na przykład dwa pola. Do tego kilka metod, przy czym część z nich korzysta tylko z pierwszego pola, a druga część tylko z drugiego. To oznacza ze te metody mają z sobą mało wspólnego i to właśnie one nadają się wtedy do rozdzielenia do dwóch osobnych klas (razem z polami, na których działają). Krótko mówiąć - kod blisko danych i bez zbędnego śmietnika dookoła.
Nie wiem czy będzie miało to zastosowanie tutaj, ale ogólnie zasada się sprawdza.

Zgadzam się z pplcanfly i dokładnie o taką sytuację mi chodziło. Załóżmy że kod w modelu ten duży na kilka tysięcy linijek jest już zoptymalizowany, ale to nie zmienia faktu że trudno się w nim połapać. Gdyby natomiast rozbić kod na do kilku modułów funkcjonalnych, to łatwiej można go ogarnąć i znalęźć odpowiednie metody (nie upraszcza to jednak testowania). Przy tak dużym pliku konieczne wydaje mi się stosowanie komentarzy dla metod - opisujących co metoda robi. Jeśli model jest DRY to masz ogromną ilość metod, a z czasem ciężko będzie zrozumieć co metoda robi (nawet jak jest teraz zrozumiała).

Właściwie to jest jeszcze jedna metoda, o której nikt nie pisał: DCI. W praktyce wygląda to tak, że rozbijamy klasę ma moduły, ale dodajemy te moduły wtedy kiedy ich potrzebujemy, a nie includujemy wszystko jak leci do klasy. Nie jestem na razie wielkim fanem tej metody i nie stosowałem tego w żadnym projekcie, ale w teorii jest całkiem fajne, bo załatwia problem dużych klas i nie mamy tego problemu co przy zwykły podzieleniu na includowane moduły. Tzn. jak np. chcemy przetestować jakąś metodę, to wystarczy ją przetestować w danym kontekście, a nie ze wszystkimi innymi modułami, np:

[code]user = User.new
user.extend(Customer)

testujemy metody, które user ma w kontekście “Customer”[/code]

Drogomir, to jest fajne podejście, ale już PO tym kiedy kolega podzieli klasę na pionowe moduły. Znaczy wtedy będzie mógł się zastanowić czy można niektóre z nich ładować leniwie, albo grupować po zbiorach kompetencji :slight_smile:

Z jednej strony tak, a z drugiej strony jak wypakowujesz moduły, żeby robić DCI, to będziesz to robił inaczej niż wypakowując je jako zwykłe moduły, które będziesz includował do klasy. Dodatkowo tak jak napisałem, o ile nie jestem na razie fanem DCI, to widzę w tym jakiś sens, a w samym dzieleniu klasy na kilka modułów nie bardzo.

Posluchaj sobie http://rubyrogues.com/object-oriented-programming-in-rails-with-jim-weirich/

Pada tam m.in. dość ciekawy pomysł tworzenia lekkich obiektów ActiveRecord do komunikacji z bazą, a całą logikę biznesową trzymania w osobnych klasach. Może ten Twój bardzo fat model da się biznesowo rozbić na parę różnych obiektów i miałbyś wtedy czytelne rozwiązanie.

Z dzieleniem takich klas na moduły się spotkałem kilkukrotnie i moim zdaniem to tylko ukrywa problem.

Kilka lat temu zrobiłem taki “refactoring”, że podzieliłem przerośnięte klasy na tematyczne moduły. Więc wiem już na własnej skórze, że nie jest to optymalne. Rozwiązuje to najwyżej połowę problemu (podział na pliki / katalogi), drugą połowę pozostawiając nierozwiązaną (mega obiekty anty-SOLID).

Obecnie właściwym rozwiązaniem (w nietrywialnej aplikacji) wydaje mi się traktowanie modeli jako DAO (asocjacje, walidacje, scopes itp) i umieszczanie logiki biznesowej w oddzielnych klasach.

Yehuda Katz na ten temat pisał tutaj: http://stackoverflow.com/questions/1068558/oo-design-in-rails-where-to-put-stuff/1071510#1071510