Rozszerzalny kod

Rozszerzalny kod
PI
  • Rejestracja:ponad 9 lat
  • Ostatnio:3 miesiące
  • Postów:2787
0

Siema

Każdy wie, że trzeba pisać kod, który jest rozszerzalny / extensible, w końcu to oczywiste... no i właśnie im dłużej o tym myślę, tym bardziej się utwierdzam w przekonaniu, że ja takiego pisać nie umiem.

Teorię powiedzmy że znam - kod powinien być otwarty na rozszerzenia, czyli żeby drugi programista mógł dodać jakąś rzecz bez zmieniania obecnego kodu. No i właśnie, co tak naprawdę, odwołując się do konkretnego kodu, to znaczy?? Przecież, koniec końców, gdzieś ta zmiana musi być - niżej lub wyżej w stosie wywołań metod, no ale chyba musi być...

Załóżmy, że mamy sklep internetowy, i można w nim oprócz dodawania produktu i jego kupna, również ocenić produkt, wystawić mu recenzję. I jak tu napisać jakiś serwis do wystawiania recenzji danego produktu, żeby był rozszerzalny?

Jakie konkretne techniki w javie możemy zastosować? Interfejsy? Ale koniec końców i tak się gdzieś podaje konkretną implementację, więc w tamtym miejscu i tak trzeba będzie zmienić. Dziedziczenie i korzystanie z klasy bazowej? Nadal chyba gdzieś ta zmiana musiałaby być poczyniona.

A jak już dołożymy do tego enterprise standardy, jak np JPA (no jest słabe, ale jestem do niego najbardziej przyzwyczajony) i ostatecznie wychodzi mi taka aplikacja, gdzie załóżmy podział pakietowy mam nawet spoko, ale nadal w każdym pakiecie mam jakiś kontroler, jakiś dto, jakiś mapper... czy to jest extensible code?

99xmarcin
Z jednej strony rozszerzalny z drugiej YAGNI i KISS, jak nie robię rozszerzalności jak nie muszę i dobrze na tym wychodzę. Przyszłości nie przewidzisz...
jarekr000000
  • Rejestracja:ponad 8 lat
  • Ostatnio:21 minut
  • Lokalizacja:U krasnoludów - pod górą
  • Postów:4707
7
Pinek napisał(a):

Siema

Każdy wie, że trzeba pisać kod, który jest rozszerzalny / extensible, w końcu to oczywiste...

Otóż nie. Nie każdy wie. Ja nie wiem. I nie uważam, że trzeba pisać kod rozszerzalny.
Trzeba pisać kod przystosowany do zmian (utrzymywalny).

Wydaje mi się, że 15 lat temu to też (rozszerzalność FTW) było dla mnie oczywiste. Teraz nie wiem nawet czemu.


jeden i pół terabajta powinno wystarczyć każdemu
edytowany 2x, ostatnio: jarekr000000
AF
  • Rejestracja:prawie 18 lat
  • Ostatnio:18 dni
5

Tu nie chodzi o kompletny brak zmian, tylko o wymianę implementacji w kilku łatwo kontrolowanych miejscach, na przykład w kontenerze DI lub konfiguracji. Przydają się interfejsy, strategie, fabryki itp.

Co do oczywistości pisania takiego kodu, to moim zdaniem lepiej pisać kod, który da się łatwo i szybko przepisać bez orania całej aplikacji, czyli jakieś sensowne komponenty itp. Najlepszy jest kod, który można łatwo wyrzucić i zastąpić nowym, to bardzo upraszcza ciągle dyskusje o nazwach zmiennych i jakości testów.

Charles_Ray
  • Rejestracja:około 17 lat
  • Ostatnio:około 8 godzin
  • Postów:1873
1

Pytanie podstawowe brzmi: rozszerzalny w jakim kierunku? Polecam lekturę https://www.thoughtworks.com/books/building-evolutionary-architectures

