设计模式--SOLID四大原则 KISS ,YAGIN ,DRY 编写代码的模式原则

2020/11 09 16:11

一.如何实现KISS原则(简单)

1.不使用同事看不懂的技术编写代码

2.使用现有成熟的扩展,而不重复造轮子

3.不过度为了性能牺牲代码可读性

二.YAGIN原则(不需要)

1.不要设计和当前无关的设计

2.不要去编写当前使用不到的功能

三.DRY原则(不重复)

1.实现逻辑重复,并不一定违反原则(在不同语义下,实现逻辑重复,并不违反原则。但是可以优化,提取公共部分,供不同语义调用。提高代码复用)

2.语义重复(导致调用者不知道调用那个。相同功能的不同语义导致后期难维护)

3.代码执行重复。相同代码对同一事物的多次执行,违反原则。

如何写出不违反DRY原则的代码

1.满足单一原则

2.减少耦合(依赖关系)

3.使用设计模式

4.通用代码下沉,上层代码调用下层代码,下层不能调用上层

5.模块化(封装)

6.业务逻辑和非业务逻辑分离

 

 

设计模式--SOLID四大原则,KISS和YAGNI笔记

单一职责原则:S

	
		
	
		
		
			
			
			
			
			
	
		
	1.如何理解(SRP):
		一个类只负责完成一个职责的功能,不要设计大而全的类,单一职责原则是为了实现代码的高内聚,低耦合,提高代码的可读性和复用性
	2.如何判断一个类是否符合单一职责原则:
		不同的应用场景,不同的阶段的需求背景,对同一个类的职责是否单一有不同的判定结果。
		我们可以使用以下的小技巧来判断:
			1.类中的代码行数,函数或者属性过多
			2.类依赖的其他类过多,或者依赖类的其他类过多
			3.私有方法过多
			4.比较难给一个类起一个合适的名字,起的名字比较笼统,例如:manger类,context类
			5.类中大量的方法都是集中操作某一些属性
	3.类的职责是否设计的越单一越好呢?
		并不是越设计的越单一越好,如果拆分的过细的话,实际上会降低内聚性,也会影响代码的可维护性

对外开放,修改关闭原则:O

	
		
		
			
			
	
		
	
	
	1.如何理解:
		添加一个功能,应该是通过已有代码的基础上扩展代码,而非修改代码的方法来完成。
		注意:
			1.开闭原则并非是说完全杜绝修改,而是以最小的修改来完成新功能的开发
			2.同样的代码改动,在不同的代码粒度下可能理解的不同
	2.如何做到:
		我们时刻要具备扩展意识,抽象意识,封装意识。在写代码的时候,我们要多花些时间想想,这段代码在未来可能有哪些需求的变动,如何设计代码,事先留好扩展点,以便未来需求变更的时候,在极小的代码改动下,将新的功能插入到扩展点上。
	3.最常用来提高代码扩展性的方法有
	多态、依赖注入、基于接口而非实现编程,以及大部分的设计模式(比如,装饰、策略、模板、职责链、状态)

里氏替换原则:L


	
	
	

	
		
			
				

			
				
				
				

			
				

	


	
* 理解:
	1.用来指导继承关系中子类如何设计的一个原则,核心:“design by contarct:按照协议来设计”
	2.子类完美继承父类的设计初衷,并做了增强
	3.子类对象能够替换程序中父类对象出现的任何地方,并且保证原来的程序的逻辑行为不变及正确性不被破坏
* 具体体现:
	父类定义了函数的“协议”,那么子类可以改变函数的**内部实现逻辑**,但是不能改变函数原有的“约定”
		这里的约定包括:
			1.父类函数声明要实现的功能,子类必须实现该功能
				例:父类实现的函数是按照订单金额排序的,而继承的子类是按照下单时间排序的,这就违反了里氏替换原则

			2.对父类定义的输入的数据,输出的数据,异常的约定不能违背
				例1(输入):父类能输入任意的整数,而子类只能输入正整数这就违背了原则
				例2(输出):父类的函数约定:运行出错的时候返回null,获取数据为空的时候返回空集合,而子类的实现变成了运行出错返回异常,获取数据为空返回null,这就违反了里氏替换原则
				例3(异常):父类定义了只会抛出 ArgumentNullException 异常,那么子类必须只能抛出ArgumentNullException异常,其他的异常就违背了原则

			3.注释中所罗列的任何特殊说明不能违背
				例:父类函数的注释是:用户的提现金额不得超过账户余额,而子类重写函数后,针对vip用户实现提现金额可以大于账户余额。这就违反了里氏替换原则
