前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Java设计模式(三)策略模式

Java设计模式(三)策略模式

作者头像
每天学Java
发布2020-06-01 18:01:34
4280
发布2020-06-01 18:01:34
举报
文章被收录于专栏:每天学Java每天学Java

在软件工程中,行为型设计模式为设计模式的一种类型,用来识别对象之间的常用交流模式并加以实现。如此,可以在交流时增强灵活性。

01

入门小例子

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

我们来看一下下面的小例子,首先我们定义了一个公共接口,然后它有两个实现类,分别输出 每天学Java 和 每天学Java之策略模式学习。

代码语言:javascript
复制
public interface Strategy {
    public void strategyInter();
}

class StrategyImpO implements Strategy {

    @Override
    public void strategyInter() {
        System.out.println("每天学Java");
    }
}

class StrategyImp1 implements Strategy {

    @Override
    public void strategyInter() {
        System.out.println("每天学Java之策略模式学习");
    }
}

如果在平时我们会一般会这样去调用这两个实现类的方法

代码语言:javascript
复制
        Strategy strategy0 = new StrategyImpO();
        Strategy strategy1 = new StrategyImp1();

        strategy0.strategyInter();
        strategy1.strategyInter();

假设一下这个接口的实现类有很多,而且每个实现类都在不同地方被多次实现,那是不是意味这我们重复写了很多相同的代码。遇到这种情况你可能写一个中间类(具体策略角色:包装了相关的算法或行为),其构造方法参数是这个接口的实例,在客户端调用的时候去传入不同的实现类来达到减少重复代码。

代码语言:javascript
复制
class Context {
    private Strategy strategy;

    /**
     * 构造函数,传入具体的策略对象
     *
     * @param strategy
     */
    public Context(Strategy strategy) {
        this.strategy = strategy;
    }

    /**
     * 调用策略方法
     */
    public void method() {
            strategy.strategyInter();
    }
}

然后客户端这样去调用:

代码语言:javascript
复制
        A:new Context(new StrategyImpO()).method();
        B:new Context(new StrategyImp1()).method();

这种方式确实比上一种方法高级一些,但是这种方式很好吗?我们来看一下,如果Strategy接口有N个实现类,C客户端在实现Context这个接口时候,难道你要让他了解每一个实现类吗?这种方法是不合理的。拿自己来说,我们公司的产品再调用第三方的接口的时候,一般给的文档肯定是这种形式:

代码语言:javascript
复制
method(int/String/...... 参数) // 参数1代表什么,参数2代表什么,参数3代表什么

不会是

代码语言:javascript
复制
method(接口 参数)//接口实例类1是什么,接口实例类2是什么。

再有就是:假如接口的实例类A突然不能用了,我们要换一个新的实现类来替代,然后你给客户打电话说,那个方法的参数不能用了,你要改成XXX实现类。这肯定不合适

说白一点,就是耦合性太高了。(耦合性:也称块间联系。指软件系统结构中各模块间相互联系紧密程度的一种度量。模块之间联系越紧密,其耦合性就越强,模块的独立性则越差。模块间耦合高低取决于模块间接口的复杂性、调用的方式及传递的信息。)

那么我们一般在那个中间类上面做一下改动:

代码语言:javascript
复制
class Context {
    private Strategy strategy;

    /**
     * 调用策略方法
     */
    public void method(int param) {
        if (param > 0) {
            strategy = new StrategyImpO();

        } else if (param < 0) {
            strategy = new StrategyImp1();
        }
        strategy.strategyInter();
    }
}

这样改动之后,在后续即使StrategyImpO有了更高级的类去代替,我们也仅仅需要修改一下这个Context类,并不会影响客户端的调用(在实际工作中,客户根本不会关心你的接口内部有了什么改动,不影响我使用就可以了,如果你接口内部有了改动,作为客户的我也要有相应的改动,我肯定会不开心)。

看到这里,我想大家就明白类什么是策略模式:

策略模式定义和封装了一系列的算法,它们是可以相互替换的,也就是说它们具有共性,而它们的共性就体现在策略接口的行为上,另外为了达到最后一句话的目的,也就是说让算法独立于使用它的客户而独立变化,我们需要让客户端依赖于策略接口

02

深化策略模式

