本文我们将讨论策略模式,是非常常用的一个模式,我们应该熟悉掌握,策略模式的定义:定义一系列算法,每个都封装起来可换着使用,策略使算法独立于使用它的客户端而变化。使用策略模式可以使我们的代码设计高内聚、松耦合。
在应用开发中,你将经常编写一些利用不同算法来满足一些业务需求的对象。一个常见的例子是支持数字排序的类,可以用冒泡排序、合并排序和快排。类似的,一个文件压缩类可以支持不同的压缩算法,比如ZIP,GZIP,LZ4 或者一个定制的压缩算法。另一个例子是数据加密类可以用不同的加密算法,例如AES,TripleDES 和 Blowfish。通常,我们倾向于在一个类中支持所有算法,这样会导致这个类很庞大,里面充满了多个 switch 语句 或者条件判断语句。下面是一个例子,来展示一个类来支持多个加密数据算法。
public class Encryptor {
private String algorithmName;
private String plainText;
public Encryptor(String algorithmName){
this.algorithmName=algorithmName;
}
public void encrypt(){
if (algorithmName.equals("Aes")){
System.out.println("Encrypting data using AES algorithm");
/*Code to encrypt data using AES algorithm*/
}
else if (algorithmName.equals("Blowfish")){
System.out.println("Encrypting data using Blowfish algorithm");
/*Code to encrypt data using Blowfish algorithm */
}
/*More else if statements for other encryption algorithms*/
}
/*Getter and setter methods for plainText*/
}
这个类使用了条件判断语句来判断执行不同的加密算法。在运行时,根据客户端指定的算法去执行相应的语句。这样写会使代码紧耦合和难以改变,如果试着实现一个新的加密算法,可以想象对原有代码改动所带来的的后果,你不得不在类 Encryptor
基础上进行修改,或者当一个已存在的算法需要修改时,此类也需要更改。这时聪明的你已经发现,Encryptor
类违反了SOLID设计原则中的 开闭原则
,新算法应该通过新增代码的方式添加,而不是修改原有的代码,这种违反原则的发生是因为我们没有遵循面向对象编程最佳实践的基本原则 封装不同的部分
。
这些不好的设计如果贯穿整个应用中,最终会导致开发的应用很脆弱,需求变化时代码很难维护,这种情况可以策略模式很好的解决。使用策略模式,我们定义一系列相关的算法,用单独的类去封装各个算法,客户端代码可在运行时选择某个算法。这样做,我们可以很容易的添加新算法、修改原有的算法或者删除不再用的算法。每个算法类符合另外一个SOLID 原则-单一职责
原则,除此之外,把算法封装在各个小类里,可以更好的针对特殊场景进行单元测试。
继续拿上面数据加密的例子来说,首先定义一个接口,不同的加密算法来实现,接口命名 EncryptionStrategy
,两个实现类为 AesEncryptionStrategy
和 BlowfishEncryptionStrategy
,这两个类就是我们说的策略。
我们后续会重构上面的类 Encyptor
,删除所有条件判断语句,把一个加密请求委托给一个具体的加密算法。
我们来总结下策略模式的参与者:
现在利用策略模式来实现上面提到的 Encryptor
类
public interface EncryptionStrategy {
void encryptData(String plainText);
}
public class AesEncryptionStrategy implements EncryptionStrategy{
@Override
public void encryptData(String plaintext) {
System.out.println("-------Encrypting data using AES algorithm-------");
try {
KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
keyGenerator.init(128);
SecretKey secretKey = keyGenerator.generateKey();
byte[] plaintTextByteArray = plaintext.getBytes("UTF8");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
byte[] cipherText = cipher.doFinal(plaintTextByteArray);
System.out.println("Original data: " + plaintext);
System.out.println("Encrypted data:");
for (int i = 0; i < cipherText.length; i++) {
System.out.print(cipherText[i] + " ");
}
}
catch(Exception ex){
ex.printStackTrace();
}
}
}
public class BlowfishEncryptionStrategy implements EncryptionStrategy{
@Override
public void encryptData(String plaintext) {
System.out.println("\n-------Encrypting data using Blowfish algorithm-------");
try {
KeyGenerator keyGenerator = KeyGenerator.getInstance("Blowfish");
keyGenerator.init(128);
SecretKey secretKey = keyGenerator.generateKey();
byte[] plaintTextByteArray = plaintext.getBytes("UTF8");
Cipher cipher = Cipher.getInstance("Blowfish");
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
byte[] cipherText = cipher.doFinal(plaintTextByteArray);
System.out.println("Original data: " + plaintext);
System.out.println("Encrypted data:");
for (int i = 0; i < cipherText.length; i++) {
System.out.print(cipherText[i] + " ");
}
}
catch(Exception ex){
ex.printStackTrace();
}
}
}
public class Encryptor {
private EncryptionStrategy strategy;
private String plainText;
public Encryptor(EncryptionStrategy strategy){
this.strategy=strategy;
}
public void encrypt(){
strategy.encryptData(plainText);
}
public String getPlainText() {
return plainText;
}
public void setPlainText(String plainText) {
this.plainText = plainText;
}
}
public class EncryptorTest {
@Test
public void testEncrypt() throws Exception {
EncryptionStrategy aesStrategy=new AesEncryptionStrategy();
Encryptor aesEncryptor=new Encryptor(aesStrategy);
aesEncryptor.setPlainText("This is plain text");
aesEncryptor.encrypt();
EncryptionStrategy blowfishStrategy=new BlowfishEncryptionStrategy();
Encryptor blowfishEncryptor=new Encryptor(blowfishStrategy);
blowfishEncryptor.setPlainText("This is plain text");
blowfishEncryptor.encrypt();
}
}
测试代码输出:
------------------------------------------------------
T E S T S
-------------------------------------------------------
-------Encrypting data using AES algorithm-------
Original data: This is plain text
Encrypted data:
97 11 54 73 -21 92 109 -124 -120 110 -43 20 123 99 87 120 120 95 70 -63 -8 70 41 44 -73 -94 48 127 61 43 -96 110
-------Encrypting data using Blowfish algorithm-------
Original data: This is plain text
Encrypted data:
3 -34 -23 -74 -61 -55 99 -114 71 -113 124 -57 -65 -45 -128 37 -123 -83 118 107 42 -123 84 14