author-avatar
Dominik Martyniak
Wzorzec projektowy 6 minut

Praktyczne wykorzystanie Wzorca Strategia w Javie

Wzorzec projektowy Strategia to jedna z kluczowych technik programistycznych i jednocześnie jeden z moich ulubionych, który wiele razy okazał się pomocny w sytuacjach, gdzie napotykałem powtarzający się kod lub gdy chciałem stworzyć funkcjonalność, która nie wymagała od przyszłych programistów zgadywania, co kryje się pod spodem metody. Wzorzec ten pozwala na odpowiednie wywołanie różnych operacji, wykorzystując dostarczony typ interfejsu.

 

Krótka i formalna informacja co to jest Wzorzec Strategii

 

Wzorzec strategii w programowaniu Java jest behawioralnym wzorcem projektowym, który umożliwia zdefiniowanie rodziny algorytmów lub strategii, umożliwiając zmianę ich implementacji niezależnie od kodu klienta, który ich używa. Wzorzec ten pomaga w unikaniu nadmiernego kodu powtarzającego się oraz pozwala na elastyczną zmianę zachowania komponentów w trakcie działania programu.



Zalety Wykorzystania Wzorca Strategii

 

  • Rozdzielenie Odpowiedzialności: Wzorzec Strategii pomaga oddzielić różne algorytmy od głównej logiki aplikacji, co ułatwia zarządzanie kodem i jego rozwijanie.
  • Łatwa Rozszerzalność: Dodawanie nowych strategii płatności lub algorytmów jest prostsze, ponieważ nie ma konieczności modyfikowania istniejącego kodu.
  • Czytelność i Utrzymanie: Kod staje się bardziej zrozumiały, ponieważ logika związana z różnymi strategiami jest izolowana w osobnych klasach.
  • Testowanie: Możliwość testowania poszczególnych strategii płatności niezależnie od siebie ułatwia weryfikację poprawności kodu.

 

Let's code strategy

 

Zakładając, że w ramach naszej dziedziny biznesowej zajmujemy się obsługą przypadków, naszym zadaniem jest odczytanie plików CSV pochodzących z różnych banków, a następnie przetworzenie ich do formy zgodnej z naszą dziedziną biznesową. Ten proces przygotowuje dane do dalszej obróbki lub zapisu w bazie danych.

 

Na samym początku stwórzmy sobie DocReaderStrategyFactory, jest to fabryka która umożliwi nam wybór odpowiedniej strategii zależnie od typu który wskażemy. 

 

@Component
public class DocReaderStrategyFactory {

    private final Set<DocReaderStrategy> strategies;

    public DocReaderStrategyFactory(Set<DocReaderStrategy> strategySet) {
        this.strategies = strategySet;
    }

    public Optional<DocReaderStrategy> findStrategyFor(DocBankType type) {
        return strategies.stream()
                .filter(strategy -> strategy.isSuitableFor(type))
                .findFirst();
    }
}

 

Wewnątrz klasy mamy kolekcję strategii DocReaderStrategy, a konstruktor spełnia rolę wzbogacania tej kolekcji poprzez dodawanie obiektów (beany), które implementują dany interfejs. Ponadto, klasa posiada metodę findStrategy, która umożliwia wybór odpowiedniej strategii na podstawie określonego typu. Zastosowanie obiektu Optional pozwala na elastyczną obsługę przypadku, gdy nie ma dostępnej odpowiedniej strategii w serwisie, który będzie realizował fabrykę.

 

Przykładowa implementacja interface

 

public interface DocReaderStrategy {

    List<TransactionDTO> readTransactions(InputStream file);

    boolean isSuitableFor(DocBankType type);
}

 

Przedstawiam prosty interfejs, który zawiera dwie metody. Pierwsza z nich, isSuitableFor, służy do weryfikacji, czy dany typ jest dla nas interesujący. Druga metoda, readTransactions, odpowiada za odczyt transakcji z pliku w formacie CSV.

 

Ten interfejs zapewnia elastyczność poprzez możliwość implementacji różnych strategii do przetwarzania transakcji w zależności od typu pliku oraz dostarcza metody do odczytu transakcji z pliku CSV.

 

Docelowa klasa imlementacyjna wzorca strategii

 

@Service
class PKOReader implements DocReaderStrategy {

    @Override
    public boolean isSuitableFor(DocBankType type) {
        return type == DocBankType.PKO;
    }

    @Override
    public List<TransactionDTO> readTransactions(InputStream file) {

        var workbook = new HSSFWorkbook();
        try {
            workbook = new HSSFWorkbook(file);
            Sheet sheet = workbook.getSheetAt(0);
            for (Row row : sheet) {
                for (Cell cell : row) {
                   //code
                }
            }
            workbook.close();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

        //code

    }

}

 

Powyższy przykład to już klasa, która docelowo będzie posiadała logiczną strukturę opartą na wzorcu stratergii i zawierającą logike biznesową odpowiedzialną za przetwarzanie konkretnego pliku CSV.

Pierwsza metoda, isSuitableForreadTransactions, to właściwa część procesu, gdzie następuje obróbka danych oraz zwracana jest konkretne implementacje obiektów.

 

Przykładowe zastosowanie

    @Override
    public List<Transaction> readDoc(InputStream file, DocBankType type) {
        return docReaderStrategyFactory.findStrategyFor(type)
                .orElseThrow(() -> new RuntimeException("No suitable strategy found for " + type))
                .readTransactions(file)
                .stream()
                .map(transactionMapper::toDomain)
                .collect(Collectors.toCollection(List::of));
    }

 


Powyższa implementacja potrzebuje jedynie pliku wejściowego oraz określenia oczekiwanego typu obsługi, co czyni ją niezwykle prostą, a jednocześnie zaawansowaną pod względem funkcjonalności biznesowych.

 

Podsumowanie

Wzorzec Strategii to wartościowa technika programistyczna, która pozwala na elastyczne i efektywne zarządzanie różnymi algorytmami w aplikacji. W Javie można wykorzystać ten wzorzec do oddzielenia i wymiany różnych strategii w przejrzysty sposób. Dzięki temu kod staje się bardziej zorganizowany, łatwiejszy w utrzymaniu i rozszerzalny, co przyczynia się do wydajnego i skalowalnego projektowania oprogramowania.

3
[java, wzorzec projektowy, wzorzec strategia]

Więcej od Dominik Martyniak

Więcej artykułów