tło
Nie jestem wielkim fanem abstrakcji. Przyznaję, że można skorzystać z możliwości dostosowania, przenośności i ponownego wykorzystywania interfejsów itp. Są tam realne korzyści i nie chcę tego kwestionować, więc zignorujmy to.
Jest jeszcze jedna główna „korzyść” abstrakcji, która polega na ukryciu logiki implementacji i szczegółów przed użytkownikami tej abstrakcji. Argument polega na tym, że nie musisz znać szczegółów, i że w tym momencie należy skoncentrować się na własnej logice. Ma to sens w teorii.
Jednak za każdym razem, gdy utrzymuję aplikacje dla dużych przedsiębiorstw, zawsze muszę znać więcej szczegółów. Staje się to ogromnym problemem, coraz głębiej i głębiej zagłębiając się w abstrakcję na każdym kroku, aby dowiedzieć się, co dokładnie robi; tj. konieczność wykonania „otwartej deklaracji” około 12 razy przed znalezieniem zastosowanej procedury składowanej.
Ta mentalność „ukrywania szczegółów” wydaje się po prostu przeszkadzać. Zawsze pragnę bardziej przejrzystych interfejsów i mniej abstrakcji. Potrafię czytać kod źródłowy wysokiego poziomu i wiedzieć, co on robi, ale nigdy nie będę wiedział, jak to robi, kiedy to, jak to robi, to naprawdę muszę wiedzieć.
Co tu się dzieje? Czy każdy system, nad którym kiedykolwiek pracowałem, został źle zaprojektowany (przynajmniej z tej perspektywy)?
Moja filozofia
Kiedy tworzę oprogramowanie, czuję, że staram się podążać za filozofią, która moim zdaniem jest ściśle związana z filozofią ArchLinux :
Arch Linux zachowuje nieodłączną złożoność systemu GNU / Linux, jednocześnie zapewniając dobrą organizację i przejrzystość. Programiści i użytkownicy Arch Linux uważają, że próba ukrycia złożoności systemu prowadzi do jeszcze bardziej złożonego systemu i dlatego należy go unikać.
Dlatego nigdy nie próbuję ukrywać złożoności mojego oprogramowania za warstwami abstrakcji. Staram się nadużywać abstrakcji, a nie stać się jej niewolnikiem.
Pytanie w sercu
- Czy ukrywanie szczegółów ma realną wartość?
- Czy nie poświęcamy przejrzystości?
- Czy ta przejrzystość nie jest cenna?
Odpowiedzi:
Powodem ukrywania szczegółów nie jest ukrywanie szczegółów; umożliwia modyfikację implementacji bez rozbijania kodu zależnego.
Wyobraź sobie, że masz listę obiektów, a każdy obiekt ma
Name
właściwość i inne dane. I wiele razy musisz znaleźć pozycję na liście, któraName
pasuje do określonego ciągu.Oczywistym sposobem jest zapętlenie każdego elementu jeden po drugim i sprawdzenie, czy
Name
pasuje do łańcucha. Ale jeśli okaże się, że zajmuje to zbyt dużo czasu (tak jak gdyby to miało kilka tysięcy pozycji na liście), możesz chcieć zastąpić go wyszukiwaniem słownika obiektów łańcuchowych.Teraz, jeśli wszystkie twoje wyszukiwania zostały wykonane przez pobranie listy i zapętlenie jej, masz ogromną pracę do zrobienia, aby to naprawić. Jest jeszcze trudniej, jeśli jesteś w bibliotece i korzystają z niej użytkownicy zewnętrzni; nie możesz wyjść i naprawić ich kodu!
Ale jeśli miał
FindByName
metodę hermetyzacji procesu nazwą odnośnika, można po prostu zmienić sposób jest to realizowane i cały kod, który wywołuje to będzie kontynuować pracę i dostać dużo szybciej za darmo. To jest prawdziwa wartość abstrakcji i enkapsulacji.źródło
Właśnie skończyłem czytać sekcję w Code Complete o abstrakcji, więc tam większość źródeł.
Celem abstrakcji jest usunięcie potrzeby pytania „jak to się realizuje?”. Kiedy dzwonisz
user.get_id()
, wiesz, żeid
to właśnie otrzymasz. Jeśli musisz zapytać „w jaki sposób uzyskuje identyfikator użytkownika?” wtedy prawdopodobnie nie potrzebujeszid
alboget_id()
zwraca coś, co jest nieoczekiwane i źle zaprojektowane.Używasz abstrakcji, aby projektować:
nie projekt
źródło
Tak. Prezentując abstrakcje, możemy myśleć i programować na wyższym poziomie.
Wyobraź sobie modelowanie układów fizycznych bez rachunku różniczkowego lub algebry macierzy. Jest to całkowicie niepraktyczne. Podobnie, jeśli możemy programować tylko na poziomie skalarnym, nie będziemy w stanie rozwiązać interesujących problemów. Nawet stosunkowo proste aplikacje internetowe mogą znacznie skorzystać z abstrakcji, takich jak biblioteki znaczników. Znacznie łatwiej jest wstawić znacznik oznaczający „pola wprowadzania adresu” niż wielokrotnie tworzyć cztery pola tekstowe i pole wyboru. A jeśli zdecydujesz się na ekspansję zagraniczną, możesz po prostu zmodyfikować definicję tagu, zamiast naprawiać każdy formularz do obsługi adresów międzynarodowych. Skuteczne wykorzystanie abstrakcji sprawia, że niektórzy programiści są dziesięć razy skuteczniejsi niż inni.
Ludzie mają ograniczoną pamięć roboczą. Abstrakcja pozwala nam wnioskować o dużych systemach.
Nie. Jeśli abstrakcje nie są używane, to cel komponentu oprogramowania jest zakopany w powtarzających się szczegółach. Programiści spędzają dni na brodzeniu przez kod w ten sposób:
i myślę „och tak, kolejna czteropoziomowa pętla nad zestawami”, zamiast widzieć
Jak wskazałeś, pośrednictwo wiąże się z pewnymi kosztami. Tworzenie warstw „na wszelki wypadek” nie ma sensu. Użyj abstrakcji, aby zmniejszyć powielanie i wyjaśnić kod.
źródło
Kiedy ludzie mówią, że abstrakcje ukrywają szczegóły implementacji, w rzeczywistości nie oznaczają „ukryć” w tym sensie, że utrudniają ich znalezienie. Chodzi o oddzielne szczegóły implementacji od interfejsu publicznego, aby interfejs był prosty, zwięzły i łatwy w zarządzaniu. Podobnie jak samochód „ukrywa” większość swoich istotnych części i oferuje jedynie dość podstawowy zestaw elementów sterujących do ich obsługi, moduł oprogramowania „ukrywa” większość swojej funkcjonalności głęboko w jelitach i udostępnia jedynie ograniczoną liczbę metod dostępu do prowadź to. Wyobraź sobie samochód, w którym musiałeś ręcznie obsługiwać wszystkie elementy wewnętrzne silnika (a jest ich naprawdę dużo), trudno byłoby mieć oko na ruch drogowy i znaleźć drogę.
Ale utrzymanie prostego interfejsu nie jest jedynie kwestią estetyczną; może to zmienić różnicę między udanym projektem a marszem śmierci. Grajmy przez chwilę w adwokata diabła; wyobraź sobie projekt oprogramowania bez żadnych abstrakcji. Jeśli chcesz zachować wartość, użyj zmiennej globalnej. Jeśli chcesz użyć funkcji więcej niż raz, skopiuj ją i wklej. Jeśli potrzebujesz dwóch różnych wersji określonej sekcji kodu, skopiuj-wklej, zawiń ją w
if
instrukcji i zmodyfikuj obie gałęzie. Technicznie rzecz biorąc, to działa, ale kilka miesięcy później będziesz walczył z kilkoma naprawdę paskudnymi problemami:W źle wyodrębnionej bazie kodu wpływ zwykle rośnie wykładniczo wraz z rozmiarem bazy kodu, to znaczy dodanie stałej ilości kodu zwiększa wysiłek konserwacyjny o stały współczynnik. Co gorsza, dodanie większej liczby programistów do projektu nie zwiększa produktywności liniowo, ale w najlepszym razie logarytmicznie (ponieważ im większy zespół, tym więcej narzutu wymaga komunikacja).
źródło
Myślę, że powinieneś zrozumieć, jak to działa w razie potrzeby. Kiedy już określisz, że robi to, co myślałeś, że to zrobi, masz spokój. Nigdy nie myślałem, że celem było ukrycie tego na zawsze.
Po ustawieniu budzika, który jest pewny, że zadziała, możesz spać, wiedząc, że zadziała w odpowiednim momencie. Budzenie się o godzinę wcześniej, żebyś mógł obserwować, jak sekundy odliczają, to marnowanie.
źródło
Aby odpowiedzieć konkretnie na twoje pytania:
Tak. Jak przyznajesz w pierwszym wierszu pytania.
Nie całkiem. Dobrze napisana abstrakcja ułatwi zrozumienie szczegółów w razie potrzeby.
Tak. Abstrakcje powinny być zaprojektowane i wdrożone, aby ułatwić zrozumienie szczegółów w razie potrzeby / potrzeby.
źródło
Powiedziałbym, że ukrywanie szczegółów jest świetne, gdy działają ukryte rzeczy.
Załóżmy na przykład, że opracowujemy interfejs, który definiuje zachowania (tj. GetListItems, SendListItem), które są dwiema funkcjami inicjowanymi przez użytkownika poprzez kliknięcie przycisku lub coś takiego .. TERAZ każdy użytkownik może mieć swój własny „ListItemStore” .. powiedz jeden jest na Facebooku, na myspace .. (na przykład) .. i powiedz, że jest zapisany jako właściwość / ustawienie użytkownika gdzieś w aplikacji poprzez preferencje użytkownika .. i powiedz, że twórcy aplikacji mogą dodawać dodatkowe ListItemStore w trakcie czas (mybook, facespace itp.)
teraz jest wiele szczegółów na temat łączenia się z Facebookiem i zdobywania ich przedmiotów .. i jest tyle samo szczegółów podczas łączenia się z myspace .. i tak dalej ...
teraz, po napisaniu początkowego kodu „dostępu do sklepu”, może nie być konieczne jego modyfikowanie (w przypadku Facebooka prawdopodobnie potrzebujemy pełnoetatowego programisty, aby nadążyć za zmianami, zing ..),
więc kiedy używasz kodu, jest to coś takiego:
a teraz masz dane użytkownika z dowolnego miejsca, w którym je przechowujesz, a ponieważ martwię się o to, aby uzyskać listę przedmiotów i coś z tym zrobić, a ponieważ zajęło to tylko 2 wiersze, które będą działać bez względu na to, jak dodano wiele innych sklepów, mogę wrócić do odpowiadania / zamieszczania pytań na stosie ... lol ..
Tak więc cała instalacja hydrauliczna, aby tak się stało, jest „ukryta” i kogo to naprawdę obchodzi, o ile to zrobię, dopóki dostanę prawidłową listę przedmiotów. Jeśli masz testy jednostkowe, możesz nawet łatwiej odpocząć, ponieważ wyniki powinny „ zostały już określone ilościowo ..
źródło
To, co nazywacie „ukrywaniem”, wielu postrzega jako oddzielne obawy (np. Implementacja vs. Interfejs).
Moim zdaniem, jedną z głównych korzyści z abstrakcji jest zmniejszenie niepotrzebnych szczegółów z ograniczonej przestrzeni mózgowej dewelopera.
Gdyby kod implementacyjny był zaciemniony, widziałbym, że utrudnia to przejrzystość, ale abstrakcja, którą widzę, jest po prostu dobrą organizacją.
źródło
Po pierwsze, wszystko poza instrukcją pojedynczego kodu maszynowego jest w istocie abstrakcją -
while...do
pętla jest spójnym symbolicznym sposobem reprezentowania porównań i wywołań adresowych wymaganych do powtórzenia zestawu instrukcji do momentu spełnienia warunku. Podobnie typ int jest abstrakcją dla liczby X bitów (w zależności od systemu). W programowaniu chodzi o abstrakcję.Prawdopodobnie zgodziłbyś się, że te prymitywne abstrakcje są niezwykle przydatne. Cóż, więc jest w stanie zbudować własny. OOAD i OOP są najważniejsze.
Załóżmy, że masz wymaganie, aby użytkownicy chcieli móc eksportować dane z ekranu w różnych formatach: tekst rozdzielany, Excel i pdf. Czy to nie przydatne, że można utworzyć interfejs o nazwie „Eksporter” za pomocą metody eksportu (danych), na podstawie której można zbudować DelimitedTextExporter, ExcelExporter i PDFExporter, z których każdy wie, jak stworzyć swoje konkretne wyjście? Jedyny program wywołujący musi wiedzieć, że może wywoływać metodę eksportu (danych), a cokolwiek zastosuje implementacja, spełni swoje zadanie. Co więcej, jeśli zmieniają się reguły tekstowe rozdzielane, możesz zmienić DelimitedTextExporter bez konieczności bałagania się w ExcelExporter, być może zerwania.
Prawie wszystkie dobrze znane wzorce projektowe stosowane w programowaniu OO zależą od abstrakcji. Polecam lekturę Freemana i Wzory pierwszego projektu Freemana, aby lepiej zrozumieć, dlaczego abstrakcja jest dobra
źródło
Myślę, że rozumiem twoje zdanie na ten temat i myślę, że mam podobne zdanie.
Współpracowałem z programistami Java, którzy przekształcają 50 linii wiersza kodu w 3 klasy i 3 interfejsy, ponieważ jest to łatwe do zrozumienia. I nie mogłem tego znieść.
Sprawa była strasznie trudna do zrozumienia, prawie niemożliwa do debugowania i nigdy nie musiała „przełączać implementacji”.
Z drugiej strony widziałem również kod, w którym wiele obiektów ma podobne zachowanie i jest używanych w jednym miejscu, i mogłyby naprawdę korzystać ze wspólnych pętli sortowania / przetwarzania, gdyby metody zostały ujawnione za pośrednictwem wspólnego interfejsu.
Tak więc, IMHO, podstawowe obiekty, które prawdopodobnie będą wykorzystywane w podobnych scenariuszach, zwykle korzystają ze wspólnego zachowania, które powinno być dostępne poprzez interfejs. Ale to wszystko, abstrahując od prostych rzeczy, ponieważ ma rację lub umożliwia zmianę implementacji, jest tylko sposobem na bałagan w kodzie.
Z drugiej strony wolę dłuższe, inteligentniejsze klasy od gwałtownej liczby małych klas ze wszystkimi problemami zarządzania przez całe życie, trudnymi do zobaczenia relacjami i wykresami połączeń spaghetti. Więc niektórzy ludzie się ze mną nie zgodzą.
źródło
Nadrzędnym celem ukrywania i abstrakcji powinno być oddzielenie użytkownika od implementacji, aby można je było niezależnie zmienić. Jeśli konsument jest połączony ze szczegółami implementacji, z powodu majstrowania przy wewnętrznych elementach, oba są odlane w kamieniu i trudniej jest wprowadzić nowe funkcje lub lepsze algorytmy w przyszłości.
Podczas pisania modułu ukryte części implementacji dają ci poczucie, że możesz je zmienić bez ryzyka złamania innego kodu, o którym nie możesz pomyśleć.
Kolejną zaletą zapewniania nieprzejrzystych interfejsów jest to, że znacznie zmniejszają one powierzchnię między podsystemami. Zmniejszając liczbę sposobów interakcji, mogą stać się bardziej przewidywalne, łatwiejsze do przetestowania i mniej błędów. Interakcje między modułami również zwiększają się kwadratowo wraz z liczbą modułów, dlatego warto kontrolować ten wzrost złożoności.
To powiedziawszy, oczywiście można ukryć za dużo i zbyt głęboko zagnieździć interfejsy. Zadaniem programisty, jako inteligentnego człowieka, jest zaprojektowanie systemu tak, aby był maksymalnie użyteczny, jednocześnie minimalizując złożoność i maksymalizując łatwość konserwacji.
źródło
W tak wielu przypadkach po prostu nie musisz wiedzieć, jak rzeczy są realizowane. Mogę prawie zagwarantować, że napiszesz taki kod
people.Where(p => p.Surname == "Smith")
tak wiele razy dziennie, ale prawie nigdy nie pomyślisz „jak taWhere()
metoda faktycznie działa?” Po prostu cię to nie obchodzi - wiesz, że ta metoda istnieje i że zapewnia ci oczekiwane rezultaty. Dlaczego miałbyś obchodzić, jak to działa?Jest to dokładnie to samo w przypadku dowolnego oprogramowania wewnętrznego; to, że nie zostało napisane przez Oracle, Microsoft itp., nie oznacza, że powinieneś polować na sposób jego implementacji. Można zasadnie oczekiwać, że wywołana metoda
GetAllPeopleWithSurname(string name)
zwróci Ci listę osób o tym nazwisku. Może iterować po liście, może używać słownika, może zrobić coś całkowicie szalonego, ale po prostu nie powinno cię to obchodzić .Istnieje oczywiście wyjątek od tej reguły (bez niej nie byłaby to reguła!), Czyli w przypadku błędu w metodzie. Tak więc w powyższym przykładzie, jeśli masz listę z 3 osób w tym i wiesz, że jeden z nich ma nazwisko Smith i nie są zwracane w liście następnie dbasz o realizacji tej metody, ponieważ jest wyraźnie uszkodzony .
Po prawidłowym wykonaniu abstrakcja jest cudowna, ponieważ pozwala odfiltrować wszystkie rzeczy, które nie są przydatne, gdy trzeba ją przeczytać później. Nie zapominaj, że dużo więcej czasu spędza się na czytaniu kodu niż na pisaniu go, dlatego należy położyć nacisk na jak najłatwiejsze wykonanie tego zadania. Być może myślisz również, że abstrakcja oznacza hierarchię obiektów tak długich jak twoje ramię, ale może być tak prosta, jak refaktoryzacja metody 100-liniowej na 10 metod, każda o długości 10 linii. To, co kiedyś było 10 krokami wszystkie razem, teraz jest 10 oddzielnymi krokami, dzięki czemu możesz udać się prosto do miejsca, w którym ukrywa się ten nieznośny błąd.
źródło
PeopleFactory.People.Strategy.MakePeople.(CoutryLaw.NameRegistry.NameMaker.Make()) as People.Female
Abstrakcje powodują ukrycie informacji. To powinno skończyć się na dolnym sprzęgle. Powinno to prowadzić do zmniejszenia ryzyka zmian. Powinno to doprowadzić do zadowolenia programistów, nie denerwując się przy dotykaniu kodu.
Te idee są wyrażone przez trzy podstawowe prawa w architekturze oprogramowania:
Prawo Simona: „Hierarchie zmniejszają złożoność”. (Hierarchie wprowadzają abstrakcję)
Prawo Parny: „Tylko to, co jest ukryte, można zmienić bez ryzyka”.
Prawo Constantina: solidne programy wymagają niskiego sprzężenia i wysokiej spójności
źródło
Jestem również w branży aplikacji korporacyjnych i to pytanie przykuło moją uwagę, ponieważ sam mam takie samo pytanie. Do tej pory miałem pewien wgląd w kwestię abstrakcji w trakcie mojej kariery, ale mój wgląd nie jest bynajmniej uniwersalną odpowiedzią. Nadal uczę się / słucham nowych pomysłów i myśli, więc to, co obecnie uważam, może się zmienić.
Kiedy utrzymywałem dużą i złożoną aplikację opieki zdrowotnej, po prostu cię lubiłem, nienawidziłem tam wszystkich abstrakcji. Ustalenie, dokąd zmierza cały kod, to ból szyi. Skakanie po różnych klasach powodowało zawroty głowy. Więc powiedziałem sobie: „abstrakcja jest do bani, zminimalizuję abstrakcję, kiedy projektuję”.
Potem przyszedł czas, kiedy musiałem zaprojektować aplikację (względnie mały komponent usługi internetowej) od podstaw. Pamiętając cały ból, miałem dość płaską konstrukcję komponentu. Problem polegał na tym, że kiedy wymagania uległy zmianie, musiałem dokonać zmian w wielu różnych miejscach (wymagania były dość płynne i nie mogłem nic z tym zrobić). Było tak źle, że w zasadzie zrzucam swój pierwotny projekt i przeprojektowanie z abstrakcją, a sytuacja się poprawiła - nie musiałem wprowadzać zmian w wielu miejscach, gdy wymagania się zmieniały.
Dostarczyłem aplikację, usiadłem przez kilka tygodni, a potem powiedziano mi, aby rozpocząć utrzymywanie aplikacji. Od jakiegoś czasu nie pamiętałem wszystkiego, więc trochę starałem się zrozumieć mój własny kod, a abstrakcja nie pomagała.
Później wziąłem udział w wielu innych projektach i miałem trochę więcej okazji do zabawy z poziomami abstrakcji. To, co faktycznie znajduję, to tylko moja osobista opinia, abstrakcja bardzo pomaga w rozwoju, ale ma negatywny wpływ, gdy nie napisałeś kodu i próbujesz zrozumieć wszystko na najgłębszym poziomie aplikacji; spędzicie więcej czasu skacząc po różnych klasach i próbując nawiązać kontakty.
Mam wrażenie, że abstrakcja jest tak cenna w czasie programowania, że kłopoty, które przechodzimy jako opiekun, gdy próbujemy zrozumieć kod, są warte zachodu. Istnieje oprogramowanie do rozwiązywania problemów biznesowych, problemy biznesowe ewoluują z czasem; stąd oprogramowanie musi ewoluować z czasem. Bez abstrakcji ewolucja oprogramowania jest bardzo trudna. Można zaprojektować abstrakcję w taki sposób, że opiekun może z łatwością poruszać się po bazie kodu, gdy zobaczy wzór struktury kodu, tak że frustruje tylko początkowa krzywa uczenia się.
źródło
Jak powiedzieli inni, „ukrywanie szczegółów” za abstrakcją pozwala na ich zmianę bez wpływu na użytkowników. Pomysł ten pochodzi z „ Kryteriów stosowanych przez Parnasa w dekompozycji systemów na moduły” (1972) i jest związany z ideą abstrakcyjnych typów danych (ADT) i programowaniem obiektowym.
Mniej więcej w tym samym czasie motywacyjny model relacyjnych danych dużych dużych banków danych Codda (1970) (patrz streszczenie i wprowadzenie) chciał zmienić reprezentację pamięci wewnętrznej baz danych bez wpływu na użytkowników bazy danych. Widział, jak programiści regularnie poświęcają dni, modyfikując strony kodu, aby poradzić sobie z niewielkimi zmianami pamięci.
To powiedziawszy, abstrakcja nie jest zbyt przydatna, jeśli musisz zobaczyć, co jest w środku, aby móc z niej skorzystać. Dobrze może być dobrze zaprojektowane. Przykładem dobrej abstrakcji jest dodatek - kiedy ostatni raz musiałeś pomyśleć o tym, co dzieje się w środku? (ale czasami to robisz, np. w przypadku przepełnienia).
Podstawowym problemem (IMHO) jest to, że aby dobrze zaprojektować moduły (w sensie Parnasa), musisz przewidzieć, co się zmieni, a co nie. Przewidywanie przyszłości jest trudne - ale jeśli masz duże doświadczenie z czymś i dobrze to rozumiesz, możesz dobrze przewidzieć. Dlatego możesz zaprojektować moduł (abstrakcja), który działa dobrze.
Wydaje się jednak, że los wszystkich abstrakcji - nawet tych najlepszych - że ostatecznie nastąpią nieprzewidziane (i prawdopodobnie nieprzewidywalne) zmiany, które wymagają przerwania abstrakcji. Aby rozwiązać ten problem, niektóre abstrakcje mają wyjście, w którym możesz uzyskać dostęp do głębszego poziomu, jeśli naprawdę tego potrzebujesz.
To wszystko wydaje się bardzo negatywne. Ale myślę, że prawda jest taka, że otaczają nas abstrakcje, które działają tak dobrze, że ich nie zauważamy ani nie zdajemy sobie sprawy z tego, co ukrywają. Dostrzegamy tylko słabe abstrakcje, więc mamy ich żółtawy widok.
źródło
Abstrakcje są głównie z korzyścią dla ich konsumentów (np. Programistów aplikacji). Programiści systemowi (projektanci) mają więcej pracy, aby uczynić je pięknymi i użytecznymi, dlatego dobry projekt zwykle nie jest wykonywany przez początkujących.
Może nie lubisz abstrakcji, ponieważ zawsze dodają złożoności? Może systemy, nad którymi pracowałeś, miały zapalenie abstrakcji (nadmierne korzystanie z abstrakcji)? Nie są panaceum.
Dodatkowa praca i złożoność przydatnej abstrakcji powinny się opłacić, ale trudno jest na pewno wiedzieć. Jeśli myślisz o abstrakcji jako punkcie zwrotnym, wówczas projekt oprogramowania może być elastyczny po obu stronach: implementacje abstrakcji można modyfikować bez łamania kodu klienta i / lub nowi klienci mogą łatwo ponownie używać abstrakcji do tworzenia nowych rzeczy.
Można niemal zmierzyć zwrot z inwestycji w abstrakcje, pokazując, że zostały one „wygięte” w czasie w jednym lub obu z tych kierunków: stosunkowo bezbolesne zmiany wdrożenia i dodatkowi nowi klienci.
Na przykład: Używając abstrakcji klasy Socket w Javie, jestem pewien, że mój kod aplikacji z Java 1.2 nadal działa dobrze w Javie 7 (chociaż mogą to być pewne zmiany wydajności). Od wersji Java 1.2 zdecydowanie było też wielu nowych klientów, którzy korzystali z tej abstrakcji.
Jeśli chodzi o niezadowolenie z abstrakcji, jeśli rozmawiałem z programistami, którzy utrzymywali kod za klasą Socket, to może ich życie nie jest tak brzoskwiniowe i różowe jak klienci, którzy używali Socket do pisania zabawnych aplikacji. Praca nad implementacją abstrakcji z pewnością wymaga więcej pracy niż jej użycia. Ale to nie jest złe.
Jeśli chodzi o przezroczystość, w odgórnej strategii projektowania całkowita przejrzystość powoduje złe projektowanie. Inteligentni programiści mają tendencję do maksymalnego wykorzystywania informacji, którymi dysponują, a następnie system jest ściśle powiązany. Najmniejsza zmiana szczegółów (np. Zmiana kolejności bajtów w strukturze danych) w module może złamać jakiś kod gdzie indziej, ponieważ inteligentny programista wykorzystał te informacje, aby zrobić coś pożytecznego. David Parnas wskazał na ten problem w artykułach sprzed 1971 roku, w których proponował ukrywanie informacji w projektach.
Twoje odniesienie do ArchLinux ma dla mnie sens, jeśli weźmiesz pod uwagę „wnętrze” systemu operacyjnego jako złożoną implementację abstrakcji, jaką jest system operacyjny dla aplikacji na nim działających. Prostota w trzewiach abstrakcji.
źródło
Odpowiem na twoje pytanie pytaniem; kiedy pojechałeś dziś rano do pracy (zakładam, że tak zrobiłeś), czy obchodziło cię dokładnie, jak silnik otworzył zawory, aby wpuścić mieszanki paliwowo-powietrzne, a następnie je zapalił? Nie. Nie obchodzi Cię, jak działa silnik twojego samochodu podczas jazdy w dół drogi. Dbasz, że ma pracę.
Załóżmy, że pewnego dnia twój samochód nie działa. Nie uruchamia się, rzuca pręt, zrywa pasek, niewytłumaczalnie wbija się w tę betonową barierę bez własnej winy, kiedy byłeś zajęty pisaniem wiadomości. Teraz potrzebujesz nowego samochodu (przynajmniej tymczasowo). Czy obchodzi Cię dokładnie, jak działa ten nowy samochód? Nie. Najważniejsze jest, aby po pierwsze działało, a po drugie, możesz korzystać z tej samej wiedzy i umiejętności, których używałeś do prowadzenia starego samochodu do prowadzenia nowego. Idealnie powinno ci się wydawać, że nie ma zmian w samochodzie, którym jeździsz. Realistycznie sposób, w jaki działa ten nowy samochód, powinien dać ci jak najmniej „niespodzianek”, jak to możliwe.
Te podstawowe zasady są podstawową zasadą enkapsulacji i abstrakcji. Wiedza o tym, jak obiekt robi to, co robi, nie powinna być wymagana do używania go do robienia tego, co robi. Nawet w programowaniu komputerowym szczegóły ścieżek elektrycznych w procesorze uruchomionym przez program są wyodrębnione za co najmniej kilkanaście warstw instrukcji I / O, sterowników, oprogramowania systemu operacyjnego i środowiska wykonawczego. Wielu bardzo udanych inżynierów oprogramowania pisze doskonale dobry kod, nie martwiąc się nawet o dokładną architekturę sprzętu, a nawet kompilację systemu operacyjnego, która go uruchomi. W tym mnie.
Ukrywanie enkapsulacji / informacji pozwala na mentalność „nie przejmuj się, jak to robi, tylko dbaj o to”. Twój obiekt powinien ujawniać to, co jest użyteczne dla konsumenta, w taki sposób, aby konsument mógł go łatwo konsumować. Teraz, w prawdziwym świecie, nie oznacza to, że samochód nie powinien przekazywać użytkownikowi żadnych informacji o wewnętrznych działaniach ani że samochód powinien umożliwiać użytkownikowi jedynie najbardziej podstawowe funkcje, takie jak zapłon, kierownica, i pedały. Wszystkie samochody mają prędkościomierze i wskaźniki paliwa, tachometry, światła idioty i inne informacje zwrotne. Praktycznie wszystkie samochody mają również przełączniki do różnych niezależnych podsystemów, takich jak reflektory, kierunkowskazy, radio, regulacja siedzeń itp. Niektóre samochody umożliwiają dość ezoteryczny wkład użytkownika, na przykład czułość środkowego mechanizmu różnicowego o ograniczonym poślizgu. We wszystkich przypadkach, jeśli wiesz wystarczająco dużo, możesz go otworzyć i zmienić, aby działał nieco inaczej. Ale w większości przypadków, być może, użytkownik może nie być w stanie bezpośrednio i niezależnie sterować pompami paliwa z kabiny? Może, być może, użytkownik nie powinien być w stanie włączyć swoich świateł hamowania bez faktycznego naciśnięcia pedału hamulca?
Abstrakcja pozwala na to, że „to nie to samo, ale ponieważ oba są XI, mogę używać ich tak, jak bym dowolną X”. Jeśli Twój obiekt dziedziczy lub implementuje abstrakcję, twoi klienci powinni oczekiwać, że twoja implementacja przyniesie taki sam lub podobny wynik jak inne znane implementacje abstrakcji. Toyota Camry i Ford Fusion to „samochody”. Jako takie mają one wspólny zestaw oczekiwanych funkcji, takich jak kierownica. Obróć w lewo, samochód jedzie w lewo. Obróć w prawo, samochód jedzie w prawo. Możesz wsiąść do dowolnego samochodu w Stanach Zjednoczonych i oczekiwać, że samochód będzie miał kierownicę i co najmniej dwa pedały, z których jeden po prawej to pedał „samochód jedzie”, a środkowy to pedał „samochód zatrzymuje się” .
Następstwem abstrakcji jest „teoria najmniejszego zdziwienia”. Jeśli zasiadłeś za kierownicą nowego samochodu na jazdę próbną, obróciłeś kierownicę zgodnie z ruchem wskazówek zegara, a samochód skręcił w lewo, byłbyś zaskoczony co najmniej. Oskarżyłbyś dealera o sprzedawanie punktów sprzedaży i mało prawdopodobne jest, aby usłyszał którykolwiek z jego powodów, dla których nowe zachowanie jest „lepsze” niż to, do czego jesteś przyzwyczajony, lub jak dobrze to zachowanie jest „udokumentowane” lub jak „ przejrzysty ”system sterowania. Pomimo tego, że nowy samochód i wszystkie inne, którymi jeździłeś, nadal są „samochodami”, prowadząc ten samochód, musisz zmienić kilka podstawowych koncepcji, w jaki sposób samochód powinien być prowadzony, aby z powodzeniem prowadzić nowy samochód. To zazwyczaj zła rzecz, i dzieje się tak tylko wtedy, gdy nowy paradygmat ma intuicyjną przewagę. Być może dodanie pasów bezpieczeństwa jest dobrym przykładem; 50 lat temu właśnie wsiadłeś i pojechałeś, ale teraz musisz się zapiąć, intuicyjną zaletą jest to, że nie przechodzisz przez przednią szybę lub do siedzenia pasażera w razie wypadku. Nawet wtedy kierowcy stawiali opór; wielu właścicieli samochodów odcina pasy bezpieczeństwa od samochodu, dopóki nie zostaną uchwalone przepisy nakazujące ich stosowanie.
źródło