W teorii, aby przygotować kod na rozszerzenia, trzeba znać plan jego rozwoju. Przykład. Dawno temu projektowałem model danych pod sameday delivery, przy czym współpracowaliśmy tylko z jednym przewoźnikiem. Podjęliśmy świadomą decyzję, że robimy model zamknięty na rozszerzenia, ponieważ tak będzie szybciej plus nie znamy domeny i nie wiemy w jakim kierunku miałby się rozszerzać - okienka doręczenia, godziny podjazdów etc. W końcu przez jeden punkt przebiega nieskończenie wiele prostych, a mnie uczono, żeby zawsze takich punktów mieć co najmniej 3 :)


”Engineering is easy. People are hard.” Bill Coughran
edytowany 1x, ostatnio: Charles_Ray
UR
  • Rejestracja:prawie 5 lat
  • Ostatnio:prawie 3 lata
  • Postów:360
6

Dopóki nie piszemy jakiegoś API, od którego zależny jest kod klientów, to raczej każda próba to masturbowania się z Open Closed Principle NA SIŁĘ łamie YAGNI i zazwyczaj potem kończy się tym, że tworzone są interfejsy z jedną konkretną implementacją, a unit testy to testy mocków :)

edytowany 1x, ostatnio: urke
RE
  • Rejestracja:prawie 5 lat
  • Ostatnio:ponad 4 lata
  • Postów:49
5

Zapomniałeś o tym co jest wczesniej, czyli separacja.

Musisz miec wczesniej odseparowane kawałki kodu, o to warto się bić, zwłaszcza, że taka rzecz stosunkowo łatwo przychodzi jak stosujesz się do DRY. Separacja to dobra rzecz niezależnie od języka i pomaga zepchnąć szczegóły na dalszy plan. Praca wymaga na ogół skupienia, a skupienie najłatwiej idzie utrzymać gdy w kodzie zajmujesz się jedną rzeczą na raz.

Niestety nie na długo się nacieszysz, po pewnym czasie okazuje się, że masz pewną pulę podobnych operacji, albo że musisz te operacje stosować zamiennie i ogólnie siłą rzeczy dochodzi do pewnych powtórzeń, czasem do tego etapu dochodzisz szybciej (barokowy język) czasem wolniej (język z makrami).

No i tutaj możesz możesz próbować sprowadzać temat do wspólnego interfejsu, klasy bazowej - co wymaga doświadczenia, bo najlepsze interfejsy / klasy bazowe pozwalają na mało operacji. Dzięki temu bronią się dłużej przed złamaniem kompatybilności. W każdym razie ten etap to jest moment, gdy intensywnie korzystasz z OOP lub FP.

I już na tym etapie uzyskujesz rozszerzalny kod. Co zyskujesz jak masz rozszerzalny kod? W zasadzie to mnie cieszy fakt, że jak pomysł z implementacja nie wypali to usunięcie kodu nie będzie wymagalo wiele prac. Po prostu odpinam implementacje, i na jej miesce wstawiam coś innego. Taki łatwiej można dostosować do zmieniających się okoliczności.

Jak zrobisz krok dalej, czyli odseparujesz od siebie rzeczy, które da się ze sobą składać (compose) wtedy wychodzą Ci wzorce.

WSKAZÓWKI:

  1. Nie twórz złożonych klas / intefejsów - takie ściągają zło na kod i one obrywają pierwsze, gdy klient zmieni wymagania.

  2. Opóźniaj tworzenie rozszerzalnego kodu, ponieważ brak świadomości o dziedzinie skutkować będzie niekompletnym interfejsem. Poza tym jak masz 2-3 podobne przypadki to jeszcze nie jest powód, aby wydzielać wspólny interfejs. Pozwól im leżakować, poczekaj aż sytuacja bardziej się skomplikuje i wtedy w ramach refaktoryzacji można odpowiedzieć w tym kierunku który najbardziej boli - wtedy będziesz mieć więcej wiedzy.

  3. Jak rozszerzalność przesuniesz na dalszy plan to szybciej bedziesz kodował, może mniej perfekcyjnie, ale za to skutecznie.

