Czy Dijkstra zamierzał modularyzować kod, pisząc o rozdzieleniu problemów?

9

Najpierw przeczytałem fragment artykułu Edsgera W. Dijkstry z 1974 r. „O roli myśli naukowej”:

Pozwól, że wyjaśnię ci, co według mojego gustu jest charakterystyczne dla każdego inteligentnego myślenia. Chodzi o to, że chce się dogłębnie przestudiować aspekt przedmiotu w oderwaniu dla własnej spójności, cały czas wiedząc, że zajmuje się tylko jednym aspektem. Wiemy, że program musi być poprawny i możemy go studiować tylko z tego punktu widzenia; wiemy również, że powinien on być skuteczny i że tak powiem, możemy zbadać jego skuteczność w innym dniu. W innym nastroju możemy zadać sobie pytanie, czy, a jeśli tak, to dlaczego program jest pożądany. Ale nic nie zyskuje - wręcz przeciwnie! - przez jednoczesne zajęcie się tymi różnymi aspektami. Jest to coś, co czasami nazywam „oddzieleniem obaw”, co nawet jeśli nie jest całkowicie możliwe, jest jeszcze jedyną dostępną techniką skutecznego uporządkowania własnych myśli, o której wiem. Mam na myśli to, że „skupiam uwagę na pewnym aspekcie”: nie oznacza to ignorowania innych aspektów, po prostu oddaje sprawiedliwość temu, że z punktu widzenia tego aspektu drugi jest nieistotny. Jest jednocześnie jedno- i wielościeżkowy.

Widzę współczesne oddzielenie problemów mówiących o modularyzacji twojego kodu. Jednak, czytając powyższy cytat, rozumiem, że skupiasz swój umysł na jednym konkretnym zadaniu na raz, nie koncentrując się jednak na innych aspektach. Nie oznacza to dla mnie koniecznie, że kod musi być podzielony na części modułowe.

To znaczy, powiedzmy, że przed tobą jest kod, który w jednym pliku zawiera pojęcia widoku, repozytorium, kontrolera, obsługi zdarzeń, fabryki itp. W jednym pliku.

Na krótki przykład oto kod, który ma dostęp do danych, i przeglądaj (dane wyjściowe):

$sql = "SELECT * FROM product WHERE id = " . db_input($id);
$row = db_fetch_array(db_query($sql)); 
<option value="<?=$row['id']?>"<?= $row['ver'] == $row['ver'] ? '  selected="selected"' : '' ?>>Version <?=$row['ver']?></option>

Korzystając z nowoczesnego OO, mogę umieścić dostęp do danych we własnym pliku przy użyciu wzorca repozytorium, kod View może przejść do własnego szablonu pliku i mogę połączyć je ze sobą, aby komunikować się za pośrednictwem kontrolera (lub działania lub procedury obsługi żądań), i mogę dodaj fabrykę, aby tworzyć i łączyć różne zależności. I mogę mieć plik konfiguracyjny, który definiuje te fabryki. Z pewnością jest to krok od pojedynczego pliku-wszystkiego.

Moje pytanie dotyczące rozdzielenia problemów jest takie: czytając cytat Dijkstry, wpadłem na pomysł, że być może niekoniecznie miał na myśli, że rozdzielenie obaw to „modułowe rozdzielenie kodu (na pliki lub ich własne funkcje / metody / itp.)”, i że tym bardziej chciał skupić umysł na aspekcie programu, nie obciążając się skupieniem na innych ważnych, ale jeszcze nieistniejących aspektach, niezależnie od tego, czy są one fizycznie rozdzielone w kodzie, czy nie.

Dlaczego zatem obciążamy się fizycznymi modułowymi wzorcami separacji kodów i wzorami projektowymi? Czy nie wystarczy skupić się na jakimś aspekcie, niezależnie od struktury kodu?

Nie mówię o napisaniu najstraszniejszego kodu spaghetti, a następnie rozważeniu tylko jego aspektu, który prawdopodobnie byłby ciężarem. Ale w końcu, do czego zmierzam, to po co przeprowadzać fizyczną separację kodu, po co dzielić kod na osobne pliki lub fragmenty (metody), skoro nie jest to konieczne do mentalnego skupienia się na aspekcie?