* 与多态的区别:
	虽然定义描述和代码实现有点类似,但是他们的关注点是不一样的,多态是面向对象的一大特性,它是一种代码实现的思路。而里式替换是一种设计原则,指导继承关系中子类该如何设计

* 判断的小技巧:
	拿父类的单元测试去验证子类的代码。如果某些单元测试运行失败,就有可能说明,子类的设计实现没有完全地遵守父类的约定,子类有可能违背了里式替换原则

接口隔离原则:I


	
		
			
				

	
		
			
				

	
		


	
* 重点是理解“接口”二字,以下三种的不同的理解
	1.“接口理解为一组接口集合”:
		可以是某个微服务的接口或者某个类库的接口,如果这部分接口只被部分调用者来使用的话,我们就需要将这部分接口隔离出来,单独给这部分调用者使用,而不强迫其他调用者也依赖这部分不会被用到的接口
			例如:
				用户的删除接口,我们只希望给后台管理者使用,其他普通用户不能使用,这时候我们就需要把这接口给隔离出来

	2.“接口理解为单个api接口或者函数”:
		部分的调用者只需要函数中的部分功能,闹我们就需要把这个函数拆分成粒度更小的多个函数,让调用者只依赖它需要的那个细粒度函数
			例如:
				count()是完成求平均数,最大数,最小数的函数,那么如果有些用户只需要求平均数,不常用到最大数和最小数,那么我们就需要把函数拆分为max(),min(),avg()这三个粒度更小的函数

	3.“接口理解为oop的接口”:
		那么接口的设计要尽量的单一,不要让接口的实现类和调用者,依赖不需要的接口函数

* 接口隔离原则和单一职责原则的区别:
	单一职责原则是对类,模块,接口的设计,接口隔离原则相对于单一职责原则,一方面侧重于接口的设计,另一方面思考的角度不同,它提供了一种判断接口的职责是否单一的一个标准:通过调用者如何使用接口来间接的判定,如果调用者只使用部分的接口或者接口的部分功能,那接口的设计就不够单一

依赖反转 D:


	

	

	

	
		
		
			
				
					
				
					
				
			
			
		
		
		
			
			
				
		
			
				
					
				
			
		
		
		
			
			
			
			
		


	

	

	
	


	
	
	


	

	
	

	
		
		
		
		
# 控制反转:
	1.这里的“控制”指的是对程序的执行流程的控制,而“反转”指的是没有使用框架之前,程序员自己控制整个程序的执行。在使用框架之后,整个程序的执行流程可以通过框架来控制。流程额控制从程序员“反转”到了框架

	2.它不是具体的实现技巧,而是笼统的设计思想,用于指导框架的设计

	3.实现控制反转的方法有很多,例如:模板设计模式方法,依赖注入等(搜集)

	4.案例分析:(模板设计的控制反转)
		//测试必须实现的接口
		public abstract class TestCase {
			public voi d run() {
				if(doTest()){
					System.out.println("Testsucceed.");
				}else{
					System.out.println("Testfailed.");
				}
			}
			public abstract voi d doTest() ;
		}
		//测试框架
		public class JunitApplication {
			private static final List<TestCase> testCases = new ArrayList<>() ;
			public static void register(TestCase testCase) {
				testCases.add(testCase) ;
		}
			public static final void main(Stri ng[] args) {
				for(TestCase case: testCases) {
					case.run();
				}
			}
		}
		//具体测试的类
		publi c class UserServi ceTest extends TestCase {
			@Overri de
			publi c boolean doTest() {
			// 主要测试的功能
			}
		}