EDIT:

PS, warto zwrócić uwagę, że rozszerzalność ogólnie ładnie prezentuje się na przykładzie bibliotek / frameworków. Dzieje się tak, że tam wymagania rzadko się drastycznie się zmieniają. Jeśli biblioteka nie pozwala na jakąś operacje no to cóż.. szuka się innej biblioteki.

Natomiast w przypadku biznesu i produktów zmana wymagań może być nawet o 180' ponieważ wraz z tą zmianą chodzi o hajs, z resztą o to chodzi od samego początku więc taka rzecz nie powinna dziwić.

edytowany 1x, ostatnio: ret
SA
  • Rejestracja:około 12 lat
  • Ostatnio:około 2 godziny
  • Postów:1431
2
ret napisał(a):

PS, warto zwrócić uwagę, że rozszerzalność ogólnie ładnie prezentuje się na przykładzie bibliotek / frameworków. Dzieje się tak, że tam wymagania rzadko się drastycznie się zmieniają. Jeśli biblioteka nie pozwala na jakąś operacje no to cóż.. szuka się innej biblioteki.

No właśnie nie. To skopane biblioteki, których twórcy nie rozumieją OCP są takie, że trzeba szukać innych. Otwarty na rozszerzenia to nic innego jak możliwość dopisania brakującego kawałku kodu, który robi to czego oczekuję bez potrzeby modyfikowania biblioteki (zamknięty na modyfikacje).

RE
Wszędzie są jakieś limity, i też nie każdy ma czas, aby opatrzeć rozszerzalność tam gdzie to wskazane więc zgodzę się z Tobą, nie jest tak pięknie, ale gdy mam do porównania typowy kod projektu, a typowy kod biblioteki to bezkonkurencyjnie bardziej pomyślane są biblioteki.
SA
Jeśli ludzie nie mają OCP na względzie to tylko zatruwają życie nierozszerzalnym kodem. Chyba łatwiej dopisać override lub implementacje interfejsu niz zakładać issue i czekać na zmianę? A biblioteki mogą być też wewnatrzfirmowe i ich kodu też bezpośrednio nie kontrolujemy.
S9
  • Rejestracja:ponad 10 lat
  • Ostatnio:5 miesięcy
  • Lokalizacja:Warszawa
  • Postów:3573
4

Dopóki nie piszemy jakiegoś API, od którego zależny jest kod klientów, to raczej każda próba to masturbowania się z Open Closed Principle NA SIŁĘ
W teorii, aby przygotować kod na rozszerzenia, trzeba znać plan jego rozwoju.

Dokładnie !
Założmy na przykład że jest aplikacja bankowa w której robimy eksport histori do plików.
Teraz drogi są dwie:
1)Zakładamy że będzie to tylko PDF i tyle. Tworzymy kawałek kodu który nie polega na abstrakcjach i jest jakaś klasa/zestaw funkcji eksportujących do PDF, koniec.
2)Zakładamy że teraz to PDF i CSV, ale później ewentualnie może coś dojść. Robimy wtedy interfejs i np. enum jak ReportFileType. Można wtedy przekazac mapę Map<ReportFileType, ReportGenerator> albo zrobić np. ReportGenerator z metodą getSupportedFileType i wstrzyknąć kolekcje implementacji przez konstruktor. Warto pamiętać że kontenery IoC w żaden nsposób nie sa wymagane do tego :)


"w haśle <młody dynamiczny zespół> nie chodzi o to ile masz lat tylko jak często zmienia się skład"
RE
  • Rejestracja:prawie 5 lat
  • Ostatnio:ponad 4 lata
  • Postów:49
5

