Bardzo fat model

Mam model prawie 1k lini kodu.
20 validacji, 10 assocjacji, 40 named scopów, dekoratory, aftery befory, ze 40 prywatnych itp

Chcę to porozbijać na mniejsze pliki. Czym się kierować przy podziale i nazywaniu plików/modułów ?
Mogę gdzieś podglądnąć dobre praktyki takiego refaktoringu. Jak to u Was wygląda?

After/before przenieś do obserwerów o ile ma to sens. Prywatne metody do klas / obiektów narzędziowych. Własne walidacje z metod do walidatorów.

Pionowo, tzn. kompletnymi podfunkcjonalnościami, tak jak to robią gemy/pluginów (acts_as_taggable, acts_as_tree).

Dzięki.
Powzoruję się tutaj https://github.com/teambox/teambox/tree/dev/app/models

[quote=pski]Dzięki.
Powzoruję się tutaj https://github.com/teambox/teambox/tree/dev/app/models[/quote]
Średni pomysł.

Tzn. nie przeglądałem wszystkiego, ale większość to tylko te same klasy, ale rozrzucone po kilku plikach. Jak to mówią: “Now you have 2 problems” :wink:

Nosz w mordesku.
A jest jakiś open source project gdzie rozwiązano to w sposób przykładny?

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.

[code]class Foo
set_table_name :table_name
end

class Bar
set_table_name :table_name
end[/code]
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.

Dzięki, podejdę od tej strony do tego.

[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.

[code]class Foo
set_table_name :table_name
end

class Bar
set_table_name :table_name
end[/code]
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]
To nie musi być sensowne w większości przypadków. Byłbym bardzo ostrożny i nie rzucał się na refaktoryzację wszystkiego w ten sposób.

Możesz sprówać konkretne funkcjonalnoći przenieść do modułów znajdujących się np. w podkatalogu a następnie je includować.

Przykład: Jeśli decydujemy się, że nasz model ma dedykowany (własnego autorstwa) mechanizm stanów (state machine) to można to wrzucić do oddzielnego modułu.

[quote=wafcio]Możesz sprówać konkretne funkcjonalnoći przenieść do modułów znajdujących się np. w podkatalogu a następnie je includować.

Przykład: Jeśli decydujemy się, że nasz model ma dedykowany (własnego autorstwa) mechanizm stanów (state machine) to można to wrzucić do oddzielnego modułu.[/quote]
To też tylko rozbije kod na mniejsze pliki, ale końcowo będziesz miał prawie to samo (a nawet gorzej, bo część rzeczy trzeba będzie zrobić w metodzie “included” jako, że to już nie będzie klasa).

Myślę, że odpowiedź na to pytanie jest ciężka, bo wszystko zależy od tego co w tych modelach tak naprawdę jest. W większości wypadków, żeby takie refaktoryzacje miały sens, trzeba to dobrze przemyśleć, a rozwiązania nie są trywialne, tzn. nie ma jakiegoś wzorca tego czy rozpakować jakiś kawałek do zewnętrznego pluginu przy okazji odrywając kod od aplikacji, czy wypakować np. zawartość jakiejś metody do innej klasy i zostawić w aplikacji.

https://github.com/stephankaag/concerned_with wydaje się być stworzony dla Twojego problemu :slight_smile:

+1 dla observerów, ja lubię tam wrzucać wszystko co zmienia inne modele, chyba że jest to jakaś jedna malutka metoda, a jakie jest Wasze podejście ?

Fajnie jest jak się czyta chociaż z jedną czy dwie odpowiedzi w wątku :wink:

Ten plugin to zwykłe require, czyli nie różni się zasadniczo niczym od rozwiązań, które były wyżej wspomniane: https://github.com/stephankaag/concerned_with/blob/master/lib/concerned_with.rb#L5, a tylko sprawia wrażenie czegoś innego.

Kolega ktory robil update z 3.0 do 3.1 mial duze klopoty z tym pluginem. Skonczylo sie tak ze zaczal uzywac modulow aby osiagnac to samo. Dla mnie ten gem to slaby hack

Do tego bardzo dobre są moduły, dodatkowo warto poszczególne elementy (walidacje, asocjacje itp) układać alfabetycznie - łatwiej się szuka.

Czyli ładowanie modułów zamiast otwierania klasy w innych plikach.
A czego z pliku głównego nie ruszać? Jest sens np asocjacje przenosić do modułu?

Moduły sprawdzają się tylko i wyłącznie do współdzielenia funkcjonalności pomiędzy różnymi klasami, możesz sobie “wmiksować” pewien zestaw metod do jednej, drugiej i trzeciej klasy, nie używając wspólnej klasy bazowej.

Moduły nie rozwiążą problemu przerostu ilości kodu w pojedynczej klasie, tylko ten problem zafałszują. Niestety to trudny problem do sensownego rozwiązania, szczególnie w przypadku ActiveRecord.

A taki prosty przykład… Spasły model ma 10 validacji railsowych i 10 validacji moimi metodami. Chcę to wyekstrachować do innego pliku.
Do modułu pomimo iż tego kodu nie użyje w żadnej innej klasie czy otwarcie klasy w innym pliku i wklepanie tam kodu? Podobnie ze scopami…
Pewnie przetestuje obie wersje i zobacze co wygodniej i ładniej ale chciałbym poznać opinie bardzie doświadczonych kolegów.

+1 do tego co napisał Hubert. Tak jak pisałem powyżej “now you have 2 problems”.

Tak jak już pisałem na początku, każde rozwiązanie, które nie zmienia samej struktury kodu, tylko rozrzuca go w wielu miejscach niewiele pomoże. Być może dla niektórych inspiracją do takiego działania jest to co ostatnio było robione w railsach, szczególnie w ActiveRecord, tzn. kod był właśnie rozrzucony po dużej ilości modułów. Tam ma to troszeczkę większy sens, bo to jest framework. W przypadku AR nie ma to tak dużego sensu jak np. w ActionControllerze, bo jest bardzo duża szansa, że ktoś w aplikacji będzie chciał lekki kontroler np. bez obsługi cookies, a szansa na to, że ktoś sobie zbuduje własną klasę bazową do modeli jest bliska zeru, ale dalej to jest trochę inna skala problemu.

Pomyśl o tym w ten sposób: mam wielką klasę na 2000 linii, dlatego ciężko mi ją testować i połapać się we wszystkich metodach. Teraz zamieniasz tą klasę na 5 różnych plików, w których jest dokładnie ten sam kod (no… w przypadku modułów prawie ten sam, ale w tym wypadku prawie nie robi dużej różnicy). Czy nagle jest Ci łatwiej się połapać o co chodzi? Czy łatwiej jest to testować?

Zgadzam się. Ale można do tego podejść jak do trzymania plików folderach. Łatwiej się połapać, znaleźć coś jeżeli masz dobrze nazwane foldery i w nich odpowiednie pliki. Ale łapię przekaz, inaczej podejdę do refaktoringu.