Как применять шаблоны проектирования в коде

Шаблоны проектирования

Зачем использовать шаблоны проектирования

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

Как определить, где нужно использовать шаблон проектирования

image

В нашей реализации есть некоторые коды, которые написаны один раз и не изменятся в будущем, или которые не нужно расширять, например, некоторые классы инструментов. Есть часть, которая будет часто меняться, и большинство шаблонов проектирования применяются к этой части, требования к которой будут меняться. Проанализируйте, как эти коды изменятся, и выберите подходящие шаблоны проектирования для оптимизации этой части кода.

Возьмем в качестве примера потребность в рекламных акциях.

нужно

Чтобы стимулировать продажу товаров, крупные электронные торговые точки отменят некоторые рекламные мероприятия, чтобы стимулировать потребление пользователей в обычное время или во время некоторых фестивалей. Типы мероприятий могут различаться следующим образом:

  • Полная скидка, полная 400 минус 20
  • Золотой ваучер, ваучер Maserati на 5 юаней
  • скидка, скидка 10%, скидка 20%
  • Каждый полный вычет, каждые 200 вычетов по 10
  • так далее

Некоторые из них можно складывать, а некоторые можно использовать только по отдельности.

Простая реализация

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

Когда вы сначала получаете спрос, вам не нужно так много думать.

public class OrderPromotion {

    public BigDecimal promotion(Order order, int[] promotions){
        for(int promotion:promotions){
            switch (promotion){
                case 1:
                    //计算该类型折扣后的价格
                    break;
                case 2:
                    //计算该类型折扣后的价格
                    break;
                case 3:
                    //计算该类型折扣后的价格
                    break;
                //....
            }
        }
        return order.getResultPrice();
    }
}

Только с точки зрения реализации функций приведенный выше код завершил основные функции. Но приведенный выше код тоже фатальный, хоть и выглядит очень просто, но только потому, что большая часть функций заменена комментариями, а если его заменить реальным кодом, то метод может занимать тысячи строк.

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

Оптимизация 1: принцип единой ответственности

В приведенном выше коде метод Promotion(...) непосредственно выполняет всю работу, но в нашей фактической реализации лучше всего позволить методу нести одну ответственность и выполнять только определенную функцию, поэтому здесь мы будем судить о скидке. введите и рассчитайте цену отдельно:

public class OrderPromotion {

    public BigDecimal promotion(Order order, int[] promotions){
        for(int promotion:promotions){
            switch (promotion){
                case 1:
                    calculate1(order);
                    break;
                case 2:
                    calculate2(order);
                    break;
                case 3:
                    calculate3(order);
                    break;
                //more promotion
            }
        }
        return order.getResultPrice();
    }
    
    public void calculate1(Order order){
        //计算使用折扣一后的价格
    }

    public void calculate2(Order order){
        //计算使用折扣二后的价格
    }

    public void calculate3(Order order){
        //计算使用折扣三后的价格
    }
    
    //more calculate
    
}

Здесь мы отделяем суждение о типе скидки от расчета цены, что значительно уменьшает количество кода метода поощрения(...) и улучшает читаемость кода.

Оптимизация 2: режим стратегии

Приведенный выше оптимизированный код улучшает читабельность исходного кода, но проблема взрыва исходного кода класса OrderPromotion не решена. В ответ на эту проблему мы надеемся отделить код расчета от текущего кода.Первое, что мы можем придумать, это определить класс, а затем скопировать код расчета в этот класс и вызывать его при необходимости. Таким образом, он действительно разделен, но это полностью временное решение. При добавлении новой акции необходимо изменить оба класса.

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

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

Идея шаблона стратегии состоит в том, чтобы инкапсулировать каждый алгоритм в отдельный класс с общим интерфейсом для набора алгоритмов, чтобы их можно было заменять друг другом. Самая большая особенность режима стратегии заключается в том, что алгоритм можно изменить, не затрагивая клиента, тем самым изменяя различные функции.

promotion

public class OrderPromotion {

    public BigDecimal promotion(Order order, int[] promotions){
        for(int promotion:promotions){
            switch (promotion){
                case 1:
                    new PromotionType1Calculate(order);
                    break;
                case 2:
                    new PromotionType1Calculate(order);
                    break;
                case 3:
                    new PromotionType1Calculate(order);
                    break;
                //more promotion
            }
        }
        return order.getResultPrice();
    }
}

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

Оптимизация третья: заводской режим

Хотя приведенный выше код разделил реализацию действий по продвижению, OrderPromotion все еще меняется, и этот класс необходимо изменять каждый раз, когда действие добавляется или отключается. Теперь мы хотим, чтобы OrderPromotion был постоянным, исключая создание экземпляра PromotionCalculation. Создание класса, очевидно, использует шаблон проектирования factory.

OrderPromotion

public class OrderPromotion {

    public BigDecimal promotion(Order order, int[] promotions){
        for(int promotion:promotions){
            PromotionFactory.getPromotionCalculate(promotion).calculate(order);
        }
        return order.getResultPrice();
    }
}

Создание класса передается на фабрику для реализации.

PromotionFactory

public class PromotionFactory {
    
    public static PromotionCalculate getPromotionCalculate(int promotion){
        switch (promotion){
            case 1:
                return new PromotionType1Calculate(order);
            break;
            case 2:
                return new PromotionType1Calculate(order);
            break;
            case 3:
                return new PromotionType1Calculate(order);
            break;
            //more promotion
        }
        return null;
    }
}

После использования фабричного режима класс OrderPromotion менять не нужно, каждый раз, когда добавляется новая акция, ее нужно только добавить в фабричный класс.

Четвертая оптимизация: конфигурация + отражение

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

Определите файл конфигурации сопоставления

mapping.properties

1=design.order.PromotionType1Calculate
2=design.order.PromotionType2Calculate
3=design.order.PromotionType3Calculate

PromotionFactory

public class PromotionFactory {
    
    private static Map<Integer, String> mapping = new HashMap<Integer, String>();

    static {
        try {
            Properties pps = new Properties();
            pps.load(new FileInputStream("Test.properties"));
            Iterator<String> iterator = pps.stringPropertyNames().iterator();
            while(iterator.hasNext()){
                String key=iterator.next();
                mapping.put(Integer.valueOf(key), pps.getProperty(key));
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static PromotionCalculate getPromotionCalculate(int promotion) throws Exception {
        if(mapping.containsKey(promotion)){
            String beanName = mapping.get(promotion);
            return Class.forName(beanName).newInstance();
        }
        return null;
    }
}

С помощью приведенного выше кода можно реализовать гибкое расширение функции без изменения существующего кода. Конечно, код здесь только для демонстрационных целей, на самом деле есть еще много областей, которые можно улучшить, например, итоговая эффективность отражения низкая, чего можно добиться и другими способами.

резюме

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