导图社区 如何编写好的代码
从编程范式到设计原则,规整所学知识。
编辑于2020-10-16 16:29:53如何编写好的代码
评价标准
可维护性
可读性(readability)
可拓展性
灵活性
简洁性
KISS原则
keep it simple,stupid
可复用性
DRY原则
don't repeat yourself
如何提高代码可复用性
减少代码耦合
满足职责单一原则
代码模块化
业务与非业务逻辑分离
通用代码下沉
继承、多态、抽象、封装
应用模版等设计模式
可测试性
什么是代码的可测试性
就是针对代码编写单元测试的难易程度
编写可测试性代码的最有效手段
依赖注入是编写可测试性代码的最有效手段。通过依赖注入,我们在编写单元测试的时候,可以通过 mock 的方法解依赖外部服务,这也是我们在编写单元测试的过程中最有技术挑战的地方。
常见的 Anti-Patterns
代码中包含未决行为逻辑
滥用可变全局变量
滥用静态方法
使用复杂的继承关系
高度耦合的代码
编程方法论
编程范式/编程风格
分类
面向过程
什么是面向过程编程
以过程(或方法)作为组织代码的基本单元,主要的特点就是数据和方法相分离
基于贫血模型的MVC开发模式
面向对象
什么是面向对象编程
以类或对象作为组织代码的基本单元,并将继承,封装,多态,抽象作为代码设计和实现的基石
什么是面向对象编程语言
支持类或对象的语法机制,并有现成的语法机制,能方便地实现面向对象编程四大特性(封装、抽象、继承、多态)的编程语言
特性
封装
定义
封装也叫作信息隐藏或者数据访问保护
如何隐藏信息, 保护数据
实现方式
需要通过访问控制权限实现
java类中属性的的 private、protected、public
作用
1.保护数据不被随意修改,提高代码的可维护性
2.仅暴露有限的必要接口,提高类的易用性
抽象
定义
隐藏方法的具体实现
只需要关注提供什么,可以得到什么,比如去当铺提供1块表,就得到1斤米,具体当铺怎么把表卖了换成钱,钱买米给你,这具体的细节不必在意,因此,会发现并非抽象类,interface类才是抽象,任何的抽象类的实现类都可以称之为抽象
作用
1.提高代码的可扩展性、维护性,修改实现不需要改变定义,减少代码的改动范围
2.是处理复杂系统的有效手段,能有效地过滤掉不必要关注的信息
抽象类与接口
抽象类
特性
1.抽象类不允许被实例化,只能被继承。
2.抽象类可以包含属性和方法。方法既可以包含代码实现,也可以不包含代码实现。不包含代码实现的方法叫作抽象方法。
3.子类继承抽象类,必须实现抽象类中的所有抽象方法。
解决的问题
为了代码复用
接口
特性
1.接口只能声明方法,方法不能包含代码实现
2.类实现接口的时候,必须实现接口中声明的所有方法。
3.接口不能包含属性(也就是成员变量)
解决的问题
解耦
如何实现基于接口而非实现类编程
1.函数的命名不能暴露任何实现细节
2.封装具体的实现细节
3.为实现类定义抽象的接口。
继承
作用
1.解决代码复用性问题
为什么不推荐过度使用继承?
层次过深,过复杂,会影响代码的可维护性
比如鸟的分类问题,当几种行为被抽象出来,以继承的方式为鸟分类,可能复杂度是以倍数增长的.此时应当以组合及接口的方式实现,会提高其可维护性
多态
定义
实现方式
继承加方法重写
利用接口类语法
duck-typing 语法
作用
优势
思考方式更接近于人
应对复杂系统有,四大特性写出的代码易扩展、易复用、易维护
常见的问题
1.getter setter方法的滥用
违背了封装特性,信息没有隐藏,数据访问没有保护,代码风格退化为面向过程
如果返回的是集合容器(比如例子中的 List 容器),也要防范集合内部数据被修改的危险
2.滥用全局变量和全局方法
constants类需要细分
utils类 最好内部只有静态方法,不可滥用,不可大而全
如果能将这些类中的属性和方法,划分归并到其他业务类中,那是最好不过的了,能极大地提高类的内聚性和代码的可复用性。
3.定义数据和方法分离的类
mvc模式就是典型的贫血模型的开发模式,典型的面向过程开发
基于充血模型的DDD开发模式
面向对象设计
如何按部就班的进行面向对象设计
1.划分职责进而识别出有哪些类
2.定义类及其属性和方法
3.定义类与类之间的交互关系
4.将类组装起来并提供执行入口
函数式编程
作用
为编程基础
设计原则
常用的设计原则
SOLID原则
单一职责原则(Single Responsibility Principle)
如何理解
一个类只负责完成一个职责或者功能。不要设计大而全的类,要设计粒度小、功能单一的类。单一职责原则是为了实现代码高内聚、低耦合,提高代码的复用性、可读性、可维护性。
如何判定是否满足
类中的代码行数、函数或者属性过多;
类依赖的其他类过多,或者依赖类的其他类过多;
私有方法过多;
比较难给类起一个合适的名字;
类中大量的方法都是集中操作类中的某几个属性。
开闭原则(Open Closed Principle)
如何理解
添加一个新的功能,应该是通过在已有代码基础上扩展代码(新增模块、类、方法、属性等),而非修改已有代码(修改模块、类、方法、属性等)的方式来完成。
如何做到
多态、依赖注入、基于接口而非实现编程,以及大部分的设计模式(比如,装饰、策略、模板、职责链、状态)
里式替换原则(Liskov Substitution Principle)
如何理解
子类对象(object of subtype/derived class)能够替换程序(program)中父类对象(object of base/parent class)出现的任何地方,并且保证原来程序的逻辑行为(behavior)不变及正确性不被破坏。
如何判定是否违背
1.子类违背父类声明要实现的功能
2.子类违背父类对输入、输出、异常的约定
3.子类违背父类注释中所罗列的任何特殊说明
接口隔离原则(Interface Segregation Principle)
如何理解
理解为某个微服务的接口,也可以是某个类库的接口等。如果部分接口只被部分调用者使用,我们就需要将这部分接口隔离出来,单独给这部分调用者使用,而不强迫其他调用者也依赖这部分不会被用到的接口
部分调用者只需要函数中的部分功能,那我们就需要把函数拆分成粒度更细的多个函数,让调用者只依赖它需要的那个细粒度函数
理解为面向对象编程语言中的接口语法。那接口的设计要尽量单一,不要让接口的实现类和调用者,依赖不需要的接口函数。
接口隔离原则与单一职责原则的区别
单一职责原则针对的是模块、类、接口的设计。
接口隔离原则提供了一种判断接口的职责是否单一的标准:通过调用者如何使用接口来间接地判定。如果调用者只使用部分接口或接口的部分功能,那接口的设计就不够职责单一。
依赖反转原则(Dependency Inversion Principle)
控制反转
这里的控制是指对程序的控制,反转是指身份的反转,整体就是在使用了框架之后 对于程序的控制由程序设计人员交由框架
依赖注入
不通过 new 的方式在类内部创建依赖类的对象,而是将依赖的类对象在外部创建好之后,通过构造函数、函数参数等方式传递(或注入)给类来使用。
依赖注入框架
我们通过依赖注入框架提供的扩展点,简单配置一下所有需要的类及其类与类之间依赖关系,就可以实现由框架来自动创建对象、管理对象的生命周期、依赖注入等原本需要程序员来做的事情
依赖反转原则
高层模块不依赖低层模块,它们共同依赖同一个抽象。抽象不要依赖具体实现细节,具体实现细节依赖抽象。
KISS原则(keep it simple and stupid)
不要使用同事可能不懂的技术来实现代码
不要重复造轮子,要善于使用已经有的工具类库
不要过度优化
YAGNI原则(you ain't gonna need it)
预留好扩展点,等到需要的时候,再去实现其功能
DRY原则(don't repeat yourself)
实现逻辑重复,但功能语义不重复的代码,并不违反 DRY 原则
实现逻辑不重复,但功能语义重复的代码,也算是违反 DRY 原则
代码执行重复也算是违反 DRY 原则。
迪米特法则(Law of Demeter)The Least Knowledge Principle
如何理解高内聚,低耦合?
“高内聚”用来指导类本身的设计,“松耦合”用来指导类与类之间依赖关系的设计。
高内聚,就是指相近的功能应该放到同一个类中,不相近的功能不要放到同一类中
松耦合指的是,在代码中,类与类之间的依赖关系简单清晰。即使两个类有依赖关系,一个类的代码改动也不会或者很少导致依赖类的代码改动。
如何理解“迪米特法则”?
不该有直接依赖关系的类之间,不要有依赖;有依赖关系的类之间,尽量只依赖必要的接口。
“高内聚、松耦合”“单一职责原则”“接口隔离原则”“基于接口而非实现编程”“迪米特法则”,它们之间的区别和联系
单一职责是从自身提供的功能出发,迪米特法则是从类依赖关系出发,针对接口而非实现编程是使用者的角度
作用
指导代码设计,在不同的场合使用不同的设计模式
设计模式
分类
结构型
代理模式
桥接模式(Bridge Design Pattern)
装饰器模式
适配器模式(Adapter Design Pattern)
门面模式
组合模式
享元模式
创建型
建造者模式
单例模式
工厂模式
原型模式
行为型
观察者模式
模板模式
策略模式
职责链模式
迭代器模式
状态模式
访问者模式
备忘录模式
命令模式
解释器模式
中介模式
作用
提高代码可拓展性
编程规范
作用
提高代码可读性
代码重构
作用
解决代码可读性,可维护性问题