[Открыть новые позы] Вспоминая о страхе перед преобладанием «если-иначе», мы собираемся победить «если-иначе».

Java
[Открыть новые позы] Вспоминая о страхе перед преобладанием «если-иначе», мы собираемся победить «если-иначе».

предисловие

[Разблокировать новые позы] Брат деи, ваш код нужно оптимизировать
Как упоминалось в предыдущей статье, простойif-else,можно использовать卫语句оптимизировать. Однако в реальной разработке зачастую бывает не простоif-elseструктура, мы обычно不经意间Напишите следующий код:


-------------------- 理想中的 if-else  --------------------
public void today() {
    if (isWeekend()) {
        System.out.println("玩游戏");
    } else {
        System.out.println("上班!");
    }
}


-------------------- 现实中的 if-else  --------------------

if (money >= 1000) {
    if (type == UserType.SILVER_VIP.getCode()) {

        System.out.println("白银会员 优惠50元");
        result = money - 50;
    } else if (type == UserType.GOLD_VIP.getCode()) {

        System.out.println("黄金会员 8折");
        result = money * 0.8;
    } else if (type == UserType.PLATINUM_VIP.getCode()) {

        System.out.println("白金会员 优惠50元,再打7折");
        result = (money - 50) * 0.7;
    } else {
        System.out.println("普通会员 不打折");
        result = money;
    }
}


//省略 n 个 if-else ......

Не будет преувеличением сказать, что все мы писали похожий код, вспоминая, какif-elseИз-за страха господства мы часто ничего не делаем или даже отпускаем.

Позвольте мне поделиться сложностями, с которыми я столкнулся при разработкеif-elseутверждение“优雅处理”идеи. Если что-то не так, пожалуйста, поделитесь и изучите вместе.

необходимость

Допустим есть такое требование:

Система электронной коммерции, когда пользователи потребляют满1000Сумма может быть снижена в зависимости от VIP-уровня пользователя.

По VIP-уровню пользователя рассчитывается окончательная стоимость пользователя.

  • Обычные участники не делают скидки
  • Скидка серебряному участнику 50 юаней
  • Золотой участник скидка 20%
  • Платиновые участники экономят 50 юаней, затем скидка 30%

Реализация кодирования

private static double getResult(long money, int type) {

    double result = money;

    if (money >= 1000) {
        if (type == UserType.SILVER_VIP.getCode()) {

            System.out.println("白银会员 优惠50元");
            result = money - 50;
        } else if (type == UserType.GOLD_VIP.getCode()) {

            System.out.println("黄金会员 8折");
            result = money * 0.8;
        } else if (type == UserType.PLATINUM_VIP.getCode()) {

            System.out.println("白金会员 优惠50元,再打7折");
            result = (money - 50) * 0.7;
        } else {
            System.out.println("普通会员 不打折");
            result = money;
        }
    }

    return result;
}

Для удобства демонстрации я сделал простую реализацию в коде, но на самом делеif - elseбудет осуществляться复杂的逻辑выставление счетов. Функционально он в основном завершен, но для человека вроде меня, помешанного на чистоте кода, качество кода не может вынести прямого взгляда на него. мы начинаем优化посмотри на наш第一版代码Бар.

считать

Увидев приведенный выше код, первая мысль умных друзей, что это нетипично策略模式?

Вы такой умный парень, давайте сначала попробуем оптимизировать код с помощью шаблона стратегии.

режим стратегии

Что такое режим стратегии?

Некоторым друзьям может быть непонятно, что такое режим стратегии. Шаблон стратегии состоит в том, чтобы определить ряд алгоритмов, расположив их один за другим.封装и сделать их взаимно совместимыми替换.

Например, указанные выше требования返利,имеют打折,имеют折上折и Т. Д. Эти алгоритмы сами по себе策略. и эти алгоритмы могут взаимодействовать друг с другом替换Да, как сегодня я хочу白银会员优惠50, который можно заменить завтра на白银会员打9折.

Сказав так много, лучше закодировать.

кодирование

public interface Strategy {
    
    // 计费方法
    double compute(long money);
}

// 普通会员策略
public class OrdinaryStrategy implements Strategy {

    @Override
    public double compute(long money) {
        System.out.println("普通会员 不打折");
        return money;
    }
}

// 白银会员策略
public class SilverStrategy implements Strategy {

    @Override
    public double compute(long money) {

        System.out.println("白银会员 优惠50元");
        return money - 50;
    }
}

// 黄金会员策略
public class GoldStrategy implements Strategy{

    @Override
    public double compute(long money) {
        System.out.println("黄金会员 8折");
        return money * 0.8;
    }
}

// 白金会员策略
public class PlatinumStrategy implements Strategy {
    @Override
    public double compute(long money) {
        System.out.println("白金会员 优惠50元,再打7折");
        return (money - 50) * 0.7;
    }
}

мы определяемStrategyинтерфейс и определить четыре подкласса, которые реализуют интерфейс. в соответствующемcomputeМетод Реализовать логику начисления платы собственной политики.

private static double getResult(long money, int type) {

    double result = money;

    if (money >= 1000) {
        if (type == UserType.SILVER_VIP.getCode()) {

            result = new SilverStrategy().compute(money);
        } else if (type == UserType.GOLD_VIP.getCode()) {

            result = new GoldStrategy().compute(money);
        } else if (type == UserType.PLATINUM_VIP.getCode()) {

            result = new PlatinumStrategy().compute(money);
        } else {
            result = new OrdinaryStrategy().compute(money);
        }
    }

    return result;
}

