- VisualStudio2022插件的安装及使用-编程手把手系列文章
- pprof-在现网场景怎么用
- C#实现的下拉多选框,下拉多选树,多级节点
- 【学习笔记】基础数据结构:猫树
常说c#、java是面向对象的语言,但我们平时都是在用面向过程的思维写代码,实现业务逻辑像记流水账一样,大篇if else的判断;对业务没有抽象提炼、代码没有分层。随着需求变化、功能逐步拓展、业务逻辑逐渐复杂;代码越来越长、if else嵌套越来越多,代码会变成程序员都厌恶的"屎山"。这种代码后期维护成本非常高、牵一发而动全身、改一处逻辑战战兢兢。假如我们完成开发任务交差,后期维护不关自己的事;但是长期做重复的CRUD、记流水账对我们没有好处。虽然项目不是自己的,但是时间是自己的,这样几年过去似乎没有精进变化,长期下去年龄增大会逐渐丧失竞争力.
下面记录今天开发的一个小功能,演示一步一步重构的过程.
简单实现,只判断如果是普通用户就检查次数,不满足就返回提示:
if (service.isNormalUser() && service.freeNumUseUp()) {
return AjaxResult.error("普通用户免费识别次数已使用完!");
}
// todo:调用识别接口
if (service.isVipUser() && service.vipUserExpire()) {
return AjaxResult.error("会员已到期!");
}
if (service.isNormalUser() && service.freeNumUseUp()) {
return AjaxResult.error("普通用户免费识别次数已使用完!");
}
// todo:调用识别接口
以上修改的问题:普通用户充值以后,是增加一个VIP的角色而不是把原普通用户角色更新为VIP角色。此时这个用户有两个角色,那么上面的代码先判断VIP角色是否到期是没问题的,但是下面又判断了是否为普通用户就有问题了,因为他有两个角色呀,VIP未到期时第2个条件也满足了会给出不合理的提示。怎么改,首先想到的是不是检查VIP后就不检查普通用户了?于是修改为:
if (service.isVipUser()) {
if (service.vipUserExpire()) {
return AjaxResult.error("会员已到期!");
}
} else {
if (service.isNormalUser() && service.freeNumUseUp()) {
return AjaxResult.error("普通用户免费识别次数已使用完!");
}
}
// todo:调用识别接口
以上仍然有问题,如果是VIP角色就不会检查普通用户角色了,可是按需求VIP到期以后他还具有普通用户角色,可以在每天免费次数内使用。于是再改:
boolean dontPass = service.isVipUser() && service.vipUserExpire();
if (dontPass) {
dontPass = service.isNormalUser() && service.freeNumUseUp();
if (dontPass) {
return AjaxResult.error("普通用户免费识别次数已使用完!");
} else {
return AjaxResult.error("会员已到期!");
}
}
// todo:调用识别接口
以上修改可以满足VIP和普通用户的检查了,还差了管理员的判断,还要再嵌套:
boolean dontPass = !service.isAdmin();
if (dontPass) {
dontPass = service.isVipUser() && service.vipUserExpire();
if (dontPass) {
dontPass = service.isNormalUser() && service.freeNumUseUp();
if (dontPass) {
return AjaxResult.error("普通用户免费识别次数已使用完!");
} else {
return AjaxResult.error("会员已到期!");
}
}
}
// todo:调用识别接口
终于满足3个角色的检查了,加了3层if判断。以后再出现新的角色怎么办?如果功能交给同事来升级,原来的代码轻易不敢动只能再嵌套.
梳理以上需求,3个角色有任意一个通过就可以了。实际上检查时可以按以下先后顺序逐个过,最后一个不满足才返回提示.
a. 是否有管理员角色,否进入下一级
b. 是否有VIP角色且未到期,否进入下一级
c. 是否有普通用户角色且满足免费次数条件,否进入下一级;如果没有下一级则检查不通过。
public interface IAudit {
/**
* 角色判断:是否为我的责任
*
* @return
*/
boolean isMyDuty();
/**
* 是否通过
*
* @return
*/
boolean auditPass();
/**
* 检查(审批)意见:不通过时返回空字符串
*
* @return
*/
String auditMessage();
}
isMyDuty()&&auditPass()
,作用就是本来要写3行,现在只写1行。看上去没有必要?因为现在只有3个类呀,如果以后扩展到5个角色,5类那多了。还有,如果是功能修改呢,那就要6个类里分别改了。每改一个类都需要针对这个类单独测试。修改测试花时间多了,这里只有一次修改测试。public abstract class AbstractAudit implements IAudit {
/**
* 角色是否检查通过
*
* @return
*/
public boolean checkPass() {
return isMyDuty() && auditPass();
}
}
@Service
public class AdminAudit extends AbstractAudit {
@Autowired
private IdentifyService identifyService;
@Override
public boolean isMyDuty() {
return identifyService.isAdmin();
}
@Override
public boolean auditPass() {
return true;
}
/**
* 管理员是没有限制的,所以没有提示
*
* @return
*/
@Override
public String auditMessage() {
return "";
}
}
@Service
public class VipUserAudit extends AbstractAudit {
@Autowired
private IdentifyService identifyService;
@Override
public boolean isMyDuty() {
return identifyService.isVipUser();
}
@Override
public boolean auditPass() {
return !identifyService.vipUserExpire();
}
/**
* 这里还需要优化,因为isMyDuty和auditPass可能被调用两次,可以将isMyDuty、auditPass返回值存在临时变量中
*
* @return
*/
@Override
public String auditMessage() {
if (!isMyDuty()) {
return "不是会员";
} else if (!auditPass()) {
return "会员过期";
}
return "";
}
}
@Service
public class NormalUserAudit extends AbstractAudit {
@Autowired
private IdentifyService identifyService;
@Override
public boolean isMyDuty() {
return identifyService.isNormalUser();
}
@Override
public boolean auditPass() {
return !identifyService.freeNumUseUp();
}
@Override
public String auditMessage() {
return "普通用户免费识别次数已使用完";
}
}
public class AuditChain {
private List<AbstractAudit> chain = new ArrayList<>();
/**
* 添加审批人
*
* @param auditor
*/
public void add(AbstractAudit auditor) {
chain.add(auditor);
}
/**
* 检查/审批
*
* @return
*/
public Result audit() {
Result result = new Result();
// 是否检查通过
boolean pass = chain.stream().anyMatch(a -> a.checkPass());
result.setPass(pass);
if (!pass) {
String msg = chain.stream().map(c -> c.auditMessage()).filter(m -> Strings.isNotBlank(m)).collect(Collectors.joining(","));
result.setMsg(msg);
}
return result;
}
@Data
public class Result {
private boolean pass;
private String msg;
}
}
// 审批责任链中加入3个角色,这里用的Spring Boot开发,3个角色都是容器注入的,其它框架中手动创建实例
// 添加审批人角色
auditChain.add(adminAudit);
auditChain.add(vipUserAudit);
auditChain.add(normalUserAudit);
// 审批结果
AuditChain.Result auditResult = auditChain.audit();
if (!auditResult.isPass()) {
return AjaxResult.error(auditResult.getMsg());
}
最终的实现代码简洁明了,易维护、易扩展升级:
chain.stream().anyMatch
改为chain.stream().allMatch
。anyMatch表示任意一个匹配,allMatch表示全部匹配。如果要在改造前的代码中要实现or到and的变化,原有代码几乎要完全重写。学习交流:
最后此篇关于代码精简之路-责任链模式的文章就讲到这里了,如果你想了解更多关于代码精简之路-责任链模式的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
我正在尝试在elisp中实现我自己的深层复制例程(因为类似(setq newlist oldlist)的东西似乎只能提供浅拷贝,而(copy-sequence newlist oldlist)仍然使n
您好,我正在开发客户端站点的交互性,我觉得我的按钮功能的 JS 和 carousel.fader 窗口的构建有点多余。有人可以看一下这段代码,看看是否可以进一步简化,以减少页面加载时间,实际上只是为了
我一直在优化我的网站,但阻碍我的一个问题是我不使用的所有 jQuery 函数。我唯一使用的是平滑的页面滚动器。这似乎是在浪费下载时间。 我的问题是:是否有任何脚本或程序可以删除我不需要的 jQuery
我在很大程度上不太擅长 javascript/jquery,但我知道如何让一些软件工作。但我的问题是我有一大堆 $("body").on("click", "button#thisid", funct
我的 Eclipse 3.5.2 (Ubuntu 10.10) 安装中安装了多个插件(Apatana、SVN、Pydev、Zend Debugger、PHP)。自从几年前我第一次使用 Eclipse
这个问题在这里已经有了答案: std::forward_list and std::forward_list::push_back (5 个答案) 关闭 5 年前。 我偶然发现了这段代码并试图理解它
我有一个非常简单的 jQuery 脚本,当输入元素获得焦点时,它会将宽度扩展到 250px(使用 focusin() 事件),当失去焦点时,它会缩小使用 focusout() 事件回到 200px。但
我一直在研究为什么 WYSIWYG 编辑器不利于内容创建。给出的最常见原因是它们输出不正确的 html。但是如果我使用功能减少的编辑器怎么办? 我的要求只是斜体、使文本加粗、创建有序/无序列表和(可能
我一直在与 MVVM 模式作斗争,并且在尝试为小型/中型项目创建实用设计时遇到了许多挑战。其中一项挑战是弄清楚如何在不创建大量重复且难以维护的代码的情况下获得与此模式分离的好处。 我目前的策略是创建“
按照目前的情况,这个问题不适合我们的问答形式。我们希望答案得到事实、引用或专业知识的支持,但这个问题可能会引发辩论、争论、投票或扩展讨论。如果您觉得这个问题可以改进并可能重新打开,visit the
从MVC模式和Symfony2的角度来看,我可以瘦身吗? Controller 代码 一点点,移开一些持久化逻辑 ?例如,给定一个标准的新 Action ,如下所示: public function
我打算使用适用于 Android 的 ARM DS5 Streamline。 要将 Streamline 与您的 Android 目标一起使用,您必须构建 Gator 驱动程序 gator.ko 并将
我正在使用 LESS ( http://lesscss.org ),它说 ... JavaScript evaluation JavaScript expressions can be evaluat
我一直在研究使用 EventMachine 为一些工作做后台处理的可能性。在 Sinatra 中,这似乎工作得很好,但 Rails 3 似乎在呈现 View 之前执行所有滴答。 当我在瘦网络服务器下运
if SOMETHING charge = Object (this object has a method ID) end DiffObject.update_attributes(specif
我是一名优秀的程序员,十分优秀!