设计模式之策略模式

/ 设计模式 / 没有评论 / 463浏览

今天和大家分享一下设计模式中的策略模式,这里只是分享楼主自己的见解,如有考虑不恰当的地方,还请理解,那么我们言归正传。由于楼主自己工作的原因,常常需要将数据库中资源数据生成相应的静态化文件(json文件),也就是俗称的打包。资源数据可能有很多种类型。例如:新闻、电影、小说等。不同的类型,在打包时,有不一样的流程,例如新闻和电影就有很大的不同,新闻在打包后还有要自动上传功能,这是因为对新闻的实效性要求很高,自动上传成功后,会自动调用下发服务器的下发指令,来自动更新APP中的新闻数据。而电影就不需要有此功能,一是因为电影的打包文件相比新闻太大,所以没必要调用自动上传接口,因为这样上传会比较慢,还不一定能保证电影包的完整性。二是因为电影会因一些版权等原因,需要人工审批。所以,电影和新闻相比就少了一个自动上传的功能。

下面我们按照上面的需求来设计我们相关的类,虽然新闻和电影有不同的打包流程,但我们分析后知道,除了生成json文件(数据不同)和自动上传功能不同外,其他的流程是可以公用的,那么按照面向对象的思想,我们应该设计出一个超类,将新闻和电影两个模块中相同的打包流程,在超类中实现,将两个模块不同的打包流程设计成抽象方法让它们的子类去实现,这样就可以将相同的打包流程复用了。具体的代码如下:

public abstract class AppPack {

/**
* 初始化 如:创建相应文件夹、定义一些打包参数等
*/
public void doInit() {
System.out.println("初始化");
}

/**
* 生成json文件
*/
public abstract void createJson();

/**
* 将生成的资源包打包ZIP文件
*/
public void doZip() {
System.out.println("打包ZIP文件");
}

/**
* ZIP文件自动上传 (只有新闻有此流程)
*/
public abstract void doUpload();
}
public class NewsAppPack extends AppPack {

public void createJson() {
System.out.println("生成新闻json文件");
}

public void doUpload() {
System.out.println("自动上传ZIP");
}
}
public class VideoAppPack extends AppPack {

public void createJson() {
System.out.println("生成电影json文件");
}

public void doUpload() {
// 因为电影没有此功能所以什么都不需要写
}
}
public class Test {

public static void pack(AppPack appPack) {
appPack.doInit();
appPack.createJson();
appPack.doZip();
appPack.doUpload();
}

public static void main(String[] args) {
AppPack appPack = new NewsAppPack(); pack(appPack);
System.out.println("---------------"); appPack = new VideoAppPack()
pack(appPack);
}

}
初始化
生成新闻json文件
打包ZIP文件
自动上传ZIP包
---------------
初始化
生成电影json文件
打包ZIP文件

现在看似乎没有什么问题,但是如果,我要把小说,游戏等模块都添加进来呢?上面的设计似乎就不太合理了,因为按照上面的设计,每一个子类都会默认继承doUpload()方法,按照我们之前的分析,暂时只有新闻模块是需要这个功能,其他的模块都不需要,那么这时我们子类就会包括大量的空实现。又如果我们的需求变了,其他的模块也需要有上传功能,但是是不同于新闻模块的上传逻辑的,那么上面的设计的代码就不用复用了,这就违背了面向对象的思想了。

现在我们知道使用继承并不能很好的解决我们遇到的问题,现在我们只能借助于设计模式了。我们首先看设计模式中的第一个原则:

在说的简单点就是,如果每次我们有新的需求开发时,如果要修改曾经已经编码好的代码时,那么我们基本可以确定,要把这一部分提取出来,和其他已经编写好的稳定的代码分开。

按照我们之前的分析,我们知道打包类中只有两部分是变化的,也就是生成json文件和自动上传这两个方法,所以我们把这两个方法提取出来,这样我们就可以把这两个打包流程和真正的打包类分离了。

既然我们已经知道了要把生成json文件和自动上传功能要分离,那我们到底要怎么分离呢,也就是我们到底创建相关的类分离还是接口分离呢?这里还有一个可以遵循的设计模式原则,也就是本篇中的第二个设计模式原则:

针对接口编程的意思是说针对超类型编程,针对接口编程的好处是,可以利用多态,这样程序执行的时候,会根据真正执行的子类类型来执行。

在说的简单点就是我们知道一个接口可以有很多个实现类,至于一共有哪些实现类,接口是不需要知道的,调用时只要实例化这个实现类并指向该接口就可执行调用,如果有新的实现类,只要新实例化这个新类即可,对接口方法的调用没有改变,这样就很方便扩展。

