Zawsze widzę, że abstrakcja jest bardzo przydatną funkcją OO do zarządzania bazą kodu. Ale w jaki sposób zarządzane są duże bazy kodu inne niż OO? A może ostatecznie stają się „ Wielką Kulą Błota ”?
Aktualizacja:
Wydawało się, że wszyscy myślą, że „abstrakcja” to po prostu modularyzacja lub ukrywanie danych. Ale IMHO oznacza także użycie „klas abstrakcyjnych” lub „interfejsów”, które są niezbędne do wstrzykiwania zależności, a tym samym do testowania. Jak zarządzają tym bazy kodu inne niż OO? Ponadto, oprócz abstrakcji, enkapsulacja bardzo pomaga w zarządzaniu dużymi bazami kodu, ponieważ definiuje i ogranicza relacje między danymi a funkcjami.
Dzięki C bardzo możliwe jest pisanie kodu pseudo-OO. Nie wiem wiele o innych językach innych niż OO. Czy jest to zatem sposób zarządzania dużymi bazami kodu C?
źródło
Odpowiedzi:
Wydaje ci się, że OOP jest jedynym sposobem na osiągnięcie abstrakcji.
Chociaż OOP z pewnością jest w tym bardzo dobry, nie jest to jednak jedyny sposób. Zarządzanie dużymi projektami może być również utrzymywane przez bezkompromisową modularyzację (wystarczy spojrzeć na Perla lub Pythona, z których oba są doskonałe, podobnie jak języki funkcjonalne, takie jak ML i Haskell), oraz za pomocą mechanizmów takich jak szablony (w C ++).
źródło
static
dołączane do modyfikatora dostępu.Moduły, funkcje (zewnętrzne / wewnętrzne), podprogramy ...
jak powiedział Konrad, OOP nie jest jedynym sposobem zarządzania dużymi bazami kodu. W rzeczywistości napisano przed nim dość dużo oprogramowania (przed C ++ *).
źródło
Zasada modułowości nie ogranicza się do języków obiektowych.
źródło
Realistycznie albo rzadkie zmiany (myślę o obliczeniach emerytalnych w systemie zabezpieczenia społecznego) i / lub głęboko zakorzeniona wiedza, ponieważ ludzie utrzymujący taki system jak to robią od jakiegoś czasu (cyniczne zajęcie to bezpieczeństwo pracy).
Lepsze rozwiązania to powtarzalna walidacja, przez którą rozumiem test automatyczny (np. Testowanie jednostkowe) i testowanie na ludziach, które wykonują zabronione kroki (np. Testowanie regresyjne) „w przeciwieństwie do klikania i sprawdzania, co się psuje”.
Aby rozpocząć przejście do pewnego rodzaju zautomatyzowanego testowania z istniejącą bazą kodów, polecam przeczytanie „Efektywnej pracy Michaela Feathera ze starszym kodem” , która szczegółowo opisuje podejście do wprowadzania istniejących baz kodu, aż do pewnego rodzaju powtarzalnej struktury testowej OO, czy nie. Prowadzi to do pomysłów, na które inni odpowiedzieli, takich jak modularyzacja, ale książka opisuje właściwe podejście do tego, ale nie psuje rzeczy.
źródło
Chociaż wstrzykiwanie zależności oparte na interfejsach lub klasach abstrakcyjnych jest bardzo dobrym sposobem przeprowadzania testów, nie jest konieczne. Nie zapominaj, że prawie każdy język ma wskaźnik funkcji lub eval, który może zrobić wszystko, co możesz zrobić z interfejsem lub klasą abstrakcyjną (problem polega na tym, że mogą zrobić więcej , w tym wiele złych rzeczy, i że nie „ same w sobie dostarczają metadane). Taki program może faktycznie osiągnąć wstrzyknięcie zależności za pomocą tych mechanizmów.
Bardzo rygorystycznie podchodzę do metadanych. W językach OO relacje między bitami kodu są definiowane (do pewnego stopnia) przez strukturę klas, w sposób wystarczająco znormalizowany, aby mieć takie funkcje jak API refleksji. W językach proceduralnych pomocne może być ich samodzielne wymyślenie.
Odkryłem również, że generowanie kodu jest znacznie bardziej pomocne w języku proceduralnym (w porównaniu do języka zorientowanego obiektowo). To gwarantuje, że metadane są zsynchronizowane z kodem (ponieważ są używane do jego generowania) i daje coś w rodzaju punktów odcięcia programowania zorientowanego na aspekty - miejsce, w którym można wstrzyknąć kod, gdy jest potrzebny. Czasami jest to jedyny sposób na programowanie w trybie DRY w takim środowisku, które mogę zrozumieć.
źródło
W rzeczywistości, jak niedawno odkryłeś , funkcje pierwszego rzędu są wszystkim, czego potrzebujesz do odwrócenia zależności.
C obsługuje funkcje pierwszego rzędu, a nawet do pewnego stopnia zamknięcia . Makra C są potężną funkcją do programowania ogólnego, jeśli są obsługiwane z należytą ostrożnością.
Wszystko tam jest. SGLIB jest dość dobrym przykładem na to, jak C może być użyte do pisania kodu wielokrotnego użytku. I wierzę, że jest o wiele więcej.
źródło
Nawet bez abstrakcji większość programów jest podzielona na jakieś sekcje. Te sekcje zwykle dotyczą konkretnych zadań lub czynności i pracujesz nad nimi w taki sam sposób, jak pracowałbyś nad najbardziej szczegółowymi bitami abstrakcyjnych programów.
W małych i średnich projektach czasami jest to łatwiejsze dzięki purystycznej implementacji OO.
źródło
Abstrakcja, klasy abstrakcyjne, wstrzykiwanie zależności, enkapsulacja, interfejsy itp. Nie są jedynym sposobem kontrolowania dużych baz kodu; jest to sprawiedliwy i obiektowy sposób.
Głównym sekretem jest unikanie myślenia OOP podczas kodowania non-OOP.
Modułowość jest kluczem w językach innych niż OO. W C osiąga się to tak, jak wspomniał David Thornley w komentarzu:
źródło
Jednym ze sposobów zarządzania kodem jest rozpakowanie go na następujące typy kodu, zgodnie z architekturą MVC (model-view-controller).
Ta metoda organizacji kodu działa dobrze w przypadku oprogramowania napisanego w dowolnym języku OO lub innym niż OO, ponieważ wspólne wzorce projektowe są często wspólne dla każdego z obszarów. Ponadto tego rodzaju granice kodu są często najbardziej luźno powiązane, z wyjątkiem algorytmów, ponieważ łączą ze sobą formaty danych z danych wejściowych do modelu, a następnie z danymi wyjściowymi.
Ewolucje systemu często przybierają formę obsługi przez oprogramowanie większej liczby rodzajów danych wejściowych lub większej liczby rodzajów danych wyjściowych, ale modele i widoki są takie same, a kontrolery zachowują się bardzo podobnie. Lub system może z czasem potrzebować obsługiwać coraz więcej różnych rodzajów danych wyjściowych, nawet jeśli dane wejściowe, modele, algorytmy są takie same, a kontrolery i widoki są podobne. Lub system może zostać rozszerzony o nowe modele i algorytmy dla tego samego zestawu danych wejściowych, podobnych danych wyjściowych i podobnych widoków.
Jednym ze sposobów, w jaki programowanie OO utrudnia organizację kodu, jest to, że niektóre klasy są głęboko powiązane z trwałymi strukturami danych, a inne nie. Jeśli trwałe struktury danych są ściśle związane z takimi rzeczami, jak kaskadowe relacje 1: N lub relacje m: n, bardzo trudno jest określić granice klas, dopóki nie zakodujesz znaczącej i znaczącej części systemu, zanim nie będziesz wiedział, że masz rację . Każda klasa powiązana z trwałymi strukturami danych będzie trudna do ewolucji, gdy zmieni się schemat trwałych danych. Klasy, które obsługują algorytmy, formatowanie i analizowanie, są mniej podatne na zmiany w schemacie trwałych struktur danych. Użycie organizacji kodu typu MVC lepiej izoluje najbardziej niechlujne zmiany kodu w kodzie modelu.
źródło
Podczas pracy w językach, w których brakuje wbudowanej struktury i funkcji organizacyjnych (np. Jeśli nie ma przestrzeni nazw, pakietów, zestawów itp.) Lub gdy są one niewystarczające, aby kontrolować bazę kodu o takiej wielkości, naturalną reakcją jest rozwinięcie nasze własne strategie organizowania kodu.
Ta strategia organizacji prawdopodobnie obejmuje standardy dotyczące tego, gdzie należy przechowywać różne pliki, rzeczy, które muszą się zdarzyć przed / po pewnych rodzajach operacji, konwencje nazewnictwa i inne standardy kodowania, a także wiele „w ten sposób jest skonfigurowany - nie zadzieraj z tym! ” wpisz komentarze - które są ważne, o ile wyjaśniają dlaczego!
Ponieważ strategia najprawdopodobniej ostatecznie zostanie dostosowana do konkretnych potrzeb projektu (ludzi, technologii, środowiska itp.), Trudno jest stworzyć jedno uniwersalne rozwiązanie do zarządzania dużymi bazami kodu.
Dlatego uważam, że najlepszą radą jest przyjęcie strategii specyficznej dla projektu i uczynienie z niej zarządzania kluczowym priorytetem: udokumentuj strukturę, dlaczego tak jest, procesy wprowadzania zmian, poddaj ją audytowi, aby upewnić się, że jest przestrzegana, i co najważniejsze: zmień to, kiedy trzeba to zmienić.
Znamy się głównie na lekcjach i metodach refaktoryzacji, ale przy dużej bazie kodu w takim języku sama strategia organizacyjna (wraz z dokumentacją) wymaga refaktoryzacji w razie potrzeby.
Rozumowanie jest takie samo, jak w przypadku refaktoryzacji: rozwiniesz mentalny blok w kierunku pracy nad małymi częściami systemu, jeśli uważasz, że ogólna organizacja tego bałaganu, i ostatecznie pozwoli to się pogorszyć (przynajmniej takie jest moje zdanie to).
Zastrzeżenia są również takie same: użyj testu regresji, upewnij się, że możesz łatwo cofnąć, jeśli refaktoryzacja się nie powiedzie, i zaprojektuj, aby ułatwić refaktoryzację w pierwszej kolejności (lub po prostu tego nie zrobisz!).
Zgadzam się, że jest to o wiele trudniejsze niż refaktoryzacja kodu bezpośredniego i trudniej jest zweryfikować / ukryć czas przed menedżerami / klientami, którzy mogą nie rozumieć, dlaczego należy to zrobić, ale są to również rodzaje projektów najbardziej podatnych na gnicie oprogramowania spowodowane nieelastycznymi konstrukcjami najwyższego poziomu ...
źródło
Jeśli pytasz o zarządzanie dużą bazą kodu, pytasz, jak utrzymać dobrą strukturę bazy kodu na stosunkowo zgrubnym poziomie (biblioteki / moduły / budowanie podsystemów / korzystanie z przestrzeni nazw / posiadanie odpowiednich dokumentów we właściwych miejscach itp.). Zasady OO, zwłaszcza „klasy abstrakcyjne” lub „interfejsy”, to zasady utrzymywania kodu w czystości wewnątrz, na bardzo szczegółowym poziomie. Zatem techniki utrzymywania zarządzania dużą bazą kodu nie różnią się w przypadku kodu OO ani kodu innego niż OO.
źródło
Sposób obsługi polega na tym, że poznajesz granice używanych elementów. Na przykład następujące elementy w C ++ mają wyraźną granicę i wszelkie zależności poza nią muszą być dokładnie przemyślane:
Łącząc te elementy i rozpoznając ich granice, możesz stworzyć prawie dowolny styl programowania w c ++.
Przykładem takiej funkcji może być rozpoznanie, że źle jest wywoływać inne funkcje z funkcji, ponieważ powoduje to zależność, zamiast tego należy wywoływać tylko funkcje składowe parametrów oryginalnej funkcji.
źródło
Największym wyzwaniem technicznym jest problem przestrzeni nazw. Aby obejść ten problem, można użyć częściowego łączenia. Lepszym podejściem jest projektowanie przy użyciu standardów kodowania. W przeciwnym razie wszystkie symbole staną się bałaganem.
źródło
Emacs jest tego dobrym przykładem:
Testy Emacs Lisp używają
skip-unless
ilet-bind
do wykrywania funkcji i urządzeń testowych:Podobnie jak SQLite. Oto jego projekt:
sqlite3_open () → Otwórz połączenie z nową lub istniejącą bazą danych SQLite. Konstruktor dla sqlite3.
sqlite3 → Obiekt połączenia z bazą danych. Utworzony przez sqlite3_open () i zniszczony przez sqlite3_close ().
sqlite3_stmt → Przygotowany obiekt instrukcji. Utworzony przez sqlite3_prepare () i zniszczony przez sqlite3_finalize ().
sqlite3_prepare () → Skompiluj tekst SQL w bajtowy kod, który wykona kwerendę lub aktualizację bazy danych. Konstruktor dla sqlite3_stmt.
sqlite3_bind () → Przechowuj dane aplikacji w parametrach oryginalnego SQL.
sqlite3_step () → Przejdź do sqlite3_stmt do następnego wiersza wyników lub do zakończenia.
sqlite3_column () → Wartości kolumn w bieżącym wierszu wyników dla sqlite3_stmt.
sqlite3_finalize () → Destructor dla sqlite3_stmt.
sqlite3_exec () → Funkcja otoki, która wykonuje sqlite3_prepare (), sqlite3_step (), sqlite3_column () i sqlite3_finalize () dla ciągu jednej lub więcej instrukcji SQL.
sqlite3_close () → Destructor dla sqlite3.
SQLite wykorzystuje różnorodne techniki testowania, w tym:
Referencje
Koncepcyjne widoki architektury Emacsa (pdf)
Interfejs systemu operacyjnego SQLite lub „VFS”
Mechanizm wirtualnej tabeli SQLite
Wprowadzenie do interfejsu SQLite C / C ++
Emacs-Elisp-Programming · GitHub
Testy i ich środowisko - testy regresji Emacsa Lispa
Jak testowany jest SQLite
źródło