【架构设计】保持简单轻量设计的三个原则——DRY,KISS, YAGNI
前言
一个软件轻量简单的软件架构是非常重要的,它可以让我们花最小的代价就能满足业务上的需求。那如何保证轻量简单呢?那今天就和大家分享下这其中的秘密,也就是3个重要的指导原则,KISS原则,YAGNI原则和DRY原则,你们都知道并且理解吗?
欢迎关注微信公众号「JAVA旭阳」交流和学习
KISS原则
KISS原则, 英文全称Keep it simple and stupid。核心思想就是尽量保持简单。
KISS原则指导我们在软件设计的时候要尽量保持简单,使用一些成熟的、适合业务的技术方案。另外从一个使用者的角度来思考,你设计时要思考如何让自己的架构设计变得简单,足够易用,比如你开发的框架是不是对于接入成本低甚至0成本? 你设计的框架是否不侵入业务代码?
不仅软件架构设计层面,在代码层面也处处要体现KISS原则。代码的可读性和可维护性是衡量代码质量非常重要的两个标准。而 KISS 原则就是保持代码可读和可维护的重要手段。代码足够简单,也就意味着很容易读懂,bug 比较难隐藏。即便出现 bug,修复起来也比较简单。
我们来看下面校验IP是否合法的3种实现方式哪个最“KISS”,大家觉得是哪个呢?
- 方式一
- 方式二
- 方式三
- 方式一代码量最少,正则表达式本身是比较复杂的,写出完全没有 bug 的正则表达本身就比较有挑战;另一方面,并不是每个程序员都精通正则表达式。对于不怎么懂正则表达式的同事来说,看懂并且维护这段正则表达式是比较困难的。这种实现方式会导致代码的可读性和可维护性变差,所以,从 KISS 原则的设计初衷上来讲,这种实现方式并不符合 KISS 原则。
- 方式二使用了 StringUtils 类、Integer 类提供的一些现成的工具函数,来处理 IP地址字符串,逻辑清晰,可读性好。
- 方式三不使用任何工具函数,而是通过逐一处理 IP 地址中的字符,来判断是否合法,容易出bug,不好理解。
小结: 综合来看,第二种方式更符合KISS原则。并不是代码越少越好,还要考虑代码是否逻辑清晰、是否容易理解、是否够稳定。
那如何写出满足 KISS 原则的代码?
- 不要使用同事可能不懂的技术来实现代码。比如前面例子中的正则表达式,还有一些编程语言中过于高级的语法等。
- 不要重复造轮子,要善于使用已经有的工具类库。经验证明,自己去实现这些类库,出bug 的概率会更高,维护的成本也比较高。
- 不要过度优化。不要过度使用一些奇技淫巧(比如,位运算代替算术运算、复杂的条件语句代替
if-else、使用一些过于底层的函数等)来优化代码,牺牲代码的可读性。
YAGNI原则
YAGNI 原则的英文全称是:You Ain’t Gonna Need It。中文大意是说你根本不需要这么做。核心思想就是不要过度设计。
那什么是过度设计呢?比如说你的应用,现在还是处在一个单体应用的阶段,那么这时候我就考虑啊,分布式事务该怎么做,那或者说你的数据库还是一个单库的时候,我就在思考。如何去做数据异构,甚至是你的业务逻辑在并不复杂的阶段的时候,你就再去思考如何去上一个工作流引擎,或者是规则引擎。那再比如现在图形数据库非常的火,那你就在想方设法的把图形数据库引入到你自己的项目当中。那这些是什么呢啊?说好听点叫面向简历编程,说难听点就是什么?脱裤子放屁,多此一举。我们提倡面向未来的架构是什么意思啊?是去规划你未来技术发展的路线,当这一天需要到来的时候,你有能力去实现它。而不是说你现在就把未来不需要的东西去搬到自己的项目当中。
所以千万不要为了技术而技术,一定要让技术服务于业务,谨遵YAGNI原则,过度设计是灾难。
DRY原则
DRY原则,英文全称Don’t Repeat Yourself,直译过来就是不要重复你自己。那这里的重复是什么意思?就是指代码一模一样的才算重复吗?实际不是的,我这里从实现逻辑重复、功能语义重复和代码执行重复三个点来理解重复的意思。
- 实现逻辑重复
我们看下下面的例子:
- 校验用户名
- 校验密码
虽然上面两个方法的代码逻辑是重复的,但是他们语义不重复,一个是用来校验用户名,一个是用来校验密码的,所以是符合DRY原则。如果我们强行把他们封装成isValidUsernameOrPassword()方法,反而不符合单一职责原则,万一哪天密码的校验逻辑变了怎么办呢?所以并不是说代码一样就一定违反了DRY原则。反而有时候代码不一样,恰好违反了DRY原则。
- 功能语义重复
直接上例子,项目中不同的同事由于不知道就写了两个校验IP的方法。
- 代码一
- 代码二
尽管两段代码的实现逻辑不重复,但语义重复,也就是功能重复,我们认为它违反了 DRY 原则。我们应该在项目中,统一一种实现思路,所有用到判断 IP 地址是否合法的地方,都统一调用同一个函数。不然哪天校验规则变了,很容易只改了其中一个,另外一个漏改,就会出现莫名其妙的bug。
- 代码执行重复
我们看下面一个登录的代码例子。
你有没有发现email的校验逻辑被执行了2次,这种重复执行的情况我们也可以认为违反了DRY原则。
说了这么多,那有什么好的办法提高代码复用性,保证DRY原则呢?
- 使用现成的轮子,不轻易造轮子
其实最关键的就是写代码带脑子,用到一个方法先看看有没有现成的,不要看看不看,就动手在那里造轮子。
- 减少代码耦合
对于高度耦合的代码,当我们希望复用其中的一个功能,想把这个功能的代码抽取出来成为一个独立的模块、类或者函数的时候,往往会发现牵一发而动全身。移动一点代码,就要牵连到很多其他相关的代码。所以,高度耦合的代码会影响到代码的复用性,我们要尽量减少代码耦合。
- 满足单一职责原则
我们前面讲过,如果职责不够单一,模块、类设计得大而全,那依赖它的代码或者它依赖的代码就会比较多,进而增加了代码的耦合。根据上一点,也就会影响到代码的复用性。相反,越细粒度的代码,代码的通用性会越好,越容易被复用。
- 模块化
这里的“模块”,不单单指一组类构成的模块,还可以理解为单个类、函数。我们要善于将功能独立的代码,封装成模块。独立的模块就像一块一块的积木,更加容易复用,可以直接拿来搭建更加复杂的系统。
- 业务与非业务逻辑分离
越是跟业务无关的代码越是容易复用,越是针对特定业务的代码越难复用。所以,为了复用跟业务无关的代码,我们将业务和非业务逻辑代码分离,抽取成一些通用的框架、类库、组件等。
- 通用代码下沉
从分层的角度来看,越底层的代码越通用、会被越多的模块调用,越应该设计得足够可复用。一般情况下,在代码分层之后,为了避免交叉调用导致调用关系混乱,我们只允许上层代码调用下层代码及同层代码之间的调用,杜绝下层代码调用上层代码。所以,通用的代码我们尽量下沉到更下层。
- 继承、多态、抽象、封装
在讲面向对象特性的时候,我们讲到,利用继承,可以将公共的代码抽取到父类,子类复用父类的属性和方法。利用多态,我们可以动态地替换一段代码的部分逻辑,让这段代码可复用。除此之外,抽象和封装,从更加广义的层面、而非狭义的面向对象特性的层面来理解的话,越抽象、越不依赖具体的实现,越容易复用。代码封装成模块,隐藏可变的细节、暴露不变的接口,就越容易复用。
- 应用模板等设计模式
一些设计模式,也能提高代码的复用性。比如,模板模式利用了多态来实现,可以灵活地替换其中的部分代码,整个流程模板代码可复用。
总结
本文和大家介绍了软件工程领域中保持简单架构设计的三个原则,其实所有的这些原则就是为了达到一个很简单的目的,try everything to keep it simple,软件设计上的简单轻量是非常重要的。
欢迎关注微信公众号「JAVA旭阳」交流和学习
更多学习资料请移步:程序员成神之路
【架构设计】保持简单轻量设计的三个原则——DRY,KISS, YAGNI的更多相关文章
- 一种简单,轻量,灵活的C#对象转Json对象的方案(续)
本文参考资料 一种简单,轻量,灵活的C#对象转Json对象的方案 [源码]Literacy 快速反射读写对象属性,字段 一段废话 之前我已经介绍了这个方案的名称为JsonBuilder,这套方案最大的 ...
- flutter最简单轻量便捷的路由管理方案NavRouter
大家好,我是CrazyQ1,今天给大家推荐一个路由管理方案,用的非常不错的,叫nav_router. 项目地址是:https://github.com/fluttercandies/nav_route ...
- Bourbon – 简单轻量的 Sass 混入(Mixins)库
Bourbon 是一个简单易用的 Sass 混入(Mixin)库,无需配置.该混入包含用于支持所有现代浏览器的 CSS3 属性前缀.前缀需要确保在旧的浏览器支持优雅降级.Bourbon 使用 SCSS ...
- 一种简单,轻量,灵活的C#对象转Json对象的方案
简单,是因为只有一个类 轻量,是因为整个类代码只有300行 灵活,是因为扩展方式只需要继承重写某个方法即可 补充:修正无法处理可空值类型的bug 首先我将这个类称之为JsonBuilder,我希望它以 ...
- 轻量ORM-SqlRepoEx (三)Select语句
一.示例用数据库为Northwind数据库,可在百度网盘下载 https://pan.baidu.com/s/1er0Mm48kUfeAsYkSW6DfnA 密码:r7pm 二.如何初始化SqlRep ...
- quilljs 一款简单轻量的富文本编辑器(适合移动端)
quilljs入门使用教程: quill.js是一款强大的现代富文本编辑器插件.该富文本编辑器插件支持所有的现代浏览器.平板电脑和手机.它提供了文本编辑器的所有功能,并为开发者提供大量的配置参数和方法 ...
- PL/SQL轻量版(三)——游标与异常处理
一.游标 1.概念 游标是一个 指向上下文的句柄( handle) 或指针.通过游标,PL/SQL 可以控制上下文区和处理语句时上下文区会发生些什么事情. 2.游标处理 处理显式游标 主要包含以下四个 ...
- 轻量ORM-SqlRepoEx介绍
轻量级 ORM-SqlRepoEx 介绍 SqlRepoEx是 .Net平台下兼容.NET Standard 2.0人一个轻型的ORM.解决了Lambda转Sql语句这一难题,SqlRepoEx使用的 ...
- 轻量ORM-SqlRepoEx (四)INSERT、UPDATE、DELETE 语句
*本文中所用类声明见上一篇博文<轻量ORM-SqlRepoEx (三)Select语句>中Customers类 一.增加记录 1.工厂一个实例仓储 var repository = Rep ...
- Prezento – 轻量、简单的 jQuery 幻灯片插件
Prezento 是一个超级简单的 jQuery 幻灯片插件.可以让你网页以新颖的交互方式呈现.另外,Prezento 支持响应式设计,配置项也很灵活,可以根据你需要的效果配置. 您可能感兴趣的相关文 ...
随机推荐
- Codeforces Round #822 (Div. 2) A-F
比赛链接 A 题解 知识点:贪心. 注意到任意三根木棍的相等最优解是最长减最小,因此从小到大排序,三个三个取,取最小值. 时间复杂度 \(O(n\log n)\) 空间复杂度 \(O(n)\) 代码 ...
- 基于QT和C++实现的翻金币游戏
基于QT和C++的翻金币游戏 声明: QT翻金币项目可以说是每个新学QT的同学都会去写的一个项目,网上的源码也很多,我也是最近刚开始学QT,所以也参考了很多前辈的代码自己重新敲了一遍代码. 游戏介绍: ...
- 如何使用IDEA创建一个简单的java工程?
文章目录 1.创建新工程 1.1 新建项目 1.2 在src目录下创建package 1.3 在包下创建类 2.编写代码.运行项目 2.1 编写代码 2.2 运行结果 3.开心的从eclipse转移到 ...
- 网页头部的声明应该是用 lang="zh" 还是 lang="zh-CN"?
网页头部的声明应该是用 lang="zh" 还是 lang="zh-CN"? 遇到问题 不知道大家有没有留意到一个问题,就是使用 VsCode 新建的 html ...
- C++11绑定器bind及function机制
前言 之前在学muduo网络库时,看到陈硕以基于对象编程的方式,大量使用boost库中的bind和function机制,如今,这些概念都已引入至C++11,包含在头文件<functional&g ...
- mybatis-自定义映射resultMap
自定义映射resultMap resultMap处理字段和属性的映射关系 resultMap:设置自定义映射 属性: id:表示自定义映射的唯一标识,不能重复 type:查询的数据要映射的实体类的类型 ...
- 2022NISACTF--WEB
easyssrf 打开题目,显示的是 尝试输入, 发现输入flag有东西 读取文件 访问下一个网站 读取文件 不能以file开头 直接伪协议,base64解码 checkIn 奇怪的unicode编码 ...
- HTTP(s) API 经验总结
目录 参考文章 First URL HTTP(s)-Header 请求方式 参考文章 https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Overvie ...
- 成熟企业级开源监控解决方案Zabbix6.2关键功能实战-上
@ 目录 概述 定义 监控作用 使用理解 监控对象和指标 架构组成 常用监控软件分析 版本选型 俗语 安装 部署方式 部署 zabbix-agent 概述 定义 Zabbix 官网地址 https:/ ...
- Go语言正/反向代理的姿势
先重温一下什么叫反向代理,正向代理. 鹅厂二面,nginx回忆录 所谓正向,反向代理取决于代理的是出站请求,还是入站请求. 正向代理: 代理的出站请求, 客户端能感知到代理程序,架构上距离客户端更近. ...