策略模式 订阅
策略模式是指有一定行动内容的相对稳定的策略名称。策略模式在古代中又称“计策”,简称“计”,如《汉书·高帝纪上》:“汉王从其计”。这里的“计”指的就是计谋、策略。策略模式具有相对稳定的形式,如“避实就虚”、“出奇制胜”等。一定的策略模式,既可应用于战略决策,也可应用于战术决策;既可实施于大系统的全局性行动,也可实施于大系统的局部性行动。 展开全文
策略模式是指有一定行动内容的相对稳定的策略名称。策略模式在古代中又称“计策”,简称“计”,如《汉书·高帝纪上》:“汉王从其计”。这里的“计”指的就是计谋、策略。策略模式具有相对稳定的形式,如“避实就虚”、“出奇制胜”等。一定的策略模式,既可应用于战略决策,也可应用于战术决策;既可实施于大系统的全局性行动,也可实施于大系统的局部性行动。
信息
外文名
Pattern:Strategy
环境角色
持有一个策略类的引用
抽象策略角色
由一个接口或者抽象类实现
中文名
策略模式
具体策略角色
包装了相关的算法和行为
策略模式简介
策略模式作为一种软件设计模式,指对象有某个行为,但是在不同的场景中,该行为有不同的实现算法。比如每个人都要“交个人所得税”,但是“在美国交个人所得税”和“在中国交个人所得税”就有不同的算税方法。策略模式:
收起全文
精华内容
下载资源
问答
  • 主要介绍了Java设计模式编程中的状态模式和策略模式对比,文中列举了两种模式的相似点和不同点,并都举了代码的实例作为参照,需要的朋友可以参考下
  • 策略模式可以使算法可独立于使用它的客户而变化 策略模式变化的是算法 二、策略模式结构图   三、策略模式中主要角色 抽象策略(Strategy)角色:定义所有支持的算法的公共接口。通常是以一个接口或抽象来实现。...
  • 主要介绍了Java设计模式之策略模式(Strategy模式)介绍,Strategy是属于设计模式中对象行为型模式,要是定义一系列的算法,这些算法一个个封装成单独的类,需要的朋友可以参考下
  • 策略模式(Strategy Pattern)又称政策模式,其定义一系列的算法,把它们一个个封装起来,并且使它们可以互相替换。封装的策略算法一般是独立的,策略模式根据输入来调整采用哪个算法。这篇文章主要介绍了策略模式...
  • 策略模式代码,两种方式实现,第一种,通过map存储方式,第二种,通过配置文件加反射方式
  • 所有模式都可分为类模式和对象模式两种,类模式是继承,对象模式是委托,而桥接模式和策略模式都是将任务委托给另外一个接口去实现,那么两者的区别什么呢?
  • Spring下使用策略模式

    2019-06-08 01:08:20
    NULL 博文链接:https://zl4.iteye.com/blog/2333003
  • 设计模式--策略模式java例子
  • 深入浅出设计模式之抽象工厂模式+工厂方法模式+策略模式实现手机加工厂
  • 策略模式例子

    2014-12-16 11:44:04
    策略模式例子:定义了算法族,分别封装起来,让它们之间可以相互替代,此模式让算法的变化独立于使用算法的客户。
  • 本文首先将传统基于OOP策略模式的局限性进行分析说明,提出基本的策略模式以及“链式”策略模式基于AOP的具体实现,解决传统策略模式可能出现的代码分散、代码混乱问题;接着进行复杂度方面的实验对比分析;最后分析...
  • 主要介绍了Java经典设计模式之策略模式,简单说明了策略模式的概念、原理并结合实例形式分析了java策略模式的具有用法与相关注意事项,需要的朋友可以参考下
  • 策略模式Demo

    2015-12-03 22:24:14
    http://blog.csdn.net/xingjiarong/article/details/50166481
  • 设计模式demo (包括:单例模式、建造者模式、策略模式
  • 策略模式 C++实现

    2014-04-29 16:30:00
    策略模式的 C++ 代码实现, ide :XCode
  • 策略模式结合模板方法模式
  • java设计模式中策略模式的一个实例:选择不同的排序算法(①冒泡排序;②插入排序;③选择排序)对数组进行排序。
  • 策略模式

    千次阅读 2019-04-11 11:16:55
    策略模式(Strategy Pattern)中,一个类的行为或其算法可以在运行时更改。例如:出行旅游可以乘坐飞机、乘坐火车、骑自行车或自己开私家车等,超市促销可以釆用打折、送商品、送积分等方法。 意图:定义一系列的...

    在策略模式(Strategy Pattern)中,一个类的行为或其算法可以在运行时更改。例如:出行旅游可以乘坐飞机、乘坐火车、骑自行车或自己开私家车等,超市促销可以釆用打折、送商品、送积分等方法。

    意图:定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换。

    主要解决:在有多种算法相似的情况下,使用 if...else 所带来的复杂和难以维护。

    何时使用:一个系统有许多许多类,而区分它们的只是他们直接的行为。

    如何解决:将这些算法封装成一个一个的类,任意地替换。

    关键代码:实现同一个接口。

    优点: 1、算法可以自由切换。 2、避免使用多重条件判断。 3、扩展性良好。

    缺点: 1、策略类会增多。 2、所有策略类都需要对外暴露。

    使用场景: 1、如果在一个系统里面有许多类,它们之间的区别仅在于它们的行为,那么使用策略模式可以动态地让一个对象在许多行为中选择一种行为。 2、一个系统需要动态地在几种算法中选择一种。 3、如果一个对象有很多的行为,如果不用恰当的模式,这些行为就只好使用多重的条件选择语句来实现。

    注意事项:如果一个系统的策略多于四个,就需要考虑使用混合模式,解决策略类膨胀的问题。

    策略模式的主要角色如下:

    1.抽象策略(Strategy)类:定义了一个公共接口,各种不同的算法以不同的方式实现这个接口,环境角色使用这个接口调用不同的算法,一般使用接口或抽象类实现。

    2.具体策略(Concrete Strategy)类:实现了抽象策略定义的接口,提供具体的算法实现。

    3.环境(Context)类:持有一个策略类的引用,最终给客户端调用。

    其结构图如图所示:

    策略模式

    实现:

    /**
     * 
     * @ClassName: MethodOfTravel
     * @Description: 定义抽象旅行方式
     * @author: ljx
     * @date: 2019年4月11日 上午10:56:56
     */
    public interface MethodOfTravel {
    	public void Method();
    }
    
    /**
     * 
     * @ClassName: Airplane
     * @Description: 坐飞机
     * @author: ljx
     * @date: 2019年4月11日 上午10:58:43
     */
    public class Airplane implements MethodOfTravel {
    
    	@Override
    	public void Method() {
    		System.out.println("选择坐飞机的方式旅游");
    	}
    
    }
    
    /**
     * 
     * @ClassName: Train
     * @Description: 坐火车
     * @author: ljx
     * @date: 2019年4月11日 上午11:07:29
     */
    public class Train implements MethodOfTravel {
    
    	@Override
    	public void Method() {
    		System.out.println("选择坐火车的方式旅游");
    	}
    
    }
    
    /**
     * 
     * @ClassName: TravelContext
     * @Description: 环境类
     * @author: ljx
     * @date: 2019年4月11日 上午11:08:23
     */
    public class TravelContext {
    	private MethodOfTravel methodOfTravel;
    
    	public MethodOfTravel getMethodOfTravel() {
    		return methodOfTravel;
    	}
    
    	public void setMethodOfTravel(MethodOfTravel methodOfTravel) {
    		this.methodOfTravel = methodOfTravel;
    	}
    	
    	/**
    	 * 
    	 * @Title: method
    	 * @Description: 旅游方式
    	 * @return: void
    	 */
    	public void method(){
    		methodOfTravel.Method();
    	}
    }
    
    public class StrategyTest {
    
    	public static void main(String[] args) {
    		MethodOfTravel airplane = new Airplane();
    		MethodOfTravel train = new Train();
    		
    		TravelContext context = new TravelContext();
    		context.setMethodOfTravel(airplane);
    		context.method();
    		System.out.println("================================");
    		context.setMethodOfTravel(train);
    		context.method();
    	}
    
    }

    在一个使用策略模式的系统中,当存在的策略很多时,客户端管理所有策略算法将变得很复杂,如果在环境类中使用策略工厂模式来管理这些策略类将大大减少客户端的工作复杂度,其结构图如图所示。

    ... ...,欢迎指正!

    展开全文
  • if else终结者——策略模式

    千次阅读 多人点赞 2021-05-28 10:48:15
    你是不是还在写着大量的if else语句,if else 不仅难以维护不易扩展,而且使代码臃肿不堪,想不想让你的业务代码更加的健壮,更易扩展,那你一定要学一学今天的主角策略模式。学会了策略模式的使用让会你的代码更加...

    你是不是还在写着大量的if else语句,if else 不仅难以维护不易扩展,而且使代码臃肿不堪,想不想让你的业务代码更加的健壮,更易扩展,那你一定要学一学今天的主角策略模式。学会了策略模式的使用让会你的代码更加优雅。老板看了给你加薪。同事看了对你仰慕。策略模式大家用了都说好。

    阅读完本篇文章你将了解到什么是策略模式,策略模式的优缺点,以及策略模式在源码中的应用。

    策略模式引入

    在软件开发中,我们常常会遇到这样的情况,实现某一个功能有多条途径,每一条途径对应一种算法,此时我们可以使用一种设计模式来实现灵活地选择解决途径,也能够方便地增加新的解决途径。

    譬如商场购物场景中,有些商品按原价卖,商场可能为了促销而推出优惠活动,有些商品打九折,有些打八折,有些则是返现10元等。而优惠活动并不影响结算之外的其他过程,只是在结算的时候需要根据优惠方案结算。
    再比如不同的人出去旅游出行的交通方式也不同,经济条件好的会选择高铁飞机,而普通人可能会选择绿皮火车。
    在这里插入图片描述
    富豪老王打算去西藏旅游,老王定了豪华酒店,并且定了机票当天直达。而普通人老张也要去西藏旅游,他打算选择乘坐高铁出行。而学生党的我小汪肯定会选择绿皮火车,主要是为了看路边的风景,而不是因为穷。

    下面我们用代码来描述一下上诉场景:

    public class Travel {
        private String vehicle;//出行方式
        private String name;
    
        public String getName() {
            return name;
        }
    
        public Travel(String name) {
            this.name = name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public void setVehicle(String vehicle) {
            this.vehicle = vehicle;
        }
    
        public String getVehicle() {
            return vehicle;
        }
    
        public void TravelTool(){
            if(name.equals("小汪")){
                setVehicle("绿皮火车");
            }else if(name.equals("老张")){
                setVehicle("高铁");
            }else if(name.equals("老王")){
                setVehicle("飞机");
            }
            System.out.println(name+"选择坐"+getVehicle()+"去西藏旅游");
        }
    
    }
    
    public class Test {
        public static void main(String[] args) {
            Travel travel1 = new Travel("小汪");
            Travel travel2 = new Travel("老王");
            Travel travel3 = new Travel("老张");
            travel1.TravelTool();
            travel2.TravelTool();
            travel3.TravelTool();
    
        }
    }
    小汪选择坐绿皮火车去西藏旅游
    老王选择坐飞机去西藏旅游
    老张选择坐高铁去西藏旅游
    

    以上代码虽然完成了我们的需求,但是存在以下问题:

    Travel类的TravelTool方法非常庞大,它包含各种人的旅行实现代码,在代码中出现了较长的 if…else… 语句,假如日后小汪发达了也想体验一下做飞机去西藏旅游,那就要去修改TravelTool方法。违反了 “开闭原则”,系统的灵活性和可扩展性较差。
    算法的复用性差,如果在另一个系统中需要重用某些算法,只能通过对源代码进行复制粘贴来重用,无法单独重用其中的某个或某些算法。

    策略模式

    策略模式的介绍

    1. 策略模式(Strategy Pattern)中,定义算法族,分别封装起来,让他们之间可以互相替换,此模式让算法的变化独立于使用算法的客户
    2. 这算法体现了几个设计原则,第一、把变化的代码从不变的代码中分离出来;第二、针对接口编程而不是具体类(定义了策略接口);第三、多用组合/聚合,少用继承(客户通过组合方式使用策略)。

    策略模式的原理类图
    在这里插入图片描述
    角色分析
    Context(环境类):环境类是使用算法的角色,它在解决某个问题(即实现某个方法)时可以采用多种策略。在环境类中维持一个对抽象策略类的引用实例,用于定义所采用的策略。

    Strategy(抽象策略类):它为所支持的算法声明了抽象方法,是所有策略类的父类,它可以是抽象类或具体类,也可以是接口。环境类通过抽象策略类中声明的方法在运行时调用具体策略类中实现的算法。

    ConcreteStrategy(具体策略类):它实现了在抽象策略类中声明的算法,在运行时,具体策略类将覆盖在环境类中定义的抽象策略类对象,使用一种具体的算法实现某个业务处理。

    我们下面用策略模式来改进一下上面旅行的代码例子。

    抽象策略类 Discount

    public abstract class AbstractTravle {
        private String vehicle;
        private String name;
    
        public AbstractTravle(String vehicle, String name) {
            this.vehicle = vehicle;
            this.name = name;
        }
    
        public String getVehicle() {
            return vehicle;
        }
    
        public String getName() {
            return name;
        }
    
        public abstract void TravelTool();
    }
    
    

    ConcreteStrategy(具体策略类)

    public class XiaoWang extends AbstractTravle{
        public XiaoWang(String vehicle, String name) {
            super(vehicle, name);
        }
    
        @Override
        public void TravelTool() {
            System.out.println(getName()+"选择坐"+getVehicle()+"去西藏旅游");
        }
    }
    public class LaoWang extends AbstractTravle{
        public LaoWang(String vehicle, String name) {
            super(vehicle, name);
        }
    
        @Override
        public void TravelTool() {
            System.out.println(getName()+"选择坐"+getVehicle()+"去西藏旅游");
        }
    }
    public class LaoZhang extends AbstractTravle{
        public LaoZhang(String vehicle, String name) {
            super(vehicle, name);
        }
        @Override
        public void TravelTool() {
            System.out.println(getName()+"选择坐"+getVehicle()+"去西藏旅游");
        }
    
    }
    
    

    环境类

    public class Context {
        private AbstractTravle abstractTravle;
    
        public Context(AbstractTravle abstractTravle) {
            this.abstractTravle = abstractTravle;
        }
        public void TravelTool() {
            System.out.println(abstractTravle.getName()+"选择坐"+abstractTravle.getVehicle()+"去西藏旅游");
        }
    }
    
    
    public class Test {
        public static void main(String[] args) {
            Context context1 = new Context(new LaoWang("飞机", "老王"));
            context1.TravelTool();
            Context context2 = new Context(new LaoZang("高铁", "老张"));
            context2.TravelTool();
            Context context3 = new Context(new XiaoWang("绿皮火车", "小汪"));
            context3.TravelTool();
        }
    }
    老王选择坐飞机去西藏旅游
    老张选择坐高铁去西藏旅游
    小汪选择坐绿皮火车去西藏旅游
    

    策略模式总结

    策略模式的主要优点如下:

    1. 策略模式提供了对 “开闭原则” 的完美支持,用户可以在不修改原有系统的基础上选择算法或行为,也可以灵活地增加新的算法或行为。

    2. 策略模式提供了管理相关的算法族的办法。策略类的等级结构定义了一个算法或行为族,恰当使用继承可以把公共的代码移到抽象策略类中,从而避免重复的代码。

    3. 策略模式提供了一种可以替换继承关系的办法。如果不使用策略模式而是通过继承,这样算法的使用就
      和算法本身混在一起,不符合 “单一职责原则”,而且使用继承无法实现算法或行为在程序运行时的动态切
      换。

    4. 使用策略模式可以避免多重条件选择语句。多重条件选择语句是硬编码,不易维护。

    5. 策略模式提供了一种算法的复用机制,由于将算法单独提取出来封装在策略类中,因此不同的环境类可以方便地复用这些策略类。

    策略模式的主要缺点如下:

    1. 客户端必须知道所有的策略类,并自行决定使用哪一个策略类。这就意味着客户端必须理解这些算法的区别,以便适时选择恰当的算法。换言之,策略模式只适用于客户端知道所有的算法或行为的情况。

    2. 策略模式将造成系统产生很多具体策略类,任何细小的变化都将导致系统要增加一个新的具体策略类。

    3. 无法同时在客户端使用多个策略类,也就是说,在使用策略模式时,客户端每次只能使用一个策略类,不支持使用一个策略类完成部分功能后再使用另一个策略类来完成剩余功能的情况。

    适用场景

    1. 一个系统需要动态地在几种算法中选择一种,那么可以将这些算法封装到一个个的具体算法类中,而这些具体算法类都是一个抽象算法类的子类。换言之,这些具体算法类均有统一的接口,根据 “里氏代换原则” 和面向对象的多态性,客户端可以选择使用任何一个具体算法类,并只需要维持一个数据类型是抽象算法类的对象。

    2. 一个对象有很多的行为,如果不用恰当的模式,这些行为就只好使用多重条件选择语句来实现。此时,使用策略模式,把这些行为转移到相应的具体策略类里面,就可以避免使用难以维护的多重条件选择语句。

    3. 不希望客户端知道复杂的、与算法相关的数据结构,在具体策略类中封装算法与相关的数据结构,可以提高算法的保密性与安全性。

    源码分析策略模式的典型应用

    Java Comparator 中的策略模式

    java.util.Comparator 接口是比较器接口,可以通过 Collections.sort(List,Comparator) 和 Arrays.sort(Object[],Comparator) 对集合和数据进行排序,下面为示例程序

    @Data
    @AllArgsConstructor
    public class Student {
        private Integer id;
        private String name;
    
        @Override
        public String toString() {
            return "{id=" + id + ", name='" + name + "'}";
        }
    }
    
    
    // 降序
    public class DescSortor implements Comparator<Student> {
        @Override
        public int compare(Student o1, Student o2) {
            return o2.getId() - o1.getId();
        }
    }
    
    // 升序
    public class AscSortor implements Comparator<Student> {
        @Override
        public int compare(Student o1, Student o2) {
            return o1.getId() - o2.getId();
        }
    }
    
    

    通过 Arrays.sort() 对数组进行排序

    public class Test1 {
        public static void main(String[] args) {
            Student[] students = {
                    new Student(3, "张三"),
                    new Student(1, "李四"),
                    new Student(4, "王五"),
                    new Student(2, "赵六")
            };
            toString(students, "排序前");
            
            Arrays.sort(students, new AscSortor());
            toString(students, "升序后");
            
            Arrays.sort(students, new DescSortor());
            toString(students, "降序后");
        }
    
        public static void toString(Student[] students, String desc){
            for (int i = 0; i < students.length; i++) {
                System.out.print(desc + ": " +students[i].toString() + ", ");
            }
            System.out.println();
        }
    }
    排序前: {id=3, name='张三'}, 排序前: {id=1, name='李四'}, 排序前: {id=4, name='王五'}, 排序前: {id=2, name='赵六'}, 
    升序后: {id=1, name='李四'}, 升序后: {id=2, name='赵六'}, 升序后: {id=3, name='张三'}, 升序后: {id=4, name='王五'}, 
    降序后: {id=4, name='王五'}, 降序后: {id=3, name='张三'}, 降序后: {id=2, name='赵六'}, 降序后: {id=1, name='李四'}, 
    
    

    通过 Collections.sort() 对集合List进行排序

    public class Test2 {
        public static void main(String[] args) {
            List<Student> students = Arrays.asList(
                    new Student(3, "张三"),
                    new Student(1, "李四"),
                    new Student(4, "王五"),
                    new Student(2, "赵六")
            );
            toString(students, "排序前");
            
            Collections.sort(students, new AscSortor());
            toString(students, "升序后");
    
            Collections.sort(students, new DescSortor());
            toString(students, "降序后");
        }
    
        public static void toString(List<Student> students, String desc) {
            for (Student student : students) {
                System.out.print(desc + ": " + student.toString() + ", ");
            }
            System.out.println();
        }
    }
    排序前: {id=3, name='张三'}, 排序前: {id=1, name='李四'}, 排序前: {id=4, name='王五'}, 排序前: {id=2, name='赵六'}, 
    升序后: {id=1, name='李四'}, 升序后: {id=2, name='赵六'}, 升序后: {id=3, name='张三'}, 升序后: {id=4, name='王五'}, 
    降序后: {id=4, name='王五'}, 降序后: {id=3, name='张三'}, 降序后: {id=2, name='赵六'}, 降序后: {id=1, name='李四'}, 
    
    

    我们向 Collections.sort() 和 Arrays.sort() 分别传入不同的比较器即可实现不同的排序效果(升序或降序)
    这里 Comparator 接口充当了抽象策略角色,两个比较器 DescSortor 和 AscSortor 则充当了具体策略角色,Collections 和 Arrays 则是环境角色

    Spring Resource 中的策略模式

    Spring 把所有能记录信息的载体,如各种类型的文件、二进制流等都称为资源,譬如最常用的Spring配置文件。

    在 Sun 所提供的标准 API 里,资源访问通常由 java.NET.URL 和文件 IO 来完成,尤其是当我们需要访问来自网络的资源时,通常会选择 URL 类。

    URL 类可以处理一些常规的资源访问问题,但依然不能很好地满足所有底层资源访问的需要,比如,暂时还无法从类加载路径、或相对于 ServletContext 的路径来访问资源,虽然 Java 允许使用特定的 URL 前缀注册新的处理类(例如已有的 http: 前缀的处理类),但是这样做通常比较复杂,而且 URL 接口还缺少一些有用的功能,比如检查所指向的资源是否存在等。

    Spring 改进了 Java 资源访问的策略,Spring 为资源访问提供了一个 Resource 接口,该接口提供了更强的资源访问能力,Spring 框架本身大量使用了 Resource 接口来访问底层资源。

    public interface Resource extends InputStreamSource {
        boolean exists();    // 返回 Resource 所指向的资源是否存在
        boolean isReadable();   // 资源内容是否可读
        boolean isOpen();   // 返回资源文件是否打开
        URL getURL() throws IOException;
        URI getURI() throws IOException;
        File getFile() throws IOException;  // 返回资源对应的 File 对象
        long contentLength() throws IOException;
        long lastModified() throws IOException;
        Resource createRelative(String var1) throws IOException;
        String getFilename();
        String getDescription();    // 返回资源的描述信息
    }
    
    

    Resource 接口是 Spring 资源访问策略的抽象,它本身并不提供任何资源访问实现,具体的资源访问由该接口的实现类完成——每个实现类代表一种资源访问策略。
    Spring 为 Resource 接口提供的部分实现类如下:

    1. UrlResource:访问网络资源的实现类。
    2. ClassPathResource:访问类加载路径里资源的实现类。
    3. FileSystemResource:访问文件系统里资源的实现类。
    4. ServletContextResource:访问相对于 ServletContext 路径里的资源的实现类:
    5. InputStreamResource:访问输入流资源的实现类。
    6. ByteArrayResource:访问字节数组资源的实现类。
    7. WritableResource:写资源文件

    类图如下:

    在这里插入图片描述
    可以看到 AbstractResource 资源抽象类实现了 Resource 接口,为子类通用的操作提供了具体实现,非通用的操作留给子类实现,所以这里也应用了模板方法模式。(只不过缺少了模板方法)

    Resource 不仅可在 Spring 的项目中使用,也可直接作为资源访问的工具类使用。意思是说:即使不使用 Spring 框架,也可以使用 Resource 作为工具类,用来代替 URL。

    譬如我们可以使用 UrlResource 访问网络资源。

    也可以通过其它协议访问资源,file: 用于访问文件系统;http: 用于通过 HTTP 协议访问资源;ftp: 用于通过 FTP 协议访问资源等

    public class Test {
        public static void main(String[] args) throws IOException {
            UrlResource ur = new UrlResource("http://image.laijianfeng.org/hello.txt");
    
            System.out.println("文件名:" + ur.getFilename());
            System.out.println("网络文件URL:" + ur.getURL());
            System.out.println("是否存在:" + ur.exists());
            System.out.println("是否可读:" + ur.isReadable());
            System.out.println("文件长度:" + ur.contentLength());
    
            System.out.println("\n--------文件内容----------\n");
            byte[] bytes = new byte[47];
            ur.getInputStream().read(bytes);
            System.out.println(new String(bytes));
        }
    }
    文件名:hello.txt
    网络文件URL:http://image.laijianfeng.org/hello.txt
    是否存在:true
    是否可读:true
    文件长度:47
    
    --------文件内容----------
    
    hello world!
    welcome to http://laijianfeng.org
    
    

    Spring Bean 实例化中的策略模式

    Spring实例化Bean有三种方式:构造器实例化、静态工厂实例化、实例工厂实例化

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    
        <bean id="person" class="com.demo.Person"></bean>
        
        <bean id="personWithParam" class="com.demo.Person">
            <constructor-arg name="name" value="小旋锋"/>
        </bean>
        
        <bean id="personWirhParams" class="com.demo.Person">
                <constructor-arg name="name" value="小旋锋"/>
                <constructor-arg name="age" value="22"/>
        </bean>
    </beans>
    
    

    具体实例化Bean的过程中,Spring中角色分工很明确,创建对象的时候先通过 ConstructorResolver 找到对应的实例化方法和参数,再通过实例化策略 InstantiationStrategy 进行实例化,根据创建对象的三个分支( 工厂方法、有参构造方法、无参构造方法 ), InstantiationStrategy 提供了三个接口方法:

    public interface InstantiationStrategy {
    	// 默认构造方法
    	Object instantiate(RootBeanDefinition beanDefinition, String beanName, BeanFactory owner) throws BeansException;
    
    	// 指定构造方法
    	Object instantiate(RootBeanDefinition beanDefinition, String beanName, BeanFactory owner, Constructor<?> ctor,
    			Object[] args) throws BeansException;
    
    	// 指定工厂方法
    	Object instantiate(RootBeanDefinition beanDefinition, String beanName, BeanFactory owner, Object factoryBean,
    			Method factoryMethod, Object[] args) throws BeansException;
    }
    
    

    InstantiationStrategy 为实例化策略接口,扮演抽象策略角色,有两种具体策略类,分别为 SimpleInstantiationStrategy 和 CglibSubclassingInstantiationStrategy
    在这里插入图片描述
    在 SimpleInstantiationStrategy 中对这三个方法做了简单实现,如果工厂方法实例化直接用反射创建对象,如果是构造方法实例化的则判断是否有 MethodOverrides,如果有无 MethodOverrides 也是直接用反射,如果有 MethodOverrides 就需要用 cglib 实例化对象,SimpleInstantiationStrategy 把通过 cglib 实例化的任务交给了它的子类 CglibSubclassingInstantiationStrategy。

    本文参考了小旋风的策略模式
    原文链接:https://blog.csdn.net/wwwdc1012/article/details/83152856

    展开全文
  • java策略模式

    千次阅读 2019-04-09 18:19:48
    在说策略模式之前,先看一个小例子: 假如:有一场演讲比赛,有十个评委对参赛选手的成绩进行打分,但最终要通过评委的平均分来决定选手的名次。现在有两种求平均分的策略: 第一种:将十名裁判的分加起来求平均值...

    示例:

    在说策略模式之前,先看一个小例子:
    假如:有一场演讲比赛,有十个评委对参赛选手的成绩进行打分,但最终要通过评委的平均分来决定选手的名次。现在有两种求平均分的策略:
    第一种:将十名裁判的分加起来求平均值。
    第二种:去掉最高分和最低分后求平均值。
    面对以上的需求,我们可能会写出如下代码:

    public class Averge {
    
        /**
         * @param a  每个裁判所打的分数
         * @param strategy 使用的策略
         * @return
         */
        public double getScore(double a[],String strategy){
    
            double score =0,sum = 0;
    
            //使用第一种策略打分
            if("策略一".equals(strategy)){
                System.out.println("使用策略1计算分数!");
                for (int i = 0; i < a.length; i++) {
                    sum = sum + a[i];
                }
                score = sum/a.length;
    
                return score;           //返回平均值
    
            //使用第二种策略打分
            }else{
                System.out.println("使用策略2计算分数!");
                Arrays.sort(a);
                for (int i = 1; i < a.length - 1; i++) {
                    sum = sum + a[i];
                }
                score = sum/(a.length - 2);
    
                return score;
            }
        }
    }
    

    以上代码工作的很好,完全没有问题。但是,上面的代码存在一些问题,比如:将所有的策略都放在了一个方法里面,显得方法非常臃肿(这里代码少可能看不出来)。此时开始修改代码,将每一种策略进行提取出来封装为方法method1,method2,然后在if else分支进行调用方法即可。代码如下:

    public class Averge {
    
        /**
         * @param a  每个裁判所打的分数
         * @param strategy 使用的策略
         * @return
         */
        public double getScore(double a[],String strategy){
    
            //使用第一种策略打分
            if("策略一".equals(strategy)){
    
                System.out.println("使用策略1计算分数!");
                return method1(a);
            //使用第二种策略打分
            }else{
    
                System.out.println("使用策略2计算分数!");
                return method2(a);
            }
        }
    
        public double method1(double a[]){
    
            double score =0,sum = 0;
            for (int i = 0; i < a.length; i++) {
                sum = sum + a[i];
            }
            score = sum/a.length;
            return score;
        }
    
        public double method2(double a[]){
    
            double score =0,sum = 0;
            Arrays.sort(a);
            for (int i = 1; i < a.length - 1; i++) {
                sum = sum + a[i];
            }
            score = sum/(a.length - 2);
            return score;
        }
    }
    
    

    经过如上的改造,我们的方法显得不是那么的臃肿,但是,如果说此时我们又要扩展一种计算平均分的策略呢(比如:增加一种只去掉最高分然后求平均分的打分策略)?
    此时我们首先需要在该类中增加一个求平均值的方法method3,然后编写方法实现,然后再增加 if–else 语句块。这样显得十分的麻烦,而且这样的设计也违背了设计原则之一的----开闭原则。

    开闭原则:

    对于扩展是开放的(Open for extension)。这意味着模块的行为是可以扩展的。当应用的需求改变时,我们可以对模块进行扩展,使其具有满足那些改变的新行为。也就是说,我们可以改变模块的功能。

    对于修改是关闭的(Closed for modification)。对模块行为进行扩展时,不必改动模块的源代码或者二进制代码。

    以上的代码设计,不易扩展并且不易维护。所以我们需要一些解决办法来将我们的代码符合开闭原则,并且易扩展和维护。所以我们开始使用策略模式来解决我们的问题。

    定义:

    策略模式定义了一系列的算法,把他们一个个封装起来,并且使他们可相互替换。该模式使得算法可独立于使用它的客户端而变化。

    结构:

    策略模式结构中包含了三种角色:1.策略 2.上下文 3.具体策略

    1. 策略:策略是一个接口,该接口定义若干算法标识,即定义了若干个抽象方法。
    2. 上下文(context):上下文是依赖接口的类(是面向策略二设计的类),即上下文中包含了策略变量。上下文中提供一个方法,该方法委托策略变量调用具体策略所实现的策略接口中的方法。
    3. 具体策略:具体策略是实现策略接口的类。具体策略实现策略接口所定义的抽象方法。
      以上的打分示例中,method1和method2就相当于一个个具体的策略!

    使用策略模式

    使用策略模式实现评委打分方法:
    1.策略接口:

    public interface Strategy {
        public double getAverge(double a[]);
    }
    

    2.具体策略实现:
    a). 策略A:直接求平均分

    public class StrategyA implements Strategy {
    
       @Override
       public double getAverge(double[] a) {
           
           double score =0,sum = 0;
           for (int i = 0; i < a.length; i++) {
               sum = sum + a[i];
           }
           score = sum/a.length;
           return score;
       }
    }
    

    b). 策略B:去掉最高分和最低分后求平均分

    public class StrategyB implements Strategy{
    
        @Override
        public double getAverge(double[] a) {
    
            double score =0,sum = 0;
            Arrays.sort(a);
            for (int i = 1; i < a.length - 1; i++) {
                sum = sum + a[i];
            }
            score = sum/(a.length - 2);
            return score;
        }
    }
    

    3.策略上下文

    /**
     * 策略上下文
     */
    public class StrategyContext {
    
        //策略变量的引用
        private Strategy strategy;
    
        public void setStrategy(Strategy strategy){
            this.strategy = strategy;
        }
    
        public double getAverge(double a[]){
            if(strategy != null){
                //调用引用策略的方法
                double averge = strategy.getAverge(a);
    
                return averge;
            }else {
                System.out.println("没有求平均值的策略!");
                return -1;
            }
        }
    }
    

    4.客户端

    public class Client {
    
        public static void main(String[] args) {
    
            //模拟评委打分
            double a[] = {1,6,8,4,5,7,4,7,8,9};
    
            //1.创建具体打分策略
            StrategyA strategyA = new StrategyA();
    
            //2.在创建策略上下文的同时,将具体的策略实现对象注入到策略上下文当中
            StrategyContext strategyContext = new StrategyContext(strategyA);
    
            //3.调用上下文对象的方法来完成对具体策略实现的回调
            double averge = strategyContext.getAverge(a);
            System.out.println("策略1:平均分:" + averge );
    
            //======================================================================
    
            //使用第二种求平均分的策略:
            StrategyB strategyB = new StrategyB();
            strategyContext.setStrategy(strategyB);
    
            //调用上下文对象的方法来完成对具体策略实现的回调
            averge = strategyContext.getAverge(a);
            System.out.println("策略2:平均分:" + averge );
    
        }
    }
    

    以上就是策略模式的简单例子实现。在以上示例中我们需要在客户端手动的设置选择哪个策略模式进行打分,其实我们也可以不用手动设置使用哪一个策略,直接在上下文中创建要优先使用的策略,若优先使用的策略执行失败,则进行另一个策略的使用,这样便不需要客户进行手动的设置要使用的策略模式了。示例代码如下:

    public class StrategyContext {
    
        //策略变量的引用
        private Strategy strategy;
    
        public double getAverge(double a[]){
            strategy = new StrategyA();
            try{
                //调用引用策略的方法
                double averge = strategy.getAverge(a);
                return averge;
            }catch (Exception e){
                //策略A失败
                strategy = new StrategyB();
                double averge = strategy.getAverge(a);
                return averge;
            }
        }
    }
    

    深入理解策略模式:

    **策略模式的作用:**就是把具体的算法实现从业务逻辑中剥离出来,成为一系列独立算法类,使得它们可以相互替换。

    **策略模式的着重点:**不是如何来实现算法,而是如何组织和调用这些算法,从而让我们的程序结构更加的灵活、可扩展。

    **策略和上下文的关系:**在策略模式中,一般情况下都是上下文持有策略的引用,以进行对具体策略的调用。但具体的策略对象也可以从上下文中获取所需数据,可以将上下文当做参数传入到具体策略中,具体策略通过回调上下文中的方法来获取其所需要的数据。

    这里关于在策略中回调上下文中的方法示例,引用一个比较好的例子:
    引用连接:https://www.cnblogs.com/lewis0077/p/5133812.html

    在跨国公司中,一般都会在各个国家和地区设置分支机构,聘用当地人为员工,这样就有这样一个需要:每月发工资的时候,中国国籍的员工要发人民币,美国国籍的员工要发美元,英国国籍的要发英镑。

    1 //支付策略接口

    public interface PayStrategy {
         //在支付策略接口的支付方法中含有支付上下文作为参数,以便在具体的支付策略中回调上下文中的方法获取数据
       public void pay(PayContext ctx);
     }
    
    //人民币支付策略
    public class RMBPay implements PayStrategy {
        @Override
        public void pay(PayContext ctx) {
            System.out.println("现在给:"+ctx.getUsername()+" 人民币支付 "+ctx.getMoney()+"元!");
        }
    }
    
    //美金支付策略
    public class DollarPay implements PayStrategy {
        @Override
        public void pay(PayContext ctx) {
            System.out.println("现在给:"+ctx.getUsername()+" 美金支付 "+ctx.getMoney()+"dollar !");
        }
    }
    
    //支付上下文,含有多个算法的公有数据
    public class PayContext {
       //员工姓名
       private String username;
       //员工的工资
       private double money;
       //支付策略
       private PayStrategy payStrategy;
    
       public void pay(){
           //调用具体的支付策略来进行支付
           payStrategy.pay(this);
       }
    
       public PayContext(String username, double money, PayStrategy payStrategy) {
           this.username = username;
           this.money = money;
           this.payStrategy = payStrategy;
       }
    
       public String getUsername() {	
           return username;
       }
    
       public double getMoney() {
           return money;
       }
    }
    
    //外部客户端
    public class Client {
        public static void main(String[] args) {
            //创建具体的支付策略
            PayStrategy rmbStrategy = new RMBPay();
            PayStrategy dollarStrategy = new DollarPay();
            //准备小王的支付上下文
            PayContext ctx = new PayContext("小王",30000,rmbStrategy);
            //向小王支付工资
            ctx.pay();
    
            //准备Jack的支付上下文
            ctx = new PayContext("jack",10000,dollarStrategy);
            //向Jack支付工资
            ctx.pay();
        }
    }
    

    控制台输出:

    现在给:小王 人民币支付 30000.0元!
    现在给:jack 美金支付 10000.0dollar !

    那现在我们要新增一个银行账户的支付策略,该怎么办呢?

    显然我们应该新增一个支付找银行账户的策略实现,由于需要从上下文中获取数据,为了不修改已有的上下文,我们可以通过继承已有的上下文来扩展一个新的带有银行账户的上下文,然后再客户端中使用新的策略实现和带有银行账户的上下文,这样之前已有的实现完全不需要改动,遵守了开闭原则。

    //银行账户支付
    public class AccountPay implements PayStrategy {
        @Override
        public void pay(PayContext ctx) {
            PayContextWithAccount ctxAccount = (PayContextWithAccount) ctx;
            System.out.println("现在给:"+ctxAccount.getUsername()+"的账户:"+ctxAccount.getAccount()+" 支付工资:"+ctxAccount.getMoney()+" 元!");
        }
    }
    
    //带银行账户的支付上下文
    public class PayContextWithAccount extends PayContext {
        //银行账户
        private String account;
        public PayContextWithAccount(String username, double money, PayStrategy payStrategy,String account) {
            super(username, money, payStrategy);
            this.account = account;
        }
    
        public String getAccount() {
            return account;
        }
    }
    
    //外部客户端
    public class Client {
        public static void main(String[] args) {
            //创建具体的支付策略
            PayStrategy rmbStrategy = new RMBPay();
            PayStrategy dollarStrategy = new DollarPay();
            //准备小王的支付上下文
            PayContext ctx = new PayContext("小王",30000,rmbStrategy);
            //向小王支付工资
            ctx.pay();
            //准备Jack的支付上下文
            ctx = new PayContext("jack",10000,dollarStrategy);
            //向Jack支付工资
            ctx.pay();
            //创建支付到银行账户的支付策略
            PayStrategy accountStrategy = new AccountPay();
            //准备带有银行账户的上下文
            ctx = new PayContextWithAccount("小张",40000,accountStrategy,"1234567890");
            //向小张的账户支付
            ctx.pay();
        }
    }
    

    控制台输出:

    现在给:小王 人民币支付 30000.0元!
    现在给:jack 美金支付 10000.0dollar !
    现在给:小张的账户:1234567890 支付工资:40000.0 元!

    除了上面的方法,还有其他的实现方式吗?

    当然有了,上面的实现方式是策略实现所需要的数据都是从上下文中获取,因此扩展了上下文;现在我们可以不扩展上下文,直接从策略实现内部来获取数据,看下面的实现:

    //支付到银行账户的策略
    public class AccountPay2 implements PayStrategy {
        //银行账户
        private String account;
        public AccountPay2(String account) {
            this.account = account;
        }
        @Override
        public void pay(PayContext ctx) {
            System.out.println("现在给:"+ctx.getUsername()+"的账户:"+getAccount()+" 支付工资:"+ctx.getMoney()+" 元!");
        }
        public String getAccount() {
            return account;
        }
        public void setAccount(String account) {
            this.account = account;
        }
    }
    
    //外部客户端
    public class Client {
        public static void main(String[] args) {
            //创建具体的支付策略
            PayStrategy rmbStrategy = new RMBPay();
            PayStrategy dollarStrategy = new DollarPay();
            //准备小王的支付上下文
            PayContext ctx = new PayContext("小王",30000,rmbStrategy);
            //向小王支付工资
            ctx.pay();
            //准备Jack的支付上下文
            ctx = new PayContext("jack",10000,dollarStrategy);
            //向Jack支付工资
            ctx.pay();
            //创建支付到银行账户的支付策略
            PayStrategy accountStrategy = new AccountPay2("1234567890");
            //准备上下文
            ctx = new PayContext("小张",40000,accountStrategy);
            //向小张的账户支付
            ctx.pay();
        }
    }
    

    控制台输出:

    现在给:小王 人民币支付 30000.0元!
    现在给:jack 美金支付 10000.0dollar !
    现在给:小张的账户:1234567890 支付工资:40000.0 元!

    总结:

    策略模式的优点:
    1. 上下文和具体策略是松耦合关系,因此,上下文只需要知道他要使用耨一个实现Strategy接口类的实例,但不需要知道具体是哪一个类。
    2. 策略模式满足“开-闭原则”,当增加新的具体策略时,不需要修改上下文类的代码,上下文就可以引用新的策略的实例。
    适合使用策略模式的场景:
    1. 一个类定义了多种行为,并且这些行为在这个类的方法中以多个条件语句的形式出现,那么使用策略模式避免在类中使用大量的条件语句。
    2. 程序的主类(相当于上下文角色),不希望暴露复杂的,与算法相关的数据结构,那么可以使用策略模式封装算法,即将算法分别封装到具体策略中。
    3. 需要使用一个算法的不同变体。

    引用一个大佬的总结:

    策略模式的优点:
      1.策略模式的功能就是通过抽象、封装来定义一系列的算法,使得这些算法可以相互替换,所以为这些算法定义一个公共的接口,以约束这些算法的功能实现。如果这些算法具有公共的功能,可以将接口变为抽象类,将公共功能放到抽象父类里面。
      2.策略模式的一系列算法是可以相互替换的、是平等的,写在一起就是if-else组织结构,如果算法实现里又有条件语句,就构成了多重条件语句,可以用策略模式,避免这样的多重条件语句。
      3.扩展性更好:在策略模式中扩展策略实现非常的容易,只要新增一个策略实现类,然后在使用策略实现的地方,使用这个新的策略实现就好了。
    策略模式的缺点:
      1.客户端必须了解所有的策略,清楚它们的不同:
         如果由客户端来决定使用何种算法,那客户端必须知道所有的策略,清楚各个策略的功能和不同,这样才能做出正确的选择,但是这暴露了策略的具体实现。
      2.增加了对象的数量:
        由于策略模式将每个具体的算法都单独封装为一个策略类,如果可选的策略有很多的话,那对象的数量也会很多。
      3.只适合偏平的算法结构:
        由于策略模式的各个策略实现是平等的关系(可相互替换),实际上就构成了一个扁平的算法结构。即一个策略接口下面有多个平等的策略实现(多个策略实现是兄弟关系),并且运行时只能有一个算法被使用。这就限制了算法的使用层级,且不能被嵌套。
    策略模式的本质:
      分离算法,选择实现。
      如果你仔细思考策略模式的结构和功能的话,就会发现:如果没有上下文,策略模式就回到了最基本的接口和实现了,只要是面向接口编程,就能够享受到面向接口编程带来的好处,通过一个统一的策略接口来封装和分离各个具体的策略实现,无需关系具体的策略实现。
      貌似没有上下文什么事,但是如果没有上下文的话,客户端就必须直接和具体的策略实现进行交互了,尤其是需要提供一些公共功能或者是存储一些状态的时候,会大大增加客户端使用的难度;引入上下文之后,这部分工作可以由上下文来完成,客户端只需要和上下文进行交互就可以了。这样可以让策略模式更具有整体性,客户端也更加的简单。
      策略模式体现了开闭原则:策略模式把一系列的可变算法进行封装,从而定义了良好的程序结构,在出现新的算法的时候,可以很容易的将新的算法实现加入到已有的系统中,而已有的实现不需要修改。
      策略模式体现了里氏替换原则:策略模式是一个扁平的结构,各个策略实现都是兄弟关系,实现了同一个接口或者继承了同一个抽象类。这样只要使用策略的客户端保持面向抽象编程,就可以动态的切换不同的策略实现以进行替换。

    展开全文
  • 策略模式:如果某种行为拥有多种运作方式,那么把这种行为视为一种策略,把这些运作方式视为策略实现,这些策略实现之间可以相互替换。 这样,行为调用方就可以在运行时,根据不同的需求,将相应的策略实现当做参数...

    超级链接: Java常用设计模式的实例学习系列-绪论

    参考:《HeadFirst设计模式》


    1.关于策略模式

    策略模式是一种行为模式。

    策略模式:定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换。本模式使得算法可独立于使用它的客户而变化。

    如果某种行为拥有多种运作方式,那么把这种行为视为一种策略,把这些运作方式视为策略实现,这些策略实现之间可以相互替换。

    这样,行为调用方就可以在运行时,根据不同的需求,将相应的策略实现当做参数,以达到行为按照某种方式运作的目的。

    本文以购物车的支付策略为场景来学习策略模式

    • 每个商品都有名称和价钱。
    • 购物车可以添加多个商品。
    • 购物车支付方式暂时只支持支付宝和微信,但是后期可能会增减更多支付方式。

    2.关于商品

    由于本文的关注对象是设计模式,所以这里直接列出商品的代码。

    商品:Goods

    /**
     * <p>商品</P>
     *
     * @author hanchao
     */
    @Setter
    @Getter
    @AllArgsConstructor
    public class Goods {
        /**
         * 商品名称
         */
        private String name;
    
        /**
         * 商品价格
         */
        private Float price;
    }
    

    3.不采用策略模式的购物车

    3.1.代码

    购物车:OrdinaryShoppingCart

    /**
     * <p>购物车(普通版本)</P>
     *
     * @author hanchao
     */
    @Slf4j
    public class OrdinaryShoppingCart {
        /**
         * 商品列表
         */
        private List<Goods> goodsList;
    
        public OrdinaryShoppingCart() {
            goodsList = new ArrayList<>();
        }
    
        /**
         * 添加商品
         */
        public OrdinaryShoppingCart addGoods(Goods goods) {
            goodsList.add(goods);
            return this;
        }
    
        /**
         * 计算总价
         */
        public float totalCost() {
            return goodsList.stream().map(Goods::getPrice).reduce(Float::sum).orElse(0f);
        }
    
        /**
         * 通过支付宝支付
         */
        public void payWithAlipay() {
            log.info("通过支付宝支付了" + totalCost() + "元.");
        }
    
        /**
         * 通过微信支付
         */
        public void payWithWeChat() {
            log.info("通过微信支付了" + totalCost() + "元.");
        }
    }
    
    • 因为有多种支付方式,所以每种支付方式分表编写了一个方法。

    测试代码:ShoppingCartDemo

    /**
     * <p>购物车场景测试</P>
     *
     * @author hanchao
     */
    public class ShoppingCartDemo {
        public static void main(String[] args) {
            //购物车(普通版本)
          	//张三的购物行为
            new OrdinaryShoppingCart()
                    .addGoods(new Goods("一箱牛奶", 34.55f))
                    .addGoods(new Goods("一瓶白酒", 250.50f))
                    .payWithAlipay();
    
          	//李四的购物行为
            new OrdinaryShoppingCart()
                    .addGoods(new Goods("一箱牛奶", 34.55f))
                    .addGoods(new Goods("一瓶啤酒", 3.50f))
                    .payWithWeChat();
        }
    }
    

    运行结果:

    2019-07-02 15:53:48,424  INFO [main-1] pers.hanchao.designpattern.strategy.ordinary.OrdinaryShoppingCart:44 - 通过支付宝支付了285.05. 
    2019-07-02 15:53:48,429  INFO [main-1] pers.hanchao.designpattern.strategy.ordinary.OrdinaryShoppingCart:51 - 通过微信支付了38.05. 
    

    3.2.分析

    优点:

    • 实现简单:代码逻辑很简单,并且只需要编写一个类即可。

    缺点:

    • 扩展性差,每增减一种支付方式,都需要修改OrdinaryShoppingCart,违背了设计模式的开放-封闭原则(OCP,对扩展开放,对修改关闭)。
    • 可复用低,每种支付方式都采用不同的方法命名。

    4.采用策略模式的购物车

    4.1.代码

    策略模式的关键在于:将行为及其运作方式抽象为策略和策略实现。对于本场景而言,

    • 支付就是一种行为,可以将其视为一种策略,即:支付策略。
    • 支付宝支付微信支付都是支付行为的不同运作方式,可以分别将其视为一种策略实现。

    支付策略:PayStrategy

    /**
     * <p>支付策略</P>
     *
     * @author hanchao
     */
    public interface PayStrategy {
        /**
         * 支付
         *
         * @param cost 支付金额
         */
        void pay(Float cost);
    }
    

    支付策略实现一:支付宝

    /**
     * <p>支付宝支付策略</P>
     *
     * @author hanchao
     */
    @Slf4j
    public class AliPayStrategy implements PayStrategy {
        @Override
        public void pay(Float cost) {
            log.info("通过支付宝支付了" + cost + "元.");
        }
    }
    

    支付策略实现二:微信

    /**
     * <p>微信支付策略</P>
     *
     * @author hanchao
     */
    @Slf4j
    public class WeChatPayStrategy implements PayStrategy {
        @Override
        public void pay(Float cost) {
            log.info("通过微信支付了" + cost + "元.");
        }
    }
    

    购物车(策略模式版本):StrategyShoppingCart

    /**
     * <p>购物车(策略模式版本)</P>
     *
     * @author hanchao
     */
    @Slf4j
    public class StrategyShoppingCart {
    
        /**
         * 商品列表
         */
        private List<Goods> goodsList;
    
        /**
         * 支付策略
         */
        private PayStrategy payStrategy;
    
        public StrategyShoppingCart() {
            goodsList = new ArrayList<>();
        }
    
        /**
         * 添加商品
         */
        public StrategyShoppingCart addGoods(Goods goods) {
            goodsList.add(goods);
            return this;
        }
    
        /**
         * 计算总价
         */
        public float totalCost() {
            return goodsList.stream().map(Goods::getPrice).reduce(Float::sum).orElse(0f);
        }
    
        /**
         * 选择支付策略
         */
        public StrategyShoppingCart setPayStrategy(PayStrategy payStrategy) {
            this.payStrategy = payStrategy;
            return this;
        }
    
        /**
         * 支付
         */
        public void pay() {
            if (Objects.isNull(payStrategy)) {
                throw new IllegalStateException("未选择支付策略");
            } else {
                payStrategy.pay(totalCost());
            }
        }
    }
    
    • 策略(抽象类/接口)PayStrategy作为客户端StrategyShoppingCart的成员变量,如此才能承载不同的策略实现。
    • 提供策略实现的setter方法setPayStrategy(PayStrategy payStrategy),为策略实现的切换提供入口。

    测试代码:ShoppingCartDemo

    /**
     * <p>购物车场景测试</P>
     *
     * @author hanchao
     */
    public class ShoppingCartDemo {
        public static void main(String[] args) {
            //购物车(策略模式版本)
            //王五的购物行为
            new StrategyShoppingCart()
                    .addGoods(new Goods("一箱牛奶", 34.55f))
                    .addGoods(new Goods("一瓶白酒", 250.50f))
                    .setPayStrategy(new AliPayStrategy()).pay();
    
            //赵六的购物行为
            new StrategyShoppingCart()
                    .addGoods(new Goods("一箱牛奶", 34.55f))
                    .addGoods(new Goods("一瓶啤酒", 3.50f))
                    .setPayStrategy(new WeChatPayStrategy()).pay();
        }
    }
    

    运行结果:

    2019-07-02 17:47:07,309  INFO [main-1] pers.hanchao.designpattern.strategy.strategy.strategy.impl.AliPayStrategy:15 - 通过支付宝支付了285.05. 
    2019-07-02 17:47:07,310  INFO [main-1] pers.hanchao.designpattern.strategy.strategy.strategy.impl.WeChatPayStrategy:15 - 通过微信支付了38.05. 
    

    新需求:新的支付方式:银联支付

    当新的需求产生,比方说,新增一种支付方式:银联支付。

    此时,只需要新增一种支付策略实现UnionPayStrategy即可,无需修改原有代码。

    新的支付策略实现三:UnionPayStrategy

    /**
     * <p>银联支付策略</P>
     *
     * @author hanchao
     */
    @Slf4j
    public class UnionPayStrategy implements PayStrategy {
        @Override
        public void pay(Float cost) {
            log.info("通过银联支付了" + cost + "元.");
        }
    }
    

    测试代码:ShoppingCartDemo

    /**
     * <p>购物车场景测试</P>
     *
     * @author hanchao
     */
    public class ShoppingCartDemo {
        public static void main(String[] args) {
            //购物车(策略模式版本)
            //孙七的购物行为
            new StrategyShoppingCart()
                    .addGoods(new Goods("一箱牛奶", 34.55f))
                    .addGoods(new Goods("一双袜子", 23.50f))
                    .setPayStrategy(new UnionPayStrategy()).pay();
        }
    }
    

    运行结果:

    2019-07-02 17:55:51,102  INFO [main-1] pers.hanchao.designpattern.strategy.strategy.strategy.impl.UnionPayStrategy:15 - 通过银联支付了58.05. 
    

    4.2.分析

    优点:

    • 扩展性好,每增减一种支付方式,只需增减一种支付策略实现即可,无需修改OrdinaryShoppingCart,遵从了设计模式的开放-封闭原则(OCP,对扩展开放,对修改关闭)。
    • 可复用高,每种支付方式都采用相同的方法命名。

    缺点:

    • 实现复杂:如果有n种策略,则需要n个策略实现类,再加一个策略类。

    5.实际案例

    HystrixCommand熔断器的默认隔离侧率是线程池隔离,可以通过setter方法修改为信号量隔离,代码如下:

    		super(Setter
    			.withGroupKey(HystrixCommandGroupKey.Factory.asKey("myGroupKey"))
                    .andCommandPropertiesDefaults(
                    	HystrixCommandProperties.Setter()
                        	//选择隔离策略:信号量
    						.withExecutionIsolationStrategy(HystrixCommandProperties.ExecutionIsolationStrategy.SEMAPHORE)
                    ) 
           	);
    

    6.总结

    最后以UML类图来总结本文的购物车付款场景以及策略模式。

    在这里插入图片描述

    展开全文
  • 前言 项目中有这样一个场景,在公园放置了用来拍摄人像的识别杆,根据用户在不同识别杆之间采集的图象来计算用户的...现在就以实战的角度带领大家来学习策略模式,以及如何将ifelse重构为基于SpringBoot的策略模式
  • 一、策略模式 策略模式定义了一组算法,将每个算法都封装起来,并且使它们之间可以互换 1、策略模式主要角色 主要角色如下: 封装角色(Context):也叫做上下文角色,起承上启下封装作用,屏蔽高层模块对策略、...
  • 这次介绍一下策略模式(Strategy Pattern),相比之下是一种比较简单的模式。它也叫政策模式(Policy Pattern)。 策略模式使用的就是面向对象的继承和多态机制,其他的没有什么玄机。策略模式适合使用在: 1. 多个...
  • 设计模式(一):策略模式

    万次阅读 多人点赞 2019-03-01 21:37:32
    设计模式(一):策略模式 本文将以一个小Demo及其新需求来分析使用策略模式的好处。 设计模式简述: 设计模式: 1.设计模式是人们在面对同类型软件工程设计问题所总结出的一些有用的经验。模式不是代码,而是某类...
  • 策略模式(策略设计模式)详解

    千次阅读 2019-05-23 17:30:35
    策略模式(策略设计模式)详解 在现实生活中常常遇到实现某种目标存在多种策略可供选择的情况,例如,出行旅游可以乘坐飞机、乘坐火车、骑自行车或自己开私家车等,超市促销可以釆用打折、送商品、送积分等方法。 ...
  • 原来使用 Spring 实现策略模式可以这么简单!

    千次阅读 多人点赞 2021-02-01 11:07:36
    策略模式作为一种软件设计模式,指对象有某个行为,但是在不同的场景中,该行为有不同的实现算法,可以替代代码中大量的 if-else。比如我们生活中的场景:买东西结账可以使用微信支付、支付宝...
  • 策略模式(Strategy Pattern)中,一个类的行为或其算法可以在运行时更改。这种类型的设计模式属于行为型模式。 策略模式java-demo 在策略模式中,我们创建表示各种策略的对象和一个行为随着策略对象改变而改变的 ...
  • 模板方法模式和策略模式的应用场景浅析 一、定义 二、举个例子 1.杀鱼 2.做鱼 三、伪代码实现 1.杀鱼 1.1分析 1.2实现 2.做鱼 2.1分析 2.2实现 四、总结 最近闲下来整理一下模板方法模式和策略模式的区别和应用场景 ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 697,682
精华内容 279,072
关键字:

策略模式