基本介绍:对扩展开放对修改关闭,用抽象构建框架 用实现扩展细节!
所谓对扩展开放,对修改关闭,其实是设计模式里面所重点提倡的,后续所有涉及模式的介绍其实都是为了程序能够更好的扩展,提倡设计模式的本质就是为了减少 增加一点功能而修改系统的主要逻辑
!
用抽象构建框架 用实现扩展细节! 尽量通过扩展类的操作来实现行为变化,而不是通过修改已有代码来实现修改
下面还是老规矩,我们先看一段错误的代码!
需求1:我需要一个过滤特殊字符的功能
package principle.opencloeds.error;
/**
* 字符串格式化
* 规则:
* 1.剔除 html标签
* @author huangfu
*/
public class StringFormat {
/**
* 格式化字符串
* 剔除 html标签
* @param data 格式化的元数据
* @return 格式化后的数据
*/
public String stringFormat(String data) {
if (data != null) {
return return data.replaceAll("</?[^>]+>", "").replaceAll("\\s*|\t|\r|\n", "");
}
return null;
}
}
package principle.opencloeds.error.test;
import principle.opencloeds.error.StringFormat;
/**
* 数据格式化测试
* @author huangfu
*/
public class TestStringFormat {
private static final String MSG = "<span>程序必须首先让人类理解,然后顺便让机器能执行</span>/96";
public static void main(String[] args) {
StringFormat stringFormat = new StringFormat();
System.out.println(stringFormat.stringFormat(MSG));
}
}
程序必须首先让人类理解,然后顺便让机器能执行/96
Process finished with exit code 0
这样写乍一看没有任何问题,功能也实现了,但是突然有一天,领导需要你将一些特殊字符替换成特定的字符比如:/96
替换成 ^_^
!
那么苦逼的程序员又要去更改整个代码逻辑,于是代码被改成这样!
package principle.opencloeds.error;
/**
* 字符串格式化
* 规则:
* 1.剔除 < 或者 > 字符
* @author huangfu
*/
public class StringFormat {
/**
* 格式化字符串
* 剔除 < 或者 > 字符
* @param data 格式化的元数据
* @return 格式化后的数据
*/
public String stringFormat(String data) {
if (data != null) {
String specialCharactersFormat = data.replace("/96", "^_^");
return specialCharactersFormat.replaceAll("</?[^>]+>", "").replaceAll("\\s*|\t|\r|\n", "");
}
return null;
}
}
程序必须首先让人类理解,然后顺便让机器能执行^_^
Process finished with exit code 0
开发中唯一不变的就是变化
,所以我们不敢保证那一次领导又有新的需求来让你修改,每一次都修改代码是很伤的,其实上述例子还好,只是修改代码逻辑,如果有时候连参数都要改变的话,你一旦修改参数,对上层系统很不友好,对于参数的改变有两种解决方案:
参数替换的方法我不做过多讲解,上面的两种方案其实说的很明白,有兴趣的读者可以自己实现一下试试,我们继续聊现有的一段逻辑!
还记得我开头说的一句话吗?
用抽象构建框架 用实现扩展细节!
对,我们需要将经常变化的逻辑抽取出来,作为实现使用,这些经常改变的代码就是我们代码里面的一些扩展点!我们不妨尝试一下,抽象一下代码,让他的扩展性更好!
抽象出来一个接口,用于构建整个程序的框架!
package principle.opencloeds.correct;
/**
* 字符串格式化接口
* @author huangfu
*/
public interface StringFormat {
/**
* 是否生效
* @return true生效反之不生效
*/
boolean enable();
/**
* 字符串格式化逻辑
* @param data 元数据
* @return 格式化后的数据
*/
String stringFormat(String data);
}
定制接口的实现,通过扩展类的操作来实现行为变化!
HTML标签过滤
package principle.opencloeds.correct;
/**
* html的格式化
*/
public class HtmlStringFormat implements StringFormat {
@Override
public boolean enable() {
return true;
}
@Override
public String stringFormat(String data) {
return data.replaceAll("</?[^>]+>", "").replaceAll("\\s*|\t|\r|\n", "");
}
}
package principle.opencloeds.correct;
/**
* 特殊字符格式化
* @author huangfu
*/
public class SpecialStringFormat implements StringFormat {
@Override
public boolean enable() {
return true;
}
@Override
public String stringFormat(String data) {
return data.replace("/96", "^_^");
}
}
定义一个使用的工具类
package principle.opencloeds.correct;
import java.util.ArrayList;
import java.util.List;
/**
* 格式化上下文
* @author huangfu
*/
public class StringFormatUtil {
private static final List<StringFormat> stringFormats = new ArrayList<>(8);
public static void addFormatSpecification(StringFormat StringFormat) {
if (StringFormat != null) {
stringFormats.add(StringFormat);
}
}
public static String stringFormat(String data) {
for (StringFormat stringFormat : stringFormats) {
if (stringFormat.enable()) {
data = stringFormat.stringFormat(data);
}
}
return data;
}
}
package principle.opencloeds.correct;
/**
* @author huangfu
*/
public class TestStringFormat {
private static final String MSG = "<span>程序必须首先让人类理解,然后顺便让机器能执行</span>/96<div>脏话</div>";
public static void main(String[] args) {
StringFormatUtil.addFormatSpecification(new HtmlStringFormat());
StringFormatUtil.addFormatSpecification(new SpecialStringFormat());
System.out.println(StringFormatUtil.stringFormat(MSG));
}
}
程序必须首先让人类理解,然后顺便让机器能执行^_^脏话
Process finished with exit code 0
这样写咋一看比之前增加了很多的类,逻辑也变多了,但是如果上级要求将一些敏感字眼用xxx替换,我们再也不需要修改代码原有的逻辑只需要这样!
增加敏感词处理类
package principle.opencloeds.correct;
/**
* 敏感词过滤替换
* @author huangfu
*/
public class SensitiveStringFormat implements StringFormat {
@Override
public boolean enable() {
return true;
}
@Override
public String stringFormat(String data) {
if (data != null) {
return data.replace("脏话","xxxx");
}
return null;
}
}
package principle.opencloeds.correct;
/**
* @author huangfu
*/
public class TestStringFormat {
private static final String MSG = "<span>程序必须首先让人类理解,然后顺便让机器能执行</span>/96<div>脏话</div>";
public static void main(String[] args) {
StringFormatUtil.addFormatSpecification(new HtmlStringFormat());
StringFormatUtil.addFormatSpecification(new SpecialStringFormat());
StringFormatUtil.addFormatSpecification(new SensitiveStringFormat());
System.out.println(StringFormatUtil.stringFormat(MSG));
}
}
程序必须首先让人类理解,然后顺便让机器能执行^_^xxxx
Process finished with exit code 0
通过上述代码可以知道,开闭原则是是为了避免过多的修改原有的代码逻辑,用扩展代替修改而衍生的一个原则,不可否认,他也是所有的设计模式都想表达的一个结果,高扩展性。
对拓展开放是为了应对变化(需求),对修改关闭是为了保证已有代码的稳定性;最终结果是为了让系统更有弹性!
开闭原则也并不是免费的。有些情况下,代码的扩展性会跟可读性相冲突。比如,我们之前举的 StringFormat格式化的例子。为了更好地支持扩展性,我们对代码进行了重构,重构之后的代码要比之前的代码复杂很多,理解起来也更加有难度。很多时候,我们都需要在扩展性和可读性之间做权衡。在某些场景下,代码的扩展性很重要,我们就可以适当地牺牲一些代码的可读性;在另一些场景下,代码的可读性更加重要,那我们就适当地牺牲一些代码的可扩展性。