# 依赖注入:
	1.它是一种编程的技巧,不通过new()的方式在类的内部创建依赖类的对象,而是将依赖的类对象在外部创建好之后,通过构造函数,函数参数等方式传递(或注入)给类使用

	2.这里的依赖类可以定义成接口,基于接口实现而非实现编程

	3.它可以实现控制反转
	

# 依赖注入框架:
	1.通过依赖注入框架提供的扩展点,简单配置一下所有需要的类及其类与类之间的依赖关系,就可以实现由框架来自动创建对象,管理对象的生命周期,依赖注入等原本需要程序员来做的事,全都交给框架来实现
	
	2.Spring 框架的控制反转主要是通过依赖注入来实现的

# 依赖反转:(依赖倒置)
	1.高层的模块不要依赖于底层的模块。高层模块和底层的模块应该通过抽象来相互依赖,除此之外,抽象不要依赖具体的实现细节,但是具体的实现细节依赖抽象

	2.这里的高层模块简单来说,在调用链上,调用者属于高层模块,被调用者属于底层模块。
	3.跟控制反转有点类似,主要用于指导框架的设计

	4.案例分析:
		1.Tomcat是javaweb容器,我们编写的web应用程序只要部署在tomcat容器中,便可以被tomcat容器调用执行。
		2.这样的话,Tomcat就是高级模块,我们编写的应用程序属于底层模块。
		3.Tomcat和应用程序之间并没有直接的依赖关系,两者都依赖于同一个抽象,也就是servlet规范,
		4.servlet规范不依赖与具体的tomcat容器和应用程序的实现细节,而tomcat和应用程序依赖于servlet的规范

kiss原则和YAGNI原则:






	


	


	
	
	



	




	
		
		

	


	
		
		
		
	
		# Kiss原则:尽量保持代码简单

# 代码少并不一定满足KISS原则,我们还是考虑逻辑的复杂度,代码的可读性等

# 本身负责的问题,用复杂的方法解决,并不违背kiss原则:
	例如:KMP 算法本身具有逻辑复杂、实现难度大、可读性差的特点,但是对于当我们需要处理长文本字符串匹配问题,遇到系统性能瓶颈的时候我们应该考虑使用kmp算法

# 同样的代码,在某个业务场景下满足 KISS 原则,换一个应用场景可能就不满足了
	我们平时开发使用普通的工具类的时候就可以,但是如果考虑系统系统性能的时候,这时候我们就需要自己实现工具类来提高性能

# 如何写出满足KISS原则的代码?
	1.不要使用同时可能不懂的技术来实现代码
	2.不要重复造轮子,要善于使用已经有的工具类
	3.不要过度的优化,不要过度使用一些奇技淫巧(位运算,复杂的if-else)来优化代码,来牺牲代码的可读性


#建议:
	在开发中,不要过度的设计,其实用简单的方法解决复杂的问题,更能体现一个人的能力



#YAGNI原则:不要去设计当前用不到的功能,不要去编写当前用不到的代码
	例如:
		1.系统暂时使用redis来存储配置信息,以后可能会用到zookeeper,我们就没有必要提前写zookeeper代码,但是我们还是要预留好扩展点,等到需要的时候,再去实现这部分代码
		2.我们在项目中不要提前引入不需要依赖的开发包
#KISS和YAGNI区别
	KISS是“如何做”的问题(尽量保持简单),而YAGNI是“要不要做”的问题(当前不需要就不去做)

# 针对什么时候重复造轮子,什么时候应该使用现成的工具类,开源框架?
	# 什么时候重复造轮子
		1.想学习轮子的原理
		2.现有的轮子不能满足性能的需求,也没有好的替代品(因为轮子一般会考虑大而全的问题,这对性能有损耗)
		3.小而简单的问题
	# 什么时候应该使用现成的工具类,开源框架
		对于工作中,应该使用工具类和开源框架,没有必要去自己造轮子,增加了开发成本,增加了开发周期

--转载请注明: http://91o.cc/kiss-yagin-dry-%e7%bc%96%e5%86%99%e4%bb%a3%e7%a0%81%e7%9a%84%e6%a8%a1%e5%bc%8f%e5%8e%9f%e5%88%99/