Z książki adaptywny kod, tak w ogóle spoko książka; mimo, ze prawi o SOLID to znaleźć w niej można uwagi odnoszące się racji technik i ryzyka z ich użyciem, oto jeden z paru ciekawych przypadków jaki został tam omówiony:

Początkujący programiści mają tendencję do tworzenia proceduralnego kodu, nawet w językach obiektowych takich jak C#. Wykorystują klasy w charakterze magazynów metod niezależnie od tgo czy metody te są ze sobą powiązane, czy nie.

(...)

Całkowitym przeciwieństwem jest programista, który od samego początku tworzy interfejsy. Odkrywa, że ma w ręce młotek, i wszystko kojarzy mu się z gwoździem. Utworzony przez niego kod jest naszpikowany punktami rozserzeń, z których więszkosć nigdy nie będzie wykorzystana. Napisanie takiego kodu, delegującego wszystkie operacje do interfejsów wymaga sporego nakładu, podobnie jak zrozumienie działania tego kodu.

(...)

Gdyby z tych dwóch skrajnie różniących się programistów zrobić jednego, wtedy utworzony przez niego kod byłby harmonijnym kompromisem zawierajacym odpowiednią liczbę punktów rozszerzeń i przystosowanym do wprowadzania zmian jedynie w funkcjonalnościach, które są niejasne, zmienne i trudne do zaimplementowania. Do tego jednak potrzebne jest doświadczenie

Kierunek: niejasne, zmienne i trudne do zaimplementowania i nagle z 50 przypadków pozostaje 3-5, które są elementem strategii w rozwoju projektu.

A jak ktoś opakowuje banały w setki klas, to ja się nie dziwie, że potem jest przerost formy nad trescią.

edytowany 2x, ostatnio: ret
Korges
  • Rejestracja:prawie 5 lat
  • Ostatnio:około 17 godzin
  • Postów:555
3

Ja rozszerzalny kod rozumiem tak:

Kopiuj
public interface ItemReader {
    List<Tuple> read();
}
Kopiuj
public interface Strategy {
    Integer apply(List<Tuple> tupleList);
}
Kopiuj
public interface FacadeService {
    void result();
}
Kopiuj
public class FacadeServiceImpl implements FacadeService {
    private final ItemReader itemReader;
    private final Strategy strategy;

    public FacadeServiceImpl(ItemReader itemReader, Strategy strategy) {
        this.itemReader = itemReader;
        this.strategy = strategy;
    }

    @Override
    public void result() {
        Integer result = strategy.apply(itemReader.read());
    }
}
Kopiuj
public class Main {

    public static void main(String[] args) {
        new FacadeServiceImpl(
                new FlatFileItemReader(args[0]),
                new DisjointCompartmentsStrategy()
        ).result();
    }

}

Możesz stworzyć dowolną implementację spełniającą kontrakt założony przez twórcę interfejsów no i potem ją wykorzystać.

edytowany 2x, ostatnio: Korges
PI
bardzo ładny przykład
Grzyboo
A potem cała aplikacja zarzygana single implementation potworkami.
LukeJL
  • Rejestracja:około 11 lat
  • Ostatnio:4 minuty
  • Postów:8408
4

trzeba pisać kod, który jest

Nie trzeba. Trzeba to wyczuć moment, kiedy pisać coś w sposób rozszerzalny czy elastyczny, a kiedy jednak zrobić nieodporny na zmiany monolit (przy czym w jednym projekcie możesz mieć i taki i taki kod).

A przesada w każdą stronę jest zła. Bo raz napiszesz kompletnie nieelastyczny kod i będziesz cierpiał, bo zmienią się wymagania. Innym razem będziesz chciał być mądrzejszy i napiszesz kod tak super elastyczny, że potem sam się pogubisz w abstrakcjach, plus będziesz go robił 5 razy dłużej. Wtedy będziesz widział, że przesadziłeś.

