Wybór strategi na podstawie pola - mapa zamiast ifów

0

Cześć proste pytanie. Dostaję obiekt w którym jedno z pól muszę zmienić w zależności od pola enum a do samego generowania korzystam z pozostałych pól + serwisu. Np. zmiana ceny książki na podstawie jej gatunku(enum) i czasem jakiegoś zewnętrznego biznesu/serwisu. Niżej zamieszczam przykład.
Chodzi o mape Map<JakisEnum,JakasStrategiaSupplier> zamiast ifów czy są lepsze wzorce na takie sytuacje?

import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;

import java.util.Collection;
import java.util.List;
import java.util.Random;

enum Genre {
    NOVEL, POEM, DRAMA, COMEDY
}

class Book {
    private String title;
    private Integer price;
    private Genre genre;

    public Book(String title, Integer price, Genre genre) {
        this.title = title;
        this.price = price;
        this.genre = genre;
    }

    public Genre getGenre() {
        return genre;
    }

    public Integer getPrice() {
        return price;
    }

    public void setPrice(Integer price) {
        this.price = price;
    }

    @Override
    public String toString() {
        return "Book{" +
                "title='" + title + '\'' +
                ", price=" + price +
                ", genre=" + genre +
                '}';
    }
}

interface BookService {
    int calcGenrePopularity(Genre genre);
}

class BookPriceProcessor {
    private BookService bookService;

    private Generator dramaGenerator = book -> {
        Integer popularity = bookService.calcGenrePopularity(Genre.DRAMA);
        double rr = popularity * 0.10;
        return (int) Math.round(rr * book.getPrice());
    };

    private Generator comedyGenerator = book -> (int) Math.round(1.1 * book.getPrice());

    private final ImmutableMap<Genre, Generator> map = ImmutableMap.of(
            Genre.DRAMA, dramaGenerator,
            Genre.COMEDY, comedyGenerator
    );

    public BookPriceProcessor(BookService bookService) {
        this.bookService = bookService;
    }

    public void setPriceFor(Book book) {
        Genre genre = book.getGenre();
        Generator generator = map.getOrDefault(genre, Book::getPrice);
        book.setPrice(generator.getPrice(book));
    }

    public void setPriceFor(Collection<Book> books) {
        books.forEach(this::setPriceFor);
    }

    private interface Generator {
        int getPrice(Book book);
    }
}

public class Main {
    public static void main(String[] args) {
        List<Book> books = Lists.newArrayList(
                new Book("jakas komedia", 1000, Genre.COMEDY),
                new Book("jakis dramta", 1000, Genre.DRAMA),
                new Book("jakas nowela", 1000, Genre.NOVEL),
                new Book("jakas poemat", 1000, Genre.POEM)
        );

        BookPriceProcessor bookPriceProcessor = new BookPriceProcessor(genre -> new Random().nextInt(10));
        bookPriceProcessor.setPriceFor(books);

        System.out.println(books);
    }
}

albo inny przykład mapa <String,JakisTamSupplier> gdy nie mamy sytuacji typu if(jakisStr.contains("costam") tylko zwykłe equals

0

Rozumiem że chodzi o wybieranie akcji dla danego typu ? Może nie jestem zbyt purystyczny ale czy nie wystarczy zwykły if albo switch na tych warunkach wrzucony w jakąś ładnie nazwaną metodę ?

public void doSomethingBasedOnKey(Mapa mapa){
... słiczem albo if'em i luz.
}

Jak się czasem zawędruje do źródeł np: Springa na git'cie to są tam miejsca które są za1@#rane ifami - więc, kiss i dry ale bez przesady.

0

@karolinaa dużo zależy od konkretnego problemu. Patrz np. tu:
Typ obiektu zwracanego przez metodę

2

Jeżeli enum jest bardzo silnie powiązany z strategią i jest to jedno z jego głównych zastosowań to możesz też rozważyć opcję stworzenia pola w typie enum które będzie go zawierało.

1 użytkowników online, w tym zalogowanych: 0, gości: 1