Czy rozdzielenie obaw powinno być ćwiczeniem umysłowym, a nie fizycznym?
Innymi słowy, czy powinno istnieć rozłączenie między mentalnym (skupienie się) a fizycznym (kod na papierze) aspektami programowania?

Dennis
źródło
5
Jestem prawie pewien, że do 1974 r. Po prostu widział programowanie modułowe jako oczywistą rzecz i dlatego nie omówił go wyraźnie w tym artykule. Papier Parnasa o jak zmodularyzowanie było w 1972 roku, a przez ten czas czy zmodularyzować już nie pytanie. W rzeczywistości to, co opisujesz, nie jest nawet programowaniem modułowym, jest to programowanie strukturalne , które sam Dijkstra zdecydowanie poparł już w 1968 r. W swoim klasycznym artykule „Go To Consified Harmful”.
Jörg W Mittag
w porządku, więc być może bardziej rozumiem „rozdzielenie problemów” jako ćwiczenie koncentracji umysłowej, a modularyzację jako sposób na enkapsulację aspektu kodu na papierze. Jednak teraz widzę rozdzielenie problemów i modularyzację bardziej jako osobne pojęcia.
Dennis
@ JörgWMittag, czy możesz zdefiniować różnicę między programowaniem strukturalnym a modułowym? Niektóre linki w Google sugerują, że są takie same.
Dennis
Skonstruowany = IF, WHILE, FORa nie GOTO. Modułowy = moduły z dobrze zdefiniowanym publicznym interfejsem API ściśle oddzielonym od ukrytej wewnętrznej implementacji i reprezentacji. (Np. Modula, Mesa, Modula-2, Modula-3, później dialekty Pascala ( UNIT).)
Jörg W Mittag

Odpowiedzi:

2

Dijkstra zawiera wyraźne oświadczenie dotyczące sposobu myślenia. Modularyzacja programu (i procesu) - i jest pożądana - jest być może wynikiem takiego myślenia, ale kluczową kwestią, którą porusza, jest ocena problemu. Program jest zazwyczaj rozwiązaniem problemu, a zalecając „rozdzielenie obaw”, udziela mądrych rad. Najlepszym tego przykładem jest być może „optymalizacja”. Żart brzmiał: „Planując optymalizację programu, twoją pierwszą strategią powinno być: nie.” Innymi słowy, chcesz skupić się na poprawieniu programu. Sprawienie, by było szybkie i fantazyjne, jest problemem, który należy oddzielić - ale także nie do końca usunąć.

MCL
źródło
14

Rozdzielanie obaw jest abstrakcyjnym sposobem myślenia, polegającym na oddzielnym rozpatrywaniu rzeczy, które nie muszą być ze sobą powiązane.

Modularyzacja (rozdzielenie niepowiązanej grupy funkcji na moduły), enkapsulacja (ukrywanie wewnętrznych szczegółów modułów) i abstrakcja (oddzielenie generała od specyfiki i idei od jego implementacji) to wszystkie sposoby na wdrożenie tego sposobu myślenia w dziedzinie projektowania oprogramowania.

Christophe
źródło
9

Sugerowałbym, że chociaż artykuł jest historycznie interesujący, to, co Dijkstra rozumiał przez termin „rozdzielenie obaw” ponad 40 lat temu, nie jest dziś szczególnie istotne. Obecnie jest szeroko stosowany w odniesieniu do modulacji.

Istnieje wiele dowodów na to, że modulacja jest niezwykle korzystna i że korzyści te znacznie przewyższają „obciążenia”, które na nas nakładają. Cokolwiek wtedy miał na myśli Dijkstra, nie zmienia faktu, że małe fragmenty kodu, z których każdy koncentruje się na jednej rzeczy, prowadzi do kodu, który jest łatwiejszy do pisania, czytania, rozumienia, utrzymywania i testowania.