KISS i YAGNI są trudne, bo wymagają oduczenia się pewnych naleciałości. Człowiek ileś lat się uczy, że mają być dobre praktyki i kod elastyczny itp. itd. a potem się okazuje, że KISS i YAGNI. Trzeba wejść na metapoziom i myśleć biznesowo czy strategicznie (co się lepiej opłaci). Też trzeba się cofnąć w rozwoju trochę i zamiast barokowych abstrakcji napisać po prostu skromnie kod, który coś robi.

Ale tak jak było wspomniane już - prostota API, izolacja modułów, a potem refaktoring w razie potrzeby.


edytowany 3x, ostatnio: LukeJL
czysteskarpety
czysteskarpety
  • Rejestracja:prawie 10 lat
  • Ostatnio:ponad 4 lata
  • Lokalizacja:Piwnica
  • Postów:7697
1

Z rozszerzalnością to jak z backupem, niby każdy wie, ale nie każdy robi ;)


Kliknij, aby dodać treść...

Pomoc 1.18.8

Typografia

Edytor obsługuje składnie Markdown, w której pojedynczy akcent *kursywa* oraz _kursywa_ to pochylenie. Z kolei podwójny akcent **pogrubienie** oraz __pogrubienie__ to pogrubienie. Dodanie znaczników ~~strike~~ to przekreślenie.

Możesz dodać formatowanie komendami , , oraz .

Ponieważ dekoracja podkreślenia jest przeznaczona na linki, markdown nie zawiera specjalnej składni dla podkreślenia. Dlatego by dodać podkreślenie, użyj <u>underline</u>.

Komendy formatujące reagują na skróty klawiszowe: Ctrl+B, Ctrl+I, Ctrl+U oraz Ctrl+S.

Linki

By dodać link w edytorze użyj komendy lub użyj składni [title](link). URL umieszczony w linku lub nawet URL umieszczony bezpośrednio w tekście będzie aktywny i klikalny.

Jeżeli chcesz, możesz samodzielnie dodać link: <a href="link">title</a>.

Wewnętrzne odnośniki

Możesz umieścić odnośnik do wewnętrznej podstrony, używając następującej składni: [[Delphi/Kompendium]] lub [[Delphi/Kompendium|kliknij, aby przejść do kompendium]]. Odnośniki mogą prowadzić do Forum 4programmers.net lub np. do Kompendium.

Wspomnienia użytkowników

By wspomnieć użytkownika forum, wpisz w formularzu znak @. Zobaczysz okienko samouzupełniające nazwy użytkowników. Samouzupełnienie dobierze odpowiedni format wspomnienia, zależnie od tego czy w nazwie użytkownika znajduje się spacja.

Znaczniki HTML

Dozwolone jest używanie niektórych znaczników HTML: <a>, <b>, <i>, <kbd>, <del>, <strong>, <dfn>, <pre>, <blockquote>, <hr/>, <sub>, <sup> oraz <img/>.

Skróty klawiszowe

Dodaj kombinację klawiszy komendą notacji klawiszy lub skrótem klawiszowym Alt+K.

Reprezentuj kombinacje klawiszowe używając taga <kbd>. Oddziel od siebie klawisze znakiem plus, np <kbd>Alt+Tab</kbd>.

Indeks górny oraz dolny

Przykład: wpisując H<sub>2</sub>O i m<sup>2</sup> otrzymasz: H2O i m2.

Składnia Tex

By precyzyjnie wyrazić działanie matematyczne, użyj składni Tex.

<tex>arcctg(x) = argtan(\frac{1}{x}) = arcsin(\frac{1}{\sqrt{1+x^2}})</tex>

Kod źródłowy

Krótkie fragmenty kodu

Wszelkie jednolinijkowe instrukcje języka programowania powinny być zawarte pomiędzy obróconymi apostrofami: `kod instrukcji` lub ``console.log(`string`);``.

Kod wielolinijkowy

