web-dev-qa-db-fra.com

Remplacer le conditionnel par le polymorphisme

J'essaie de comprendre cette pratique de code propre avec un exemple. Considérez un produit de classe ayant un boîtier de commutateur pour remise. J'essaie de remplacer l'instruction switch par le polymorphisme.

Avant le code:

class Product {
    String priceCode;
    int discount;

    Product(String priceCode) {
        setDiscount(priceCode);
    }

    public int getDiscount() {
        return discount;
    }

    public void setDiscount(String priceCode) {
        switch (priceCode) {
            case "CODE1":
                discount = // some logic;
            case "CODE2":
                discount = // some other logic;
            case "CODE3":
                discount = // some other logic;
        }
    }
}

Dans le code ci-dessous, comme vous pouvez le voir, j'ai supprimé l'instruction switch mais j'ai encore des conditions if pour créer un objet de discountStrategy. Ma question est que j'ai encore des conditions que j'essaie de supprimer avec le polymorphisme.

Après le code:

class Product {
    String priceCode;
    DiscountStrategy discountStrategy;

    Product(String priceCode) {
        setDiscount(priceCode);
    }

    public int getDiscount() {
        return discountStrategy.getDiscount();
    }

    public void setDiscount(String priceCode) {
        if (priceCode.equals("CODE1")) {
            discountStrategy = new DiscountStrategy1();
        } else if (priceCode.equals("CODE2")) {
            discountStrategy = new DiscountStrategy2();
        }
        // ...
    }
}

interface DiscountStrategy {
    public int getDiscount();
}

class DiscountStrategy1 implements DiscountStrategy {
    public int getDiscount() {
        // calculate & return discount;
    }
}

class DiscountStrategy2 implements DiscountStrategy {
    public int getDiscount() {
        // calculate & return discount;
    }
}

class DiscountStrategy3 implements DiscountStrategy {
    public int getDiscount() {
        // calculate & return discount;
    }
}

Pouvez-vous m'aider à comprendre ce concept avec une meilleure implémentation de cet exemple?

17
user1298426

Je pense que la classe de produit ne doit pas être au courant du processus de création de remise, elle doit uniquement utiliser une remise. Donc, ma suggestion est de créer une fabrique de remises avec une carte qui contiendra différentes implémentations de remise:

class DiscountFactory {
    private static final Map<String, DiscountStrategy> strategies = new HashMap<>();
    private static final DiscountStrategy DEFAULT_STRATEGY = () -> 0;

    static {
        strategies.put("code1", () -> 10);
        strategies.put("code2", () -> 20);
    }

    public DiscountStrategy getDiscountStrategy(String priceCode) {
        if (!strategies.containsKey(priceCode)) {
            return DEFAULT_STRATEGY;
        }
        return strategies.get(priceCode);
    }
}

Après cela, la classe Product peut être simplifiée:

class Product {
    private DiscountStrategy discountStrategy;

    Product(DiscountStrategy discountStrategy) {
        this.discountStrategy = discountStrategy;
    }

    public int getDiscount() {
        return discountStrategy.getDiscount();
    }
}

L'interface fonctionnelle vous permettra de créer différentes implémentations à l'aide d'expressions lambda:

interface DiscountStrategy {
    int getDiscount();
}

Et enfin, exemple d'utilisation d'un produit avec remise:

DiscountFactory factory = new DiscountFactory();
Product product = new Product(factory.getDiscountStrategy("code1"));
19
Oleksandr Pyrohov

Mes deux cents: vous devrez passer les paramètres à la méthode discount().

une. Créez un niveau de classe statique HashMap of DiscountStrategy. Par exemple :

map.put("CODE1", new DiscountStrategy1());
map.put("CODE2", new DiscountStrategy2());

b. partout où vous en avez besoin, vous pouvez simplement utiliser:

map.get(priceCode).discount()
4
akshaya pandey

Lorsque, comme cela semble être le cas dans votre exemple, la stratégie de remise est liée à un type de produit spécifique, je calcule la remise au niveau du poste de commande. Par exemple:

class Product {
    double basePrice;
    DiscountStrategy discountStrategy;

...

    public double getBasePrice() {
        return basePrice;
    }

    public DiscountStrategy getDiscountStrategy() {
        return discountStrategy;
    }
}

interface DiscountStrategy {
    public double calculate(int quantity, Product product);
}

class OrderItem {
    int quantity;
    Product product;

    public double getAmount() {
        DiscountStrategy ds = product.getDiscountStrategy();
        double discount = ds.calculate(quantity, product);
        return quantity*(product.getBasePrice() - discount);
    }
}

Exemple de stratégie de remise: remise sur quantité:

class QuantityRateDiscount implements DiscountStrategy {
    static class QuantityRate {
        final int minQuantity;
        final double rate; // in %

        QuantityRate(int minQuantity, double rate) {
            this.minQuantity = minQuantity;
            this.rate = rate;
        }
    }

    QuantityRate[] rateTable;

    // rateTable must be sorted by ascending minQuantity
    QuantityRateDiscount(QuantityRate... rateTable) {
        this.rateTable = rateRable.clone();
    }

    @Override
    public double calculate(int quantity, Product product) {
        QuantityRate qr = null;
        for (QuantityRate qr2: rateTable) {
            if (qr2.minQuantity > quantity) {
                break;
            }
            qr = qr2;
        }
        if (qr != null) {
            return product.getBasePrice()*qr.rate/100.0;
        } else {
            return 0;
        }
    }
}
0
Maurice Perry

Voici ce que tu dois faire

    class Product {

    String priceCode;
    DiscountStrategy discountStrategy;

    HashMap<String, DiscountStrategy> map=new HashMap();

    Product(String priceCode) {
        map.put("CODE1", new DiscountStrategy1());
        map.put("CODE2", new DiscountStrategy2());
        map.put("CODE3", new DiscountStrategy3());
        setDiscount(priceCode);
    }

    public void setDiscount(String priceCode){
               discountStrategy=map.get(priceCode);
        }

    public int getDiscount() {
        return discountStrategy.getDiscount();
    }
}
0
Mars Moon