David Arno
źródło
5
Myślę, że należy zauważyć, że wiele współczesnych przemyśleń na temat separacji obaw pochodziło z wczesnych artykułów, które ostatecznie zrodziły programowanie zorientowane na aspekt od IBM. Myślę, że początkowe prace (z końca lat 90. i na początku 2000 r.) Mają tam największy wpływ. Minęło trochę czasu i wszystkie strony się zmieniły. Nie jestem pewien, czy uda mi się je jeszcze znaleźć.
Berin Loritsch,
2
Ciężka część polega na zdefiniowaniu, co oznacza „jedna rzecz”. Bez tego pomysł jest bezużyteczny pod względem praktycznego pisania kodu, a popełnienie błędu może natychmiast negatywnie wpłynąć na trudność pisania, czytania, rozumienia, utrzymywania i testowania kodu.
jpmc26
1
Naprawdę pomogłoby nam zrozumieć twoje stanowisko, gdybyś (a) wyjaśnił, co według Ciebie Dijkstra rozumie przez „rozdzielenie obaw” i (b) wyjaśnił, dlaczego uważasz, że to, co miał na myśli, nie jest już istotne.
John R. Strohm,
2

Mogę podać osobisty przykład rozdzielenia obaw, który moim zdaniem jest porównywalny z koncepcjami Dijkstry. Kiedy analizuję określony temat w oprogramowaniu, tworzę trzy poglądy.

  1. Najpierw rozważam dane. Dane reprezentują logiczne predykaty problemu. Klasy są konstruowane do bytów abstrakcyjnych w świecie rzeczywistym, a ich atrybutami są parametry abstrakcji. Powiązania między klasami reprezentują mapowania funkcjonalne między instancjami klas. W tym momencie nie ma żadnego kodu związanego z myśleniem ani żadnego przetwarzania. Tylko statyczny widok logiki związanej z tematem.
  2. Po drugie, rozważam dynamikę. Każda klasa, która ma nietrywialny cykl życia, jest modelowana jako skończona maszyna stanu. Wymaga to rozważenia wykonania i synchronizacji sekwencji. Ponownie, żaden kod po prostu nie pracuje nad tym, w jaki sposób rzeczy współdziałają i porządkują.
  3. Po trzecie, rozważam przetwarzanie. Tutaj rzeczywista praca algorytmiczna, którą należy wykonać przy przejściu stanu lub w innych operacjach synchronicznych.

Na koniec uzyskuje się trójwymiarowy obraz przedmiotu, który można następnie sformułować jako kod w dowolnych grupach dogodnych dla samego kodu i jego utrzymania. Trzy aspekty to nie tylko ćwiczenie mentalne. Tworzę pisemne opisy wszystkich aspektów. Dlaczego? Ponieważ jeśli przedmiot jest wystarczająco duży, nie mogę zatrzymać nawet jednego kompletnego aspektu w pamięci krótkotrwałej. Jeśli przedmiot jest niewielki, prawie każde podejście zadziała, ponieważ możesz trzymać to wszystko w głowie.

Motywacją do rozdzielenia problemów jest dostosowanie się do ograniczeń pamięci krótkoterminowej u ludzi. Po prostu nie możemy nosić wszystkiego w naszych głowach naraz, chociaż programiści komputerowi są zazwyczaj bardziej zdolni niż większość innych ludzi pod względem liczby pojęć, którymi mogą manipulować w swojej pamięci krótkoterminowej. Aby być skutecznym, należy rozdzielać obawy systematyczniewyklucz jeden lub więcej aspektów problemu, aby skupić się na innym konkretnym aspekcie. Oczywiście wykluczenie jednego aspektu nie powoduje, że znika ono z rozważań. Musi istnieć sposób na połączenie wszystkich aspektów problemu, aby osiągnąć rozwiązanie. Doświadczenie pokazuje, że często końcowy wynik rozdzielania i rekombinacji daje bardziej zrozumiałe rozwiązanie niż pojedynczy gigantyczny skok, w którym wiele aspektów może zostać pomieszanych. Jest to szczególnie ważne, gdy rozmiar problemu jest duży lub skomplikowany.