Dodaj fragment kodu komendą . Fragmenty kodu zajmujące całą lub więcej linijek powinny być umieszczone w wielolinijkowym fragmencie kodu. Znaczniki ``` lub ~~~ umożliwiają kolorowanie różnych języków programowania. Możemy nadać nazwę języka programowania używając auto-uzupełnienia, kod został pokolorowany używając konkretnych ustawień kolorowania składni:

```javascript
document.write('Hello World');
```

Możesz zaznaczyć również już wklejony kod w edytorze, i użyć komendy  by zamienić go w kod. Użyj kombinacji Ctrl+`, by dodać fragment kodu bez oznaczników języka.

Tabelki

Dodaj przykładową tabelkę używając komendy . Przykładowa tabelka składa się z dwóch kolumn, nagłówka i jednego wiersza.

Wygeneruj tabelkę na podstawie szablonu. Oddziel komórki separatorem ; lub |, a następnie zaznacz szablonu.

nazwisko;dziedzina;odkrycie
Pitagoras;mathematics;Pythagorean Theorem
Albert Einstein;physics;General Relativity
Marie Curie, Pierre Curie;chemistry;Radium, Polonium

Użyj komendy by zamienić zaznaczony szablon na tabelkę Markdown.

Lista uporządkowana i nieuporządkowana

Możliwe jest tworzenie listy numerowanych oraz wypunktowanych. Wystarczy, że pierwszym znakiem linii będzie * lub - dla listy nieuporządkowanej oraz 1. dla listy uporządkowanej.

Użyj komendy by dodać listę uporządkowaną.

1. Lista numerowana
2. Lista numerowana

Użyj komendy by dodać listę nieuporządkowaną.

* Lista wypunktowana
* Lista wypunktowana
** Lista wypunktowana (drugi poziom)

Składnia Markdown

Edytor obsługuje składnię Markdown, która składa się ze znaków specjalnych. Dostępne komendy, jak formatowanie , dodanie tabelki lub fragmentu kodu są w pewnym sensie świadome otaczającej jej składni, i postarają się unikać uszkodzenia jej.

Dla przykładu, używając tylko dostępnych komend, nie możemy dodać formatowania pogrubienia do kodu wielolinijkowego, albo dodać listy do tabelki - mogłoby to doprowadzić do uszkodzenia składni.

W pewnych odosobnionych przypadkach brak nowej linii przed elementami markdown również mógłby uszkodzić składnie, dlatego edytor dodaje brakujące nowe linie. Dla przykładu, dodanie formatowania pochylenia zaraz po tabelce, mogłoby zostać błędne zinterpretowane, więc edytor doda oddzielającą nową linię pomiędzy tabelką, a pochyleniem.

Skróty klawiszowe

Skróty formatujące, kiedy w edytorze znajduje się pojedynczy kursor, wstawiają sformatowany tekst przykładowy. Jeśli w edytorze znajduje się zaznaczenie (słowo, linijka, paragraf), wtedy zaznaczenie zostaje sformatowane.

  • Ctrl+B - dodaj pogrubienie lub pogrub zaznaczenie
  • Ctrl+I - dodaj pochylenie lub pochyl zaznaczenie
  • Ctrl+U - dodaj podkreślenie lub podkreśl zaznaczenie
  • Ctrl+S - dodaj przekreślenie lub przekreśl zaznaczenie

Notacja Klawiszy

  • Alt+K - dodaj notację klawiszy

Fragment kodu bez oznacznika

  • Alt+C - dodaj pusty fragment kodu

Skróty operujące na kodzie i linijkach:

  • Alt+L - zaznaczenie całej linii
  • Alt+, Alt+ - przeniesienie linijki w której znajduje się kursor w górę/dół.
  • Tab/⌘+] - dodaj wcięcie (wcięcie w prawo)
  • Shit+Tab/⌘+[ - usunięcie wcięcia (wycięcie w lewo)

Dodawanie postów:

  • Ctrl+Enter - dodaj post
  • ⌘+Enter - dodaj post (MacOS)