在上面我们初步的玩了一下策略模式,但是我们回归一下最开始的问题,如果Strategy接口有N个实现类,是用来策略模式后,虽然客户端不在关心接口实现类的改动,但是这个工作量放在服务端也是很烦的,我想没有一个开发人员想去写N个if else语句,那么这个时候该怎么办呢?(推荐一篇优秀的博文:大家可以去看看https://blog.csdn.net/u012124438/article/details/70039943/)

那就是使用注解, 所以我们需要给注解加入属性的值,用来解决判断实例化那一个实现类的问题。

首先我们写一个注解接口:如果对于注解还不是很清楚,可以看一下解读Java 注解 (Annotation)

代码语言:javascript
复制
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface TraInterface {
    int param() default 0;
}

这个注解中param就是我们要去传的值

代码语言:javascript
复制
@TraInterface(param = 1)
class StrategyImpO implements Strategy {

    @Override
    public void strategyInter() {
        System.out.println("每天学Java");
    }
}

@TraInterface(param = -1)
class StrategyImp1 implements Strategy {

    @Override
    public void strategyInter() {
        System.out.println("每天学Java之策略模式学习");
    }
}

也就是说,通过这个注解,我们来判断调用那个实现类。现在我们来通过一个工厂方法来实现具体的功能:

代码语言:javascript
复制
class FactoryStrategy {
    private ClassLoader classLoader = getClass().getClassLoader();
    //策略列表,也就是实现类的列表
    private List<Class<? extends Strategy>> list;
    public Strategy createStrategy(int val) {
        for (Class<? extends Strategy> claszz : list) {
            TraInterface traInterface = handleAnnotation(claszz);
            if (traInterface.param() == val) {
                //是的话我们返回一个当前策略的实例
                try {
                    return claszz.newInstance();
                } catch (InstantiationException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }

            }
        }
        throw new RuntimeException("策略获得失败");
    }

    //处理注解,我们传入一个策略类,返回它的注解
    private TraInterface handleAnnotation(Class<? extends Strategy> clazz) {
        Annotation[] annotations = clazz.getDeclaredAnnotations();
        if (annotations == null || annotations.length == 0) {
            return null;
        }
        for (int i = 0; i < annotations.length; i++) {
            if (annotations[i] instanceof TraInterface) {
                return (TraInterface) annotations[i];
            }
        }
        return null;
    }

    //单例
    private FactoryStrategy() {
        init();
    }

    //在工厂初始化时要初始化策略列表
    private void init() {
        list = new ArrayList<Class<? extends Strategy>>();
        System.out.println(FactoryStrategy.class.getResource(""));
        File[] resources = getResources();//获取到包下所有的class文件
        Class<Strategy> calPriceClazz = null;
        try {
            calPriceClazz = (Class<Strategy>) classLoader.loadClass(Strategy.class.getName());//使用相同的加载器加载策略接口
        } catch (ClassNotFoundException e1) {
            throw new RuntimeException("未找到策略接口");
        }
        for (int i = 0; i < resources.length; i++) {
            try {
                //载入包下的类
                Class<?> clazz = classLoader.loadClass(resources[i].getName().replace(".class", ""));
                //判断是否是CalPrice的实现类并且不是CalPrice它本身,满足的话加入到策略列表
                if (Strategy.class.isAssignableFrom(clazz) && clazz != calPriceClazz) {
                    list.add((Class<? extends Strategy>) clazz);
                }
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }
    }

    //获取扫描的包下面所有的class文件
    private File[] getResources() {
        try {
            System.out.println(classLoader.getResource(""));
            File file = new File(classLoader.getResource("").toURI());
            return file.listFiles(new FileFilter() {
                public boolean accept(File pathname) {
                    if (pathname.getName().endsWith(".class")) {//我们只扫描class文件
                        return true;
                    }
                    return false;
                }
            });
        } catch (URISyntaxException e) {
            throw new RuntimeException("未找到策略资源");
        }
    }


    public static FactoryStrategy getInstance() {
        return CalPriceFactoryInstance.instance;
    }

    private static class CalPriceFactoryInstance {
        private static FactoryStrategy instance = new FactoryStrategy();
    }

}

而Context类只需要有如下代码即可实现去除if else语句

代码语言:javascript
复制
class Context {
    private Strategy strategy;

    /**
     * 调用策略方法
     */
    public void method(int param) {
        strategy = FactoryStrategy.getInstance().createStrategy(param);
        strategy.strategyInter();
    }
}

而客户端不用改变

代码语言:javascript
复制
  public static void main(String[] args) {
        new Context().method(-1);
    }

如果大家对于第二部分不是很明白,可以手动去实现试试,还是很有意思的。


今天小程序更细题库:进入小程序

1.什么是URL?如何创建URL?

2.什么情况下需要序列化?序列化的注意事项,如何实现java 序列化(串行化)?

3.序列化有哪些注意事项?

4.java中有几种类型的流?JDK为每种类型的流提供了一些抽象类以供继承,请说出他们分别是哪些类?

5.HTTP和HTTPS的区别

这里是今天策略模式的源码:

代码语言:javascript
复制
import java.io.File;
import java.io.FileFilter;
import java.lang.annotation.*;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.List;

/**
 * @Auther: chenlong
 * @Date: 2018/10/2 10:37
 * @Description:策略
 */
public interface Strategy {
    public void strategyInter();
}

@TraInterface(param = 1)
class StrategyImpO implements Strategy {

    @Override
    public void strategyInter() {
        System.out.println("每天学Java");
    }
}

@TraInterface(param = -1)
class StrategyImp1 implements Strategy {

    @Override
    public void strategyInter() {
        System.out.println("每天学Java之策略模式学习");
    }
}

class Context {
    private Strategy strategy;

    /**
     * 调用策略方法
     */
    public void method(int param) {
        strategy = FactoryStrategy.getInstance().createStrategy(param);
        strategy.strategyInter();
    }
}

class FactoryStrategy {
    private ClassLoader classLoader = getClass().getClassLoader();
    private List<Class<? extends Strategy>> list;//策略列表,也就是实现类的列表

    public Strategy createStrategy(int value) {
        for (Class<? extends Strategy> claszz : list) {
            TraInterface traInterface = handleAnnotation(claszz);
            if (traInterface.param() == value) {
                //是的话我们返回一个当前策略的实例
                try {
                    return claszz.newInstance();
                } catch (InstantiationException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }

            }
        }
        throw new RuntimeException("策略获得失败");
    }

    //处理注解,我们传入一个策略类,返回它的注解
    private TraInterface handleAnnotation(Class<? extends Strategy> clazz) {
        Annotation[] annotations = clazz.getDeclaredAnnotations();
        if (annotations == null || annotations.length == 0) {
            return null;
        }
        for (int i = 0; i < annotations.length; i++) {
            if (annotations[i] instanceof TraInterface) {
                return (TraInterface) annotations[i];
            }
        }
        return null;
    }

    //单例
    private FactoryStrategy() {
        init();
    }

    //在工厂初始化时要初始化策略列表
    private void init() {
        list = new ArrayList<Class<? extends Strategy>>();
        System.out.println(FactoryStrategy.class.getResource(""));
        File[] resources = getResources();//获取到包下所有的class文件
        Class<Strategy> calPriceClazz = null;
        try {
            calPriceClazz = (Class<Strategy>) classLoader.loadClass(Strategy.class.getName());//使用相同的加载器加载策略接口
        } catch (ClassNotFoundException e1) {
            throw new RuntimeException("未找到策略接口");
        }
        for (int i = 0; i < resources.length; i++) {
            try {
                //载入包下的类
                Class<?> clazz = classLoader.loadClass(resources[i].getName().replace(".class", ""));
                //判断是否是CalPrice的实现类并且不是CalPrice它本身,满足的话加入到策略列表
                if (Strategy.class.isAssignableFrom(clazz) && clazz != calPriceClazz) {
                    list.add((Class<? extends Strategy>) clazz);
                }
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }
    }

    //获取扫描的包下面所有的class文件
    private File[] getResources() {
        try {
            System.out.println(classLoader.getResource(""));
            File file = new File(classLoader.getResource("").toURI());
            return file.listFiles(new FileFilter() {
                public boolean accept(File pathname) {
                    if (pathname.getName().endsWith(".class")) {//我们只扫描class文件
                        return true;
                    }
                    return false;
                }
            });
        } catch (URISyntaxException e) {
            throw new RuntimeException("未找到策略资源");
        }
    }


    public static FactoryStrategy getInstance() {
        return CalPriceFactoryInstance.instance;
    }

    private static class CalPriceFactoryInstance {
        private static FactoryStrategy instance = new FactoryStrategy();
    }

}

class Client {
    public static void main(String[] args) {
        new Context().method(-1);
    }
}

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface TraInterface {
    int param() default 0;
}
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2018-10-05,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 每天学Java 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
云开发 CloudBase
云开发(Tencent CloudBase,TCB)是腾讯云提供的云原生一体化开发环境和工具平台,为200万+企业和开发者提供高可用、自动弹性扩缩的后端云服务,可用于云端一体化开发多种端应用(小程序、公众号、Web 应用等),避免了应用开发过程中繁琐的服务器搭建及运维,开发者可以专注于业务逻辑的实现,开发门槛更低,效率更高。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档