Andy Mango
źródło
1

Rozdzielenie problemów jest logiczną koncepcją, która zostanie rozpowszechniona w modelu organizacji kodu bez względu na to, jak ją zaimplementujesz. Prawdą jest, że plik kodu jest tylko szczegółem technicznym, jednym ze sposobów na zarządzanie oprogramowaniem. Pojedynczy plik z dobrym edytorem, który pozwala zwinąć rozszerzenie regionów, może również działać (na chwilę). Lub relacyjna baza danych, która przechowuje klasy i metody w sposób nadrzędny-podrzędny w osobnych tabelach, może działać jako nośnik pamięci. Ale pliki tekstowe są trudne do pokonania w świecie, w którym musi znajdować się kod źródłowy

  • przenośny
  • dostępne przez wiele różnych zewnętrznych narzędzi
  • dostępne dla wielu programistów
  • z możliwością aktualizacji i porównywalności
  • dobrze skaluj w systemach operacyjnych, które okazują się bardzo dobre w obsłudze plików

Najważniejsze jest to, że my, ludzie, nie jesteśmy zbyt dobrzy w myśleniu lub radzeniu sobie z różnymi rzeczami na raz. Potrzebujemy więc modelu, który pozwala myśleć i pracować nad jedną rzeczą naraz, bez niebezpieczeństwa zrujnowania innej części, której w tym czasie nie rozważamy. Budujemy więc, układając po jednej cegle na raz, upewniając się, że cegły, które układamy wcześniej, nie będą kolidować z cegłami ustawionymi później. A jeśli chcemy później zmienić cegłę, rzeczy nie mogą się zawalić. To model, który działa na nasze jednośladowe umysły.

Nie tak rosną grzyby i glony ... Jak to jest z upokarzającego faktu?

Martin Maat
źródło
-1

Uważam jednak, że odpowiedziano na konkretną odpowiedź na cytat Dijkstry, ponieważ stwierdzasz: „Korzystając z nowoczesnego OO, mogę umieścić dostęp do danych we własnym pliku” i pytasz: „Czy rozdzielenie obaw powinno być ćwiczeniem umysłowym, a nie fizycznym?” Pozwólcie, że zwrócę uwagę na to, dokąd kierują nas nowi zleceniodawcy.

Należy postępować zgodnie z zasadami SOLID przy programowaniu za pomocą OO. Oto fajny link do nich, ale TLDR na temat „rozdzielenia problemów” znajduje się głównie w S w SOLID: Zasada pojedynczej odpowiedzialności lub SRP.

Zdecydowanie jest to ćwiczenie fizyczne, a nie mentalne. Na przykład MVC (lub jego rodzeństwo MVVM i MVP) nakazuje fizyczny podział koncertów Model, View i Controller / Presenter / ViewModel na osobne pliki. Widziałem niektóre implementacje MVVM, w których są one implementowane w oddzielnych zespołach, aby jeszcze bardziej ograniczyć skłonność do „pomieszania koncepcji”.

Ale. Wykracza poza zwykłe „to jest widok i to jest model”, jeśli podążysz za tym wujkiem Bobem .

Należy również wziąć pod uwagę źródło wymagań dla każdego konkretnego elementu OO. Jeśli łączysz, powiedzmy, czego chce Klient z tym, czego chce Personel Operacyjny, naruszasz również SRP. Lub, mówiąc inaczej, jak wujek Bob: Klasa powinna mieć jeden i jedyny powód do zmiany.

Zdecydowanie zalecamy, abyś przestudiował to dalej za pomocą dostarczonych linków lub przeszukał „solidne zasady”.

Reginald Blue
źródło
Nie. Zasady SOLID są do tej pory oderwane od rzeczywistości pisania kodu (= filozoficzne), że można je zrozumieć w jakikolwiek zdalnie użyteczny sposób, gdy już wiesz, jak pisać dobry kod, w którym to momencie są one w najlepszym wypadku zbędne. Używanie ich jako zasad przewodnich bez doświadczenia i umiejętności powoduje wyjątkowo słaby kod.
jpmc26,