设计原则之开闭原则
基本介绍:对扩展开放对修改关闭,用抽象构建框架 用实现扩展细节!
所谓对扩展开放,对修改关闭,其实是设计模式里面所重点提倡的,后续所有涉及模式的介绍其实都是为了程序能够更好的扩展,提倡设计模式的本质就是为了减少 增加一点功能而修改系统的主要逻辑
!
用抽象构建框架 用实现扩展细节! 尽量通过扩展类的操作来实现行为变化,而不是通过修改已有代码来实现修改
错误的示范
下面还是老规矩,我们先看一段错误的代码!
需求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格式化的例子。为了更好地支持扩展性,我们对代码进行了重构,重构之后的代码要比之前的代码复杂很多,理解起来也更加有难度。很多时候,我们都需要在扩展性和可读性之间做权衡。在某些场景下,代码的扩展性很重要,我们就可以适当地牺牲一些代码的可读性;在另一些场景下,代码的可读性更加重要,那我们就适当地牺牲一些代码的可扩展性。
- 举例讲解Go语言中函数的闭包使用
- 实现WordPress提交评论的时删除该页面的WP-Super-Cache缓存的方法
- php自动生成百度开放适配PC页-手机页pattern对应关系sitemap.xml
- Linux系统最大文件打开数优化,解决Too many open files报错
- 【Dev Club 分享】安卓单元测试:What, Why and How
- WordPress记住评论用户信息的js版本,直接操作cookie无视缓存
- 禁止百度转码和百度快照缓存的META声明
- 单机MySQL数据库优化推荐的编译安装参数
- Linux网络实时流量监测工具iftop的安装使用
- Golang学习--GroupCache的使用
- 基于 Webpack & Vue & Vue-Router 的 SPA 初体验
- GO语言高并发学习心得体会例
- Go 语言实现的网络连接池:Pool
- go语言操作redis连接池的方法
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法