既然我们知道应该针对接口编程,那么我们需要把生成json文件和自动上传这两个打包流程设计成两个接口,然后设计不同的子类实现它们,让子类去实现它们原有的逻辑。下面是具体的代码。

public interface CreateJson {

/**
* 生成json文件
*/
public void createJson();
}
public interface Upload {
/**
* ZIP文件自动上传
*/
public void doUpload();

}
public class NewsCreateJson implements CreateJson {

public void createJson() {
System.out.println("生成新闻json文件");
}
}
public class VidwoCreateJson implements CreateJson {

public void createJson() {
System.out.println("生成电影json文件");
}
}
public class NeedUpload implements Upload {

public void doUpload() {
System.out.println("ZIP文件自动上传");
}
}
public class NoNeedUpload implements Upload {

public void doUpload() {
System.out.println("ZIP文件不自动上传");
}
}

这样我们已经把生成json文件和自动上传两个流程的接口设计完了,那我们怎么改原先的打包类呢?按照我们上面了解的那样,我们需要针对接口编程,所以,我们需要把我们新设计的json文件和自动上传这两个流程的接口放到原先的打包类中,而原先的打包类中已经不需要自己处理这两部分流程了,具体的流程而是我们已经设计好的相关的子类去实现。这样我们就可以用多态的特性,动态改变它们的执行流程了。具体代码如下:

public abstract class AppPack {

public CreateJson createJson; // 生成json文件接口
public Upload upload; // 上传文件接口

/**
* 初始化 如:创建相应文件夹、定义一些打包参数等
*/
public void doInit() {
System.out.println("初始化");
}

/**
* 生成json文件
*/
public void createJson() {
createJson.createJson(); // 相关的子类去实现
}

/**
* 将生成的资源包打包ZIP文件
*/
public void doZip() {
System.out.println("打包ZIP文件");
}

/**
* ZIP文件自动上传
*/
public void doUpload() {
upload.doUpload(); // 相关的子类去实现
}
}
public class NewsAppPack extends AppPack {

public NewsAppPack() {
createJson = new NewsCreateJson(); // 调用生成新闻json的子类
upload = new NeedUpload(); // 调用自动上传的子类
}

}
public class VideoAppPack extends AppPack {

public VideoAppPack() {
createJson = new VidwoCreateJson(); // 调用生成电影的json子类
upload = new NoNeedUpload(); // 调用不自动上传的子类
}
}
public class Test {

public static void pack(AppPack appPack) {
appPack.doInit();
appPack.createJson();
appPack.doZip();
appPack.doUpload();
}

public static void main(String[] args) {
AppPack appPack = new NewsAppPack(); pack(appPack); System.out.println("---------------"); appPack = new VideoAppPack()    pack(appPack);
}

}
初始化
生成新闻json文件
打包ZIP文件
ZIP文件自动上传
---------------
初始化
生成电影json文件
打包ZIP文件
ZIP文件不自动上传

这样我们已经把生成json文件和自动上传相关的流程已经和打包类分离了。这样设计不但方便了我们日后扩展,我们还可以直接动态的改变原有的流程。具体代码如下:

public abstract class AppPack {

public CreateJson createJson; // 生成json文件接口
public Upload upload; // 上传文件接口

/**
* 初始化 如:创建相应文件夹、定义一些打包参数等
*/
public void doInit() {
System.out.println("初始化");
}

/**
* 生成json文件
*/
public void createJson() {
createJson.createJson(); // 相关的子类去实现
}

/**
* 将生成的资源包打包ZIP文件
*/
public void doZip() {
System.out.println("打包ZIP文件");
}

/**
* ZIP文件自动上传
*/
public void doUpload() {
upload.doUpload(); // 相关的子类去实现
}

/**
* 设置生成json文件策略
* @param createJson
*/
public void setCreateJson(CreateJson createJson) {
this.createJson = createJson;
}

/**
* 设置自动上传策略
*
* @param upload
*/
public void setUpload(Upload upload) {
this.upload = upload;
}
}
public class Test {

public static void pack(AppPack appPack) {
appPack.doInit();
appPack.createJson();
appPack.doZip();
appPack.doUpload();
}

public static void main(String[] args) {
AppPack appPack = new NewsAppPack();
pack(appPack);
appPack.setUpload(new NoNeedUpload());
System.out.println("---------------");
pack(appPack);
}

}

这样我们就可以动态的改变原有打包类中的打包逻辑了。

这就是策略模式的具体应用。下面我们看策略模式的具体定义。

策略模式的定义:定义了算法组,分别封装起来,让它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。

下面是策略模式的类图:

WX20171118-233752.png

这就是我对设计模式中策略模式的理解 ,如本文有不正确之处,欢迎指出。谢谢。