纯净、安全、绿色的下载网站

首页|软件分类|下载排行|最新软件|IT学院

当前位置:首页IT学院IT技术

策略模式 策略模式:告别if else

活跃的咸鱼   2021-06-08 我要评论
想了解策略模式:告别if else的相关内容吗活跃的咸鱼在本文为您仔细讲解策略模式的相关知识和一些Code实例欢迎阅读和指正我们先划重点:策略模式,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方法违反了 “开闭原则”系统的灵活性和可扩展性较差

算法的复用性差如果在另一个系统中需要重用某些算法只能通过对源代码进行复制粘贴来重用无法单独重用其中的某个或某些算法

策略模式

策略模式的介绍

策略模式(Strategy Pattern)中定义算法族分别封装起来让他们之间可以互相替换此模式让算法的变化独立于使用算法的客户这算法体现了几个设计原则

第一、把变化的代码从不变的代码中分离出来

第二、针对接口编程而不是具体类(定义了策略接口)

第三、多用组合/聚合少用继承(客户通过组合方式使用策略)

策略模式的原理类图

在这里插入图片描述

角色分析

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.lResource:访问网络资源的实现类

2.assPathResource:访问类加载路径里资源的实现类

3.leSystemResource:访问文件系统里资源的实现类

4.rvletContextResource:访问相对于ServletContext 路径里的资源的实现类:

5.putStreamResource:访问输入流资源的实现类

6.teArrayResource:访问字节数组资源的实现类

7.itableResource:写资源文件

类图如下

在这里插入图片描述

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有三种方式:构造器实例化、静态工厂实例化、实例工厂实例化

具体实例化Bean的过程中Spring中角色分工很明确创建对象的时候先通

<?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>

过 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

在这里插入图片描述

leInstantiationStrategy 中对这三个方法做了简单实现如果工厂方法实例化直接用反射创建对象如果是构造方法实例化的则判断是否有 MethodOverrides如果有无 MethodOverrides 也是直接用反射如果有 MethodOverrides 就需要用 cglib 实例化对象SimpleInstantiationStrategy 把通过 cglib 实例化的任务交给了它的子类ibSubclassingInstantiationStrategy

总结

1.略类之间可以自由切换由于策略类实现自同一个抽象所以他们之间可以自由切换

2.于扩展增加一个新的策略对策略模式来说非常容易基本上可以在不改变原有代码的基础上进行扩展

3.使用多重条件如果不使用策略模式对于所有的算法必须使用条件语句进行连接通过条件判断来决定使用哪一种算法在上一篇文章中我们已经提到使用多重条件判断是非常不容易维护的

以上就是java策略模式的详细内内容也请关注其它相关文章!


相关文章

猜您喜欢

  • java实现接口签名 接口签名怎么用Java实现

    想了解接口签名怎么用Java实现的相关内容吗灰太狼_cxh在本文为您仔细讲解java实现接口签名的相关知识和一些Code实例欢迎阅读和指正我们先划重点:java实现接口签名,java接口签名下面大家一起来学习吧..
  • OpenCV图像分割与提取 OpenCV-Python使用分水岭算法实现图像的分割与提取

    想了解OpenCV-Python使用分水岭算法实现图像的分割与提取的相关内容吗一天一篇Python库在本文为您仔细讲解OpenCV图像分割与提取的相关知识和一些Code实例欢迎阅读和指正我们先划重点:OpenCV图像分割与提取,OpenCV图像分割,OpenCV图像提取下面大家一起来学习吧..

网友评论

Copyright 2020 www.fresh-weather.com 【世纪下载站】 版权所有 软件发布

声明:所有软件和文章来自软件开发商或者作者 如有异议 请与本站联系 点此查看联系方式