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

Odpowiedz Nowy wątek
2014-12-14 16:41
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


PROGRAMY NA ZAMÓWIENIE, ZALICZENIA STUDENCKIE, KONFIGURACJA SERWERÓW, SYSTEMÓW I BAZ DANYCH, STRONY INTERNETOWE, POMOC W PROGRAMOWANIU, POPRAWIENIE I OPTYMALIZACJA APLIKACJI
JAVA, C++, LINUX, WWW, SQL, PYTHON
POSIADAM KOMERCYJNE DOŚWIADCZENIE
TANIO, SZYBKO I PORZĄDNIE
Z KOMENTARZAMI OBJAŚNIAJĄCYMI KOD
PISZ NA PRYWATNĄ WIADOMOŚĆ
CENY JUŻ OD 49,99ZŁ ZA PROGRAM
ZAJMIJ SIĘ TYM CO CIĘ NAPRAWDĘ INTERESUJE!
Pokaż pozostałe 2 komentarze
@karolinaa faktycznie, jeżeli chcesz powiązać jakaś logikę z typem enumeracyjnym, to może będziesz to robiła wcześnie, pozbywasz się mapy - spójrz proszę do Effective Java 2 - item 30 - niezdecydowany 2014-12-16 09:15
ta tylko wstrzykiwanie różnych serwisów wtedy do enuma (jak w strategi musze z jakiegoś skorzystać) robi się trochę zagmatwane - karolinaa 2014-12-16 10:12
czyli nie jest to immutable - tak więc item 30 odpada :D - niezdecydowany 2014-12-16 11:12
trochę jaśniej? Mape <String,JakisInterfejsSupplier> uznaje jako immutable, tylko czy słusznie? - karolinaa 2014-12-16 11:50
ale możesz wywalić po kluczy stringa, tak ? i przypisać nowy interfejssuppiler - - niezdecydowany 2014-12-16 12:00

Pozostało 580 znaków

2014-12-14 17:45
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ą [email protected]#rane ifami - więc, kiss i dry ale bez przesady.


"Perhaps surprisingly, concurrent programming isn’t so much about threads or
locks, any more than civil engineering is about rivets and I-beams."

Pozostało 580 znaków

2014-12-14 18:01
0

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


Masz problem? Pisz na forum, nie do mnie. Nie masz problemów? Kup komputer...

Pozostało 580 znaków

2014-12-16 08:11
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.

Pozostało 580 znaków

Odpowiedz
Liczba odpowiedzi na stronę

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