Problem
Pracuję nad projektem w języku Python, którego główną klasą jest nieco „ Boski obiekt ”. Jest tak cholernie wiele atrybutów i metod!
Chcę refaktoryzować klasę.
Jak dotąd…
W pierwszym kroku chcę zrobić coś stosunkowo prostego; ale kiedy wypróbowałem najprostsze podejście, przełamało kilka testów i istniejących przykładów.
Zasadniczo klasa ma długą listę atrybutów - ale mogę je przejrzeć i pomyśleć: „Te 5 atrybutów jest powiązanych… Te 8 są również powiązane… a potem jest cała reszta”.
getattr
Zasadniczo chciałem po prostu zgrupować powiązane atrybuty w klasę pomocnika przypominającą dykt. Miałem wrażenie, __getattr__
że byłby idealny do tej pracy. Przeniosłem więc atrybuty do osobnej klasy i, oczywiście, __getattr__
działałem doskonale magicznie…
Na pierwszy .
Ale potem spróbowałem uruchomić jeden z przykładów. Przykładowa podklasa próbuje ustawić jeden z tych atrybutów bezpośrednio (na poziomie klasy ). Ale ponieważ atrybut nie był już „fizycznie umiejscowiony” w klasie nadrzędnej, wystąpił błąd informujący, że atrybut nie istnieje.
@własność
Potem przeczytałem o @property
dekoratorze. Ale potem przeczytałem również, że stwarza to problemy dla podklas, które chcą zrobić, self.x = blah
gdy x
są własnością klasy nadrzędnej.
Pożądany
- Niech cały kod klienta nadal działa
self.whatever
, nawet jeśli kod nadrzędnywhatever
nie jest „fizycznie zlokalizowana” w samej klasie (lub instancji). - Grupuj powiązane atrybuty w pojemniki podobne do dict.
- Zmniejsz ekstremalną głośność kodu w klasie głównej.
Na przykład nie chcę po prostu tego zmieniać:
larry = 2
curly = 'abcd'
moe = self.doh()
Zaangażowany w to:
larry = something_else('larry')
curly = something_else('curly')
moe = yet_another_thing.moe()
… Ponieważ wciąż jest głośno. Chociaż z powodzeniem przekształca to po prostu atrybut w coś, co może zarządzać danymi, oryginał miał 3 zmienne, a poprawiona wersja wciąż ma 3 zmienne.
Byłbym jednak w porządku z czymś takim:
stooges = Stooges()
A jeśli wyszukiwanie self.larry
nie powiedzie się, coś sprawdzi stooges
i zobaczy, czy larry
jest. (Ale musi także działać, jeśli podklasa próbuje to zrobić larry = 'blah'
na poziomie klasy).
Podsumowanie
- Chcesz zastąpić pokrewne grupy atrybutów w klasie nadrzędnej jednym atrybutem, który przechowuje wszystkie dane w innym miejscu
- Chcesz pracować z istniejącym kodem klienta, który używa (np.)
larry = 'blah'
Na poziomie klasy - Chcesz nadal zezwalać na rozszerzanie, zastępowanie i modyfikowanie tych podklas atrybutów podklas bez wiedzy, że coś się zmieniło
czy to możliwe? A może szczekam niewłaściwe drzewo?
źródło
Odpowiedzi:
Po napisaniu, a następnie przeredagowaniu pythonowego „obiektu Boga”, współczuję. To, co zrobiłem, to podzielenie oryginalnego obiektu na podsekcje w oparciu o metody. Na przykład oryginał wyglądał jak ten pseudo kod:
Metoda rzeczy jest samodzielną „jednostką” pracy. Przeprowadziłem migrację do nowej klasy, którą tworzy instancja oryginalna. Wyciągnęło to również niezbędne właściwości. Niektóre były używane tylko przez podklasę i mogły poruszać się prosto. Inni zostali udostępnieni i zostali przeniesieni do wspólnej klasy.
„Boski obiekt” tworzy nową kopię klasy współdzielonej podczas uruchamiania, a każda z nowych podklas akceptuje wskaźnik jako część ich metody init. Na przykład, oto pozbawiona wersji wersja programu pocztowego:
Jest on tworzony raz i dzielony między różne klasy, które potrzebują możliwości wysyłania wiadomości.
Więc utwórz klasę
larry
z właściwościami i metodami, których potrzebujesz. Wszędzie, gdzie mówi klient,larry = blah
zamień go nalarryObj.larry = blah
. To migruje rzeczy do podprojektów bez przerywania bieżącego interfejsu.Jedyne, co należy zrobić, to poszukać „jednostek pracy”. Jeśli chcesz zamienić część „Boskiego obiektu” na własną metodę, zrób to . Ale odłóż metodę na bok . Wymusza to utworzenie interfejsu między komponentami.
Ułożenie fundamentów pozwala podążać za nim wszystkim innym. Na przykład fragment obiektu pomocnika pokazujący, w jaki sposób łączy się on z programem pocztowym:
Skoncentruj się na najmniejszej możliwej jednostce pracy i przenieś ją. Jest to łatwiejsze do zrobienia i pozwala szybko rozpocząć konfigurację. Nie patrz na właściwości przenoszenia rzeczy, w większości przypadków są one pomocnicze w stosunku do zadań, które są z nimi wykonywane. Cokolwiek pozostanie po tym, jak poradzisz sobie z metodami, prawdopodobnie powinno zostać w oryginalnym obiekcie, ponieważ jest on częścią stanu wspólnego.
Ale nowe obiekty powinny teraz akceptować właściwości, których potrzebują, jako zmienne początkowe, nie dotykając właściwości obiektów wywołujących. Następnie zwracają wszelkie niezbędne wartości, które mogą zostać użyte przez osobę dzwoniącą do zaktualizowania wspólnych właściwości w razie potrzeby. Pomaga to rozdzielić obiekty i stworzyć bardziej niezawodny system.
źródło