затем соответствоватьgetResultметод, согласноtypeЗамените соответствующим VIP-пользователем策略. В этом коде появляются повторяющиеся вызовыcompute, мы можем попытаться оптимизировать дальше.

private static double getResult(long money, int type) {

    if (money < 1000) {
        return money;
    }

    Strategy strategy;

    if (type == UserType.SILVER_VIP.getCode()) {
        strategy = new SilverStrategy();
    } else if (type == UserType.GOLD_VIP.getCode()) {
        strategy = new GoldStrategy();
    } else if (type == UserType.PLATINUM_VIP.getCode()) {
        strategy = new PlatinumStrategy();
    } else {
        strategy = new OrdinaryStrategy();
    }

    return strategy.compute(money);
}

помнишь, я былпервый разупоминается в卫语句? Мы возвращаемся рано за деньги 满1000逻辑, а также для уменьшения ненужных отступов.

обдумывать

В какой-то момент я подумал, что режим стратегии — это не что иное, как это. Я думал, что код был оптимизирован до этого момента.

Но есть еще одна ужасная вещь,if-elseВсе-еще существует :)

Я пытался пролистать много книг, чтобы увидеть, как избавиться от режима стратегии.if-else

Большинство методов в книге используют шаблон Simple Factory + Strategy. Пучокif - elseпереключить наswitchПросто создайте фабричный метод.

Но это далеко не то, что я хочу, внизif - else

Пока однажды ночью мой босс не поделилсяJava8Когда дело доходит до небольших навыков, это открывает новый мир.

Фабрика + Стратегия

public interface Strategy {

    double compute(long money);

    // 返回 type
    int getType();
}


public class OrdinaryStrategy implements Strategy {

    @Override
    public double compute(long money) {
        System.out.println("普通会员 不打折");
        return money;
    }

    // 添加 type 返回
    @Override
    public int getType() {
        return UserType.SILVER_VIP.getCode();
    }
}

public class SilverStrategy implements Strategy {

    @Override
    public double compute(long money) {

        System.out.println("白银会员 优惠50元");
        return money - 50;
    }

    // type 返回
    @Override
    public int getType() {
        return UserType.SILVER_VIP.getCode();
    }
}

....省略剩下 Strategy

Добавим новый в СтратегиюgetTypeметод использования标示этой стратегииtypeценность. Код относительно прост, поэтому я не буду вводить здесь слишком много.

public class StrategyFactory {

    private Map<Integer, Strategy> map;

    public StrategyFactory() {

        List<Strategy> strategies = new ArrayList<>();

        strategies.add(new OrdinaryStrategy());
        strategies.add(new SilverStrategy());
        strategies.add(new GoldStrategy());
        strategies.add(new PlatinumStrategy());
        strategies.add(new PlatinumStrategy());

        // 看这里 看这里 看这里!
        map = strategies.stream().collect(Collectors.toMap(Strategy::getType, strategy -> strategy));
        
        /* 等同上面
        map = new HashMap<>();
        for (Strategy strategy : strategies) {
            map.put(strategy.getType(), strategy);
        }*/
    }

    public static class Holder {
        public static StrategyFactory instance = new StrategyFactory();
    }

    public static StrategyFactory getInstance() {
        return Holder.instance;
    }

    public Strategy get(Integer type) {
        return map.get(type);
    }
}

Одноэлементный статический внутренний класс, тип реализации одноэлементного шаблона, не является предметом этой статьи.Если вы не понимаете этого, вы можете сделать это самостоятельно.google

Приступаем к созданию другогоStrategyFactoryЗаводской класс. StrategyFactory здесь я использую静态内部类单例, при построении метода инициализировать требуемыйStrategy, и положиlistпревратиться вmap. Здесь преобразование“灵魂”где.

toMap

Давайте сначала посмотримJava8Маленькая хитрость в грамматике.

Обычно мы перебираем список вручнуюputв Карту.

--------------  before -----------------

map = new HashMap<>();
for (Strategy strategy : strategies) {
    map.put(strategy.getType(), strategy);
}

--------------  after Java8 -----------------

map = strategies.stream().collect(Collectors.toMap(Strategy::getType, strategy -> strategy));

toMapПервый параметр — это Функция, соответствующая карте вkey, второй параметр тоже Функция, стратегия -> стратегия, слеваstrategyсостоит в том, чтобы пройти каждую стратегию в стратегиях, правильныхstrategyКарта соответствуетvalueценность.

Если вы не понимаетеJava8Друзья грамматики, настоятельно рекомендуем посмотреть"Java8 实战, который подробно описан в книгеLambdaвыражение,Streamи т.д. синтаксис.

Эффект

private static double getResult(long money, int type) {

    if (money < 1000) {
        return money;
    }

    Strategy strategy = StrategyFactory.getInstance().get(type);
    
    if (strategy == null){
        throw new IllegalArgumentException("please input right type");
    }

    return strategy.compute(money);
}

До сих пор, через заводской класс, в нашемgetResult()При звонке, по входящемуtype, вы можете получить соответствующийStrategy

больше не страшноif-elseутверждение.

Готовая разброска цветов :)

следовать за

Для последующей оптимизации кода, если это Java-проект, можно попробовать использовать自定义注解, аннотируйте класс реализации стратегии.

Это может упростить первоначальную необходимость добавления списка фабричных классов.Stratey 策略.

Наконец

Выше это то, с чем я столкнулся при разработке сложныхif-elseутверждение“优雅处理”Если с вашими идеями что-то не так, вы можете обменяться ими и поучиться вместе.