网站首页 > 开源技术 正文
前言
在软件领域,我们经常会听到高内聚,低耦合这两个词,特别是在一些优秀的框架或者软件架构对这种原则遵循得很到位。高内聚低耦合是软件设计中的两个重要原则,高内聚指的是软件模块或类内部的代码或元素之间紧密相关,共同完成某一特定功能或任务,这样的模块具有较好的功能独立性和重用性。低耦合则是指软件的不同模块或类之间的相互依赖程度较低,模块间的接口简单清晰,一个模块的变更不会导致其他模块的大规模修改。低耦合有助于提高系统的灵活性和可维护性。
高内聚
体现在一个模块或者一个类亦或者一个方法内部尽量的职责单一,能不对外的接口尽量不对外。内部元素之间紧密相关完成同一个功能。具体例子,我们个人计算机的磁盘,它主要功能功能是读写数据,内部的电路设计极其复杂,但是这些复杂度都是全部封装起来,从外部看磁盘的功能非常的简单,就是写入、读取数据。磁盘内部实现肯定通过不同部件和电路共同完成,但是这种复杂度都是被封装在局部,对外是看不到这些复杂度的。所以,高内聚有助于独立性和重用性
低耦合
体现在多个模块之间依据接口建立联系,不要依据具体实现,也就是我们经常说的面向接口编程。当一个模块发生了变化也不会影响到其他模块大范围的修改。还是拿磁盘为例子来说明低耦合。上面说过磁盘内部的复杂度封装是高内聚的表现,那么磁盘和其他计算机部件的对接能做到足够简单甚至可替换,那就是低耦合。比如磁盘和主板直接的接口,磁盘和CPU直接的接口,当磁盘有故障换一个新的一样可以使用,不影响主板和CUP对磁盘的供电和读写数据。因此,低耦合有助于提高系统灵活性和可维护性。
示例
下面我通过一个具体的例子,和大家分享在实际开发中如何遵循高内聚,低耦合的设计原则让你的系统更具有重用性和灵活性。为了方便大家理解,我这里继续拿若依框架来举例说明。用过若依框架的伙伴们,肯定对若依的验证码这个模块非常的熟悉,在登录界面就用到这个验证码,截图如下:
这个验证码在若依中有两种配置方式,一个是计算方式,一个是字符形式,可以在属性文件配置指定验证码生成方式,如图:
我们先看一下Controller中的实现:
具体的这两个验证码管理对象在spring中配置在ruoyi-framework模块config 目录下的 CaptchaConfig.java这个配置类中,代码截图如下:
这就是框架中对验证码如何生成的具体实现过程。从功能角度看这个还是比较灵活的,但是从设计角度看我觉得还有提升空间:
- 在controller中需要通过if else 多分支方式去实现,要是后续再新增一种验证码实现方式那 这个业务代码得修改。
- 新增一种验证实现,controller中就需要autowrite注入一个bean,如果验证方式越多,这里就要注入越多对象,但是一个系统一般只使用一种验证码方式
- 业务代码和非业务代码混合,导致非业务代码新增或者修改影响到业务代码
- 那两个验证码管理对象其实没有重用性,因为在整个业务中登录才用到验证码,感觉没必要让spring容器去管理,而且即使不需要也要实例化DefaultKaptcha对象
这里说的业务代码是指controller,因为controller就是直接面对前端的,如果后端还继续分前台和后台的话,那么controller就是前台代码,像service,mapper,utils其他代码都是后台代码。在设计中,前台代码尽可能的简单,保证业务流程的正确性和稳定性,后台代码可以相应复杂,但是要保证和前台代码低耦合。那么笔者今天就按照自己的思路做一个简单的改造,让设计更符合高内聚,低耦合的设计原则。整体思路是这样的:
- 因为验证码最终需要返回一个字符串的code和BufferedImage类型的image给前端,所以需要先定义一个包装类CaptchaReult,包含这两个参数。
- 重新定义一个CaptchaHandler接口,这个接口只有一个方法,负责创建CaptchaReult
- 分别定义两个具体实现类,实现CaptchaHandler接口
- 在spring中配置 CaptchaHandler这个Bean,根据类型具体实例化第三步的具体实现类,这里是典型的策略模式
- 在controller只依赖注入CaptchaHandler,调用创建验证码方法即可
下面是具体的代码实现:
//用来给前端返回code和image
public class CaptchaReult {
public CaptchaReult(String code, BufferedImage image) {
this.code = code;
this.image = image;
}
private BufferedImage image;
private String code;
public BufferedImage getImage() {
return image;
}
public String getCode() {
return code;
}
}
//创建验证码接口
public interface CaptchaHandler {
public CaptchaReult createCaptcha();
}
// 字符方式验证码实现
public class CharCaptchaHandler implements CaptchaHandler {
private final Producer producer;
public CharCaptchaHandler() {
this.producer = initProducer();
}
@Override
public CaptchaReult createCaptcha() {
String capStr = producer.createText();
BufferedImage image = producer.createImage(capStr);
return new CaptchaReult(capStr, image);
}
// 这里使用私有方法 创建具体的验证码生成对象
private final DefaultKaptcha initProducer() {
DefaultKaptcha defaultKaptcha = new DefaultKaptcha();
Properties properties = new Properties();
// 是否有边框 默认为true 我们可以自己设置yes,no
properties.setProperty(KAPTCHA_BORDER, "yes");
// 验证码文本字符颜色 默认为Color.BLACK
properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_COLOR, "black");
// 验证码图片宽度 默认为200
properties.setProperty(KAPTCHA_IMAGE_WIDTH, "160");
// 验证码图片高度 默认为50
properties.setProperty(KAPTCHA_IMAGE_HEIGHT, "60");
// 验证码文本字符大小 默认为40
properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_SIZE, "38");
// KAPTCHA_SESSION_KEY
properties.setProperty(KAPTCHA_SESSION_CONFIG_KEY, "kaptchaCode");
// 验证码文本字符长度 默认为5
properties.setProperty(KAPTCHA_TEXTPRODUCER_CHAR_LENGTH, "4");
// 验证码文本字体样式 默认为new Font("Arial", 1, fontSize), new Font("Courier", 1, fontSize)
properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_NAMES, "Arial,Courier");
// 图片样式 水纹com.google.code.kaptcha.impl.WaterRipple
// 鱼眼com.google.code.kaptcha.impl.FishEyeGimpy
// 阴影com.google.code.kaptcha.impl.ShadowGimpy
properties.setProperty(KAPTCHA_OBSCURIFICATOR_IMPL, ShadowGimpy.class.getName());
Config config = new Config(properties);
defaultKaptcha.setConfig(config);
return defaultKaptcha;
}
}
// 计算模式生成验证码
public class MachCaptchaHandler implements CaptchaHandler {
private final Producer producer;
public MachCaptchaHandler() {
this.producer = initProducer();
}
@Override
public CaptchaReult createCaptcha() {
String capText = producer.createText();
String capStr = capText.substring(0, capText.lastIndexOf("@"));
String code = capText.substring(capText.lastIndexOf("@") + 1);
BufferedImage image = producer.createImage(capStr);
return new CaptchaReult(code, image);
}
// 这里使用私有方法 创建具体的验证码生成对象
private final DefaultKaptcha initProducer() {
DefaultKaptcha defaultKaptcha = new DefaultKaptcha();
Properties properties = new Properties();
// 是否有边框 默认为true 我们可以自己设置yes,no
properties.setProperty(KAPTCHA_BORDER, "yes");
// 边框颜色 默认为Color.BLACK
properties.setProperty(KAPTCHA_BORDER_COLOR, "105,179,90");
// 验证码文本字符颜色 默认为Color.BLACK
properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_COLOR, "blue");
// 验证码图片宽度 默认为200
properties.setProperty(KAPTCHA_IMAGE_WIDTH, "160");
// 验证码图片高度 默认为50
properties.setProperty(KAPTCHA_IMAGE_HEIGHT, "60");
// 验证码文本字符大小 默认为40
properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_SIZE, "35");
// KAPTCHA_SESSION_KEY
properties.setProperty(KAPTCHA_SESSION_CONFIG_KEY, "kaptchaCodeMath");
// 验证码文本生成器
properties.setProperty(KAPTCHA_TEXTPRODUCER_IMPL, KaptchaTextCreator.class.getName());
// 验证码文本字符间距 默认为2
properties.setProperty(KAPTCHA_TEXTPRODUCER_CHAR_SPACE, "3");
// 验证码文本字符长度 默认为5
properties.setProperty(KAPTCHA_TEXTPRODUCER_CHAR_LENGTH, "6");
// 验证码文本字体样式 默认为new Font("Arial", 1, fontSize), new Font("Courier", 1, fontSize)
properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_NAMES, "Arial,Courier");
// 验证码噪点颜色 默认为Color.BLACK
properties.setProperty(KAPTCHA_NOISE_COLOR, "white");
// 干扰实现类
properties.setProperty(KAPTCHA_NOISE_IMPL, NoNoise.class.getName());
// 图片样式 水纹com.google.code.kaptcha.impl.WaterRipple
// 鱼眼com.google.code.kaptcha.impl.FishEyeGimpy
// 阴影com.google.code.kaptcha.impl.ShadowGimpy
properties.setProperty(KAPTCHA_OBSCURIFICATOR_IMPL, ShadowGimpy.class.getName());
Config config = new Config(properties);
defaultKaptcha.setConfig(config);
return defaultKaptcha;
}
}
@Configuration
public class CaptchaConfig {
@Autowired
private RuoYiConfig config;
// 配置验证码生成工厂bean,controller中自动注入这个bean
@Bean
public CaptchaHandler captchaHandler() {
switch (config.getCaptchaType()) {
case "math":
return new MachCaptchaHandler();
case "char":
return new CharCaptchaHandler();
// 后续有新增的类型,这里可以继续添加
default:
return null;
}
}
}
可以看到,最终一个目录下存放了有关验证码的生成的相关接口和实现,如下图所示:
最后在验证码生成的controller中改造如下:
箭头部分是改造后的代码,注释部分是之前的代码。可以看到controller中只需要注入一个 CaptchaHandler,并且调用其createCaptcha方法即可,无需关注CaptchaHandler具体是 char或者是mach类型,也不需要通过if else这样的方式去生成验证码了。未来要是想新增一种验证码生成方式,只需要新增一个具体的类去实现CaptchaHandler接口,并在CaptchaConfig下添加一个策略,controller中的代码不需要修改,如图所示:
这里的MachCaptchaHandler和CharCaptchaHandler的具体实现就是高内聚的体现,里面的构造器和createCaptcha()方法封装了验证码的具体实现方式,而DefaultKaptcha对象的获取定义为类私有,因为这个对象只为一个类服务,不需要像若依原来的实现一样把它直接暴露到容器中。最后只需要返回CaptchaReult这个对象,调用方不需要关注验证码是如何生成的。而controller中的 CaptchaReult captchaReult = captchaHandler.createCaptcha() 这一句就是改造后低耦合的体现,验证码生成方式和具体业务代码是通过接口建立联系的,未来验证码创建方式变更或者新增实现方式,这一句代码都不需要改动,这正是低耦合所遵循的灵活性和可维护性。
以上这种改进,其实在设计模式里面有一个专有的名字叫策略模式,策略模式的主要核心就是把算法封装起来,客户端可以依赖接口灵活的替换这些算法。
猜你喜欢
- 2024-10-08 Spring Security的项目中集成JWT Token令牌安全访问后台API
- 2024-10-08 做外包项目遇到的坑(一)——关于手动加入jar包到本地仓库
- 2024-10-08 从WEB登录谈网络安全(web登录安全方案)
- 2024-10-08 Java微服务_医疗管理项目_基于若依快速开发框架
- 2024-10-08 RuoYi若依系统的验证码如何替换为更美观的EasyCaptcha
- 2024-06-30 使用idea新建springBoot+Gradle项目(超详细)
- 2024-06-30 IDEA构建maven项目(idea构建maven项目找不到找不到输出文件)
- 2024-06-30 基于JAVA SSM springboot实现的抗疫物质信息管理系统设计和实现
- 2024-06-30 实战:SpringBoot分布式验证码登录方案
- 2024-06-30 前后端分离模式下验证码实现(前后端分离验证码保存方案)
你 发表评论:
欢迎- 最近发表
- 标签列表
-
- jdk (81)
- putty (66)
- rufus (78)
- 内网穿透 (89)
- okhttp (70)
- powertoys (74)
- windowsterminal (81)
- netcat (65)
- ghostscript (65)
- veracrypt (65)
- asp.netcore (70)
- wrk (67)
- aspose.words (80)
- itk (80)
- ajaxfileupload.js (66)
- sqlhelper (67)
- express.js (67)
- phpmailer (67)
- xjar (70)
- redisclient (78)
- wakeonlan (66)
- tinygo (85)
- startbbs (72)
- webftp (82)
- vsvim (79)
本文暂时没有评论,来添加一个吧(●'◡'●)