基本介绍:对扩展开放对修改关闭,用抽象构建框架 用实现扩展细节!
所谓对扩展开放,对修改关闭,其实是设计模式里面所重点提倡的,后续所有涉及模式的介绍其实都是为了程序能够更好的扩展,提倡设计模式的本质就是为了减少 增加一点功能而修改系统的主要逻辑
!
用抽象构建框架 用实现扩展细节! 尽量通过扩展类的操作来实现行为变化,而不是通过修改已有代码来实现修改
下面还是老规矩,我们先看一段错误的代码!
需求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格式化的例子。为了更好地支持扩展性,我们对代码进行了重构,重构之后的代码要比之前的代码复杂很多,理解起来也更加有难度。很多时候,我们都需要在扩展性和可读性之间做权衡。在某些场景下,代码的扩展性很重要,我们就可以适当地牺牲一些代码的可读性;在另一些场景下,代码的可读性更加重要,那我们就适当地牺牲一些代码的可扩展性。
本文分享自微信公众号 - JAVA程序狗(javacxg),作者:皇甫嗷嗷叫
原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。
原始发表时间:2020-06-02
本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。
我来说两句