策略模式:定义一系列算法,把它们一个个封装起来,并且使他们可以相互替换.
在某些设计中,一些类的设计人员经常遇到这样的问题:由于用户需求的变化,导致经常需要修改类中的某个方法,即需要不断的改变算法.
面对这样的问题,不用担心,面向对象编程有一个很好的设计原则"面向抽象编程",该原则的核心就是将类中经常需要变化的部分分割出来,
并且把每种可能的变化对应的交给抽象类的一个子类去负责,从而让类的设计者不用去关心具体实现.
策略模式结构中包括三个角色:
策略:策略是一个接口,该接口定义了若干个算法标识,即定义了若干个抽象方法.
具体策略:具体策略是实现策略接口的类,具体策略实现策略接口所定义的抽象方法,即给出算法标识的具体算法.
上下文:上下文是依赖于策略接口的类,即上下文中包含策略声明的变量,并且为此变量提供了set方法,最后还提供了一个方法用来委托策略变量
调用具体策略的方法.
应用场景一:在某个比赛中,有若干个评委为选手评分,请给出几种策略根据评委的评分为选手计算一个最后结果.
另外策略模式满足"开闭原则",即当增加新的具体策略时,不需要修改上下文类的的代码就可以引用具体策略的实例.
在某些设计中,一些类的设计人员经常遇到这样的问题:由于用户需求的变化,导致经常需要修改类中的某个方法,即需要不断的改变算法.
面对这样的问题,不用担心,面向对象编程有一个很好的设计原则"面向抽象编程",该原则的核心就是将类中经常需要变化的部分分割出来,
并且把每种可能的变化对应的交给抽象类的一个子类去负责,从而让类的设计者不用去关心具体实现.
策略模式结构中包括三个角色:
策略:策略是一个接口,该接口定义了若干个算法标识,即定义了若干个抽象方法.
具体策略:具体策略是实现策略接口的类,具体策略实现策略接口所定义的抽象方法,即给出算法标识的具体算法.
上下文:上下文是依赖于策略接口的类,即上下文中包含策略声明的变量,并且为此变量提供了set方法,最后还提供了一个方法用来委托策略变量
调用具体策略的方法.
应用场景一:在某个比赛中,有若干个评委为选手评分,请给出几种策略根据评委的评分为选手计算一个最后结果.
1.策略接口类,直接看代码:StrategyInterface.
<span style="font-size:12px;">package com.ilucky.pattern.strategy.one;
/**
* @author IluckySi
* @date 20140604
*/
public interface StrategyInterface {
public abstract double computeScore(double[] scores);
}</span>
2.具体策略类,直接看代码StrategyOne,StrategyTwo,StrategyThree.
<span style="font-size:12px;"> * @author IluckySi
* @date 20140604
*/
//计算代数平均值.
public class StrategyOne implements StrategyInterface {
@Override
public double computeScore(double[] scores) {
double average = 0.0;
double sum = 0.0;
for(int i = 0; scores != null && i < scores.length; i++) {
sum += scores[i];
}
average = sum / scores.length;
return average;
}
}</span>
<span style="font-size:12px;">package com.ilucky.pattern.strategy.one;
import java.util.Arrays;
/**
* @author IluckySi
* @date 20140604
*/
//计算几何平均值.
public class StrategyTwo implements StrategyInterface {
@Override
public double computeScore(double[] scores) {
double average = 0.0;
double sum = 1.0;
Arrays.sort(scores);
for(int i = 0; i < scores.length; i++) {
sum *= scores[i];
}
average = Math.pow(sum, 1.0 / scores.length);
return average;
}
}
</span>
<span style="font-size:12px;">package com.ilucky.pattern.strategy.one;
import java.util.Arrays;
/**
* @author IluckySi
* @date 20140604
*/
//计算去掉一个最高分和最低分代数平均值.
public class StrategyThree implements StrategyInterface {
@Override
public double computeScore(double[] scores) {
double average = 0.0;
double sum = 0.0;
Arrays.sort(scores);
for(int i = 1; i < scores.length - 1; i++) {
sum += scores[i];
}
average = sum / (scores.length - 2);
return average;
}
}</span>
3.上下文类,直接看代码Context.
<span style="font-size:12px;">package com.ilucky.pattern.strategy.one;
/**
* @author IluckySi
* @date 20140604
*/
public class Context {
private StrategyInterface strategyInterface;
public void setStrategyInterface(StrategyInterface strategyInterface) {
this.strategyInterface = strategyInterface;
}
public double computeScore(double[] scores) {
return strategyInterface.computeScore(scores);
}
}</span>
4.最后看测试类MainTest。
<span style="font-size:12px;">package com.ilucky.pattern.strategy.one;
/**
* @author IluckySi
* @date 20140604
*/
public class MainTest {
public static void main(String[] args) {
//模拟评分.
double[] scores = new double[6];
scores[0] = 50;
scores[1] = 68;
scores[2] = 72;
scores[3] = 85;
scores[4] = 90;
scores[5] = 96;
//创建上下文.
Context context = new Context();
//计算代数平均值.
StrategyInterface strategyOne = new StrategyOne();
context.setStrategyInterface(strategyOne);
double resultOne = context.computeScore(scores);
System.out.println("代数平均值:" + resultOne);
//计算几何平均值.
StrategyInterface strategyTwo = new StrategyTwo();
context.setStrategyInterface(strategyTwo);
double resultTwo = context.computeScore(scores);
System.out.println("几何平均值:" + resultTwo);
//计算去掉一个最高分和最低分代数平均值.
StrategyInterface strategyThredd = new StrategyThree();
context.setStrategyInterface(strategyThredd);
double resultThreee = context.computeScore(scores);
System.out.println("去掉一个最高分和最低分代数平均值:" + resultThreee);
}
}
/**
运行结果:
代数平均值:76.83333333333333
几何平均值:75.12607167549113
去掉一个最高分和最低分代数平均值:78.75
*/</span>
应用场景二:在某个数据传输业务中,需要为文件提供几种策略用来为文件加密解密.1.策略接口类,直接看代码:StrategyInterface.
<span style="font-size:12px;">package com.ilucky.pattern.strategy.two;
import java.io.File;
/**
* @author IluckySi
* @date 20140605
*/
public interface StrategyInterface {
public abstract void encrypt(File file);
public abstract void decrypt(File file);
}</span>
2.具体策略类,直接看代码StrategyOne,StrategyTwo.
<span style="font-size:12px;">package com.ilucky.pattern.strategy.two;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
/**
* @author IluckySi
* @date 20140605
*/
public class StrategyOne implements StrategyInterface {
private String password;
public StrategyOne() {
this.password = "IluckySi";
}
public StrategyOne(String password) {
this.password = password;
}
/**
* 使用一个字符串做密码,比如用IluckySi做密码,首先将IluckySi转换为一个字节数组,然后获取bytes数组的长度length,
* 最后将文件的内容按顺序以length个字节为一组,对每一组中的字节和bytes字节数组中对应的字节做加法运算.
*/
@Override
public void encrypt(File file) {
byte[] passwordBytes = password.getBytes();
FileInputStream fis = null;
BufferedInputStream bis = null;
FileOutputStream fos = null;
BufferedOutputStream bos = null;
try {
//对文件内容加密.
fis = new FileInputStream(file);
bis = new BufferedInputStream(fis);
long fileLength = file.length();
byte[] bytes = new byte[(int)fileLength];
//重点:将文件流内容转换为byte数组.
int bytesLength = bis.read(bytes);
for(int i = 0; i < bytesLength; i++) {
int encrpyt = bytes[i] + passwordBytes[i % passwordBytes.length];
bytes[i] = (byte)encrpyt;
}
//对文件内容加完密再写回文件.
fos = new FileOutputStream(file);
bos = new BufferedOutputStream(fos);
fos.write(bytes, 0 , bytesLength);
System.out.println("文件" + file.getPath() + "已经成功完成加密!");
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if(bos != null) {
bos.close();
bos = null;
}
if(fos != null) {
fos.close();
fos = null;
}
if(bis != null) {
bis.close();
bis = null;
}
if(fis != null) {
fis.close();
fis = null;
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
/**
* 将文件按密码的长度分组, 然后和密码对应的字节数组做减法运算.
*/
@Override
public void decrypt(File file) {
byte[] passwordBytes = password.getBytes();
FileInputStream fis = null;
BufferedInputStream bis = null;
FileOutputStream fos = null;
BufferedOutputStream bos = null;
try {
//对文件内容解密.
fis = new FileInputStream(file);
bis = new BufferedInputStream(fis);
long fileLength = file.length();
byte[] bytes = new byte[(int)fileLength];
//重点:将文件流内容转换为byte数组.
int bytesLength = bis.read(bytes);
for(int i = 0; i < bytesLength; i++) {
int encrpyt = bytes[i] - passwordBytes[i % passwordBytes.length];
bytes[i] = (byte)encrpyt;
}
//对文件内容解完密再写回文件.
fos = new FileOutputStream(file);
bos = new BufferedOutputStream(fos);
fos.write(bytes);
System.out.println("文件" + file.getPath() + "已经成功完成解密!");
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if(bos != null) {
bos.close();
bos = null;
}
if(fos != null) {
fos.close();
fos = null;
}
if(bis != null) {
bis.close();
bis = null;
}
if(fis != null) {
fis.close();
fis = null;
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
</span>
<span style="font-size:12px;">package com.ilucky.pattern.strategy.two;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
/**
* @author IluckySi
* @date 20140606
*/
public class StrategyTwo implements StrategyInterface {
private String password;
public StrategyTwo() {
this.password = "IluckySi";
}
public StrategyTwo(String password) {
this.password = password;
}
/**
* 使用一个字符串做密码,比如用IluckySi做密码,首先将IluckySi转换为一个字节数组,然后获取bytes数组的长度length,
* 最后将文件的内容按顺序以length个字节为一组,对每一组中的字节和bytes字节数组中对应的字节做异或运算.
*/
@Override
public void encrypt(File file) {
byte[] passwordBytes = password.getBytes();
FileInputStream fis = null;
BufferedInputStream bis = null;
FileOutputStream fos = null;
BufferedOutputStream bos = null;
try {
//对文件内容加密.
fis = new FileInputStream(file);
bis = new BufferedInputStream(fis);
long fileLength = file.length();
byte[] bytes = new byte[(int)fileLength];
//重点:将文件流内容转换为byte数组.
int bytesLength = bis.read(bytes);
for(int i = 0; i < bytesLength; i++) {
int encrpyt = bytes[i] ^ passwordBytes[i % passwordBytes.length];
bytes[i] = (byte)encrpyt;
}
//对文件内容加完密再写回文件.
fos = new FileOutputStream(file);
bos = new BufferedOutputStream(fos);
fos.write(bytes, 0 , bytesLength);
System.out.println("文件" + file.getPath() + "已经成功完成加密!");
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if(bos != null) {
bos.close();
bos = null;
}
if(fos != null) {
fos.close();
fos = null;
}
if(bis != null) {
bis.close();
bis = null;
}
if(fis != null) {
fis.close();
fis = null;
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
/**
* 将文件按密码的长度分组, 然后和密码对应的字节数组做异或运算.
*/
@Override
public void decrypt(File file) {
byte[] passwordBytes = password.getBytes();
FileInputStream fis = null;
BufferedInputStream bis = null;
FileOutputStream fos = null;
BufferedOutputStream bos = null;
try {
//对文件内容解密.
fis = new FileInputStream(file);
bis = new BufferedInputStream(fis);
long fileLength = file.length();
byte[] bytes = new byte[(int)fileLength];
//重点:将文件流内容转换为byte数组.
int bytesLength = bis.read(bytes);
for(int i = 0; i < bytesLength; i++) {
int encrpyt = bytes[i] ^ passwordBytes[i % passwordBytes.length];
bytes[i] = (byte)encrpyt;
}
//对文件内容解完密再写回文件.
fos = new FileOutputStream(file);
bos = new BufferedOutputStream(fos);
fos.write(bytes);
System.out.println("文件" + file.getPath() + "已经成功完成解密!");
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if(bos != null) {
bos.close();
bos = null;
}
if(fos != null) {
fos.close();
fos = null;
}
if(bis != null) {
bis.close();
bis = null;
}
if(fis != null) {
fis.close();
fis = null;
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}</span>
重点:将文件流内容转换为byte数组.:byte[] bytes = new byte[(int)fileLength],int bytesLength = bis.read(bytes)。3.上下文类,直接看代码Context.
<span style="font-size:12px;">package com.ilucky.pattern.strategy.two;
import java.io.File;
/**
* @author IluckySi
* @date 20140605
*/
public class Context {
private StrategyInterface strategyInterface;
public void setStrategyInterface(StrategyInterface strategyInterface) {
this.strategyInterface = strategyInterface;
}
public void encrypt(File file) {
strategyInterface.encrypt(file);
}
public void decrypt(File file) {
strategyInterface.decrypt(file);
}
}</span>
4.最后看测试类MainTest.
<span style="font-size:12px;">package com.ilucky.pattern.strategy.two;
import java.io.File;
/**
* @author IluckySi
* @date 20140605
*/
public class MainTest {
public static void main(String[] args) {
//测试文件.
File one = new File("E:/one.txt");
File two = new File("E:/two.txt");
//创建上下文.
Context context = new Context();
//通过第一种策略加密解密.
StrategyInterface strategyOne = new StrategyOne();
context.setStrategyInterface(strategyOne);
context.encrypt(one);
context.decrypt(one);
//通过第二种策略加密解密.
StrategyInterface strategyTwo = new StrategyTwo();
context.setStrategyInterface(strategyTwo);
context.encrypt(two);
context.decrypt(two);
}
}
</span>
策略模式的优点:上下文和具体策略是松耦合关系,即上下文中包含一个策略变量,但不需要知道具体的策略是什么.另外策略模式满足"开闭原则",即当增加新的具体策略时,不需要修改上下文类的的代码就可以引用具体策略的实例.