深入理解设计模式-策略模式(结合简单工厂、反射、Spring详细讲解)

文章目录

前言

在现实生活中常常遇到实现某种目标存在多种策略可供选择的情况,例如,出行旅游可以乘坐飞机、乘坐火车、骑自行车或自己开私家车等,超市促销可以釆用打折、送商品、送积分等方法。

在软件开发中也常常遇到类似的情况,当实现某一个功能存在多种算法或者策略,我们可以根据环境或者条件的不同选择不同的算法或者策略来完成该功能,如数据排序策略有冒泡排序、选择排序、插入排序、二叉树排序等。

如果使用多重条件转移语句实现(即硬编码),不但使条件语句变得很复杂,而且增加、删除或更换算法要修改原代码,不易维护,违背开闭原则。如果采用策略模式就能很好解决该问题。

一、定义

策略模式定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的变化不会影响使用算法的客户。策略模式属于对象行为模式,它通过对算法进行封装,把使用算法的责任和算法的实现分割开来,并委派给不同的对象对这些算法进行管理。

二、使用场景

  • 过多的if-else或者switch代码,而且当我们修改某一分支逻辑时,要改动的很多内容时。
  • 一个功能存在多种算法或者策略,我们可以根据环境或者条件的不同选择不同的算法或者策略来完成该功能
  • 一个功能的算法或者策略,客户明确度不高,频繁来回改动,而更换算法要修改原代码,不易维护,违背开闭原则。

三、代码样例

1.需求

商场活动,不定时更换促销策略,打折、满减、返利等,促销算法需要根据促销策略的变更进行改变。

2.类图

《深入理解设计模式-策略模式(结合简单工厂、反射、Spring详细讲解)》

3.代码

/** * 策略接口 */
public interface Strategy { 
    void algorithmInterface();
}

public class ConcreteStrategyA implements Strategy { 

    @Override
    public void algorithmInterface() { 
        System.out.println("策略A的具体算法实现");
    }

}
public class ConcreteStrategyB implements Strategy { 

    @Override
    public void algorithmInterface() { 
        System.out.println("策略B的具体算法实现");
    }

}
public class ConcreteStrategyC implements Strategy { 

    @Override
    public void algorithmInterface() { 
        System.out.println("策略C的具体算法实现");
    }

}
/** * 上下文 */
public class Context { 
    Strategy strategy;

    public Context(Strategy strategy) { 
        this.strategy = strategy;
    }

    /** * 上下文接口 */
    public void contextInterface() { 
        strategy.algorithmInterface();
    }

}
/** * 客户端使用策略 */
public class StrategyClient { 
    public static void main(String[] args) { 
        Context context;
        String type = "1";
        switch (type) { 
            case "1":
                context = new Context(new ConcreteStrategyA());
                break;
            case "2":
                context = new Context(new ConcreteStrategyB());
                break;
            case "3":
                context = new Context(new ConcreteStrategyC());
                break;
            default:
                throw new IllegalStateException("Unexpected value: " + type);
        }
        context.contextInterface();


    }
}

四、优缺点

优点:

  • 多重条件语句不易维护,而使用策略模式可以避免使用多重条件语句。
  • 策略模式提供了一系列的可供重用的算法族,恰当使用继承可以把算法族的公共代码转移到父类里面,从而避免重复的代码。
  • 策略模式可以提供相同行为的不同实现,客户可以根据不同时间或空间要求选择不同的。
  • 策略模式提供了对开闭原则的完美支持,可以在不修改原代码的情况下,灵活增加新算法。
  • 策略模式把算法的使用放到环境类中,而算法的实现移到具体策略类中,实现了二者的分离。

缺点:

  • 客户端必须理解所有策略算法的区别,以便适时选择恰当的算法类。
  • 策略模式造成很多的策略类。

五、延伸样例

1.结合简单工厂样例

在基础的策略模式中,调用方需要维护多个策略相关类,耦合度较高,且判断关系对于调用法未必十分知晓。

策略模式与简单工厂结合的用法,客户端就只需要认识一个类ContextWithSimpleFactory 就可以了。耦合更加降低。”“我们在客户端实例化的是ContextWithSimpleFactory 的对象,调用的是ContextWithSimpleFactory的方法,这使得具体的收费算法彻底地与客户端分离。连算法的父类Strategy都不让客户端认识了。

/** * 策略模式结合简单工厂-上下文 */
public class ContextWithSimpleFactory { 
    Strategy strategy;

    public ContextWithSimpleFactory(String type) { 
        switch (type) { 
            case "1":
                this.strategy = new ConcreteStrategyA();
                break;
            case "2":
                this.strategy = new ConcreteStrategyB();
                break;
            case "3":
                this.strategy = new ConcreteStrategyC();
                break;
            default:
                throw new IllegalStateException("Unexpected value: " + type);
        }
    }

    /** * 上下文接口 */
    public void contextInterface() { 
        strategy.algorithmInterface();
    }
}

/** * 策略模式结合简单工厂-客户端 */
public class StrategyClientWithSimpleFactory { 
    public static void main(String[] args) { 
        String type = "1";
        ContextWithSimpleFactory contextWithSimpleFactory = new ContextWithSimpleFactory(type);
        contextWithSimpleFactory.contextInterface();
    }
}

2.结合反射样例

为了避免每次添加新的策略都要修改switch语句,我们利用反射将switch语句替换掉

/** * 策略模式结合反射-上下文 */
public class ContextWithReflect { 
    Object obj = null;

    public ContextWithReflect(String className) { 
        try { 
            Class<?> clazz = Class.forName(className);
            obj = clazz.newInstance();
        } catch (ClassNotFoundException e) { 
            e.printStackTrace();
        } catch (InstantiationException e) { 
            e.printStackTrace();
        } catch (IllegalAccessException e) { 
            e.printStackTrace();
        }
    }

    /** * 上下文接口 */
    public void contextInterface() { 
        ((Strategy) obj).algorithmInterface();
    }
}
/** * 策略模式结合反射-客户端 */
public class StrategyClientWithReflect { 
    public static void main(String[] args) { 
        String className = "com.cloud.designpattern.strategy.ConcreteStrategyA";
        ContextWithReflect contextWithReflect = new ContextWithReflect(className);
        contextWithReflect.contextInterface();
    }
}

3.结合Spring样例

利用Spring注入,简化手写反射

/** * 策略模式结合Spring-上下文 */
@Component
public class ContextWithSpring { 
    @Autowired
    private Map<String, Strategy> strategyMap;

    /** * 上下文接口 */
    public void contextInterface(String serviceName) { 
        strategyMap.get(serviceName).algorithmInterface();
    }

}

/** * 策略模式结合Spring-客户端 */
public class StrategyClientWithSpring { 
    @Autowired
    private ContextWithSpring contextWithSpring;

    public void test() { 
        String serviceName = "concreteStrategyA";
        contextWithSpring.contextInterface(serviceName);
    }
}

结尾

  • 感谢大家的耐心阅读,如有建议请私信或评论留言。
  • 如有收获,劳烦支持,关注、点赞、评论、收藏均可,博主会经常更新,与大家共同进步
    原文作者:、Dong
    原文地址: https://blog.csdn.net/qq359605040/article/details/123210064
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