扫一扫
关注公众号
1、如何评价代码质量的高低
最常用到的几个评判代码质量的标准是:可维护性、可读性、可扩展性、灵活性、简洁性、可复用性、可测试性。
2、抽象类与接口的应用场景区别
如果要表示一种is-a的关系,并且是为了解决代码复用问题,就使用抽象类。如果要表示一种has-a的关系,并且为了解决抽象而非代码复用问题,就使用接口。
3、基于接口而非实现编程(基于抽象而非实现编程)
做软件设计的时候要有抽象意识、封装意识、接口意识,不要暴露任何实现细节,保证上下游调用稳定性,降低耦合性。
4、多用组合少用继承
利用组合、接口、委托,解决层次过深、过复杂的继承关系,避免影响代码的可维护性。
5、常见的不满足单一职责原则的设计
6、常见的违背里氏替换原则的设计
7、开闭原则
尽量让修改操作更集中、更少、更上层。
8、接口隔离原则
接口隔离原则提供了一种判断接口是否满足单一职责原则的方法。如果调用者只使用部分接口或者接口的部分功能,那接口的设计就不够职责单一。
9、依赖注入
依赖注入是一种具体的编程技巧,关注的是对象创建与类之前的关系,目的是提高代码的扩展性,我们可以灵活地替换依赖的类。实际上依赖注入和基于接口而非实现编程都是基于开闭原则思路的。
10、如何写出满足KISS原则的代码
11、如何提高代码的可复用性
功能语义重复和代码执行重复其实都是违反DRY原则的,那如何提高代码的可复用性呢?
12、迪米特法则
不该有直接依赖关系的类之间,不要有依赖;有依赖关系的类之间,尽量只依赖必要的接口。
13、代码分层的作用
代码分层的作用有:代码复用、隔离变化、隔离关注点、提供可测性、应对系统的复杂性。
其实编程就是一个拼插积木的过程。系统划分成若干个层次,每一层专注解决某个领域的问题。大问题分解成若干个子问题,如果子问题还没有直接解决,则继续分解成子子问题,直到可以直接解决的程序。
14、重构思绪
一定要建立持续重构意识,把重构作为开发必不可少的部分,融入到日常开发中。
15、常见的测试不友好的代码有下面五种
16、如何通过封装、抽象、模块化、中间层等解耦代码?
单一职责原则、基于接口而非实现编程、依赖注入、多用组合少用继承、迪米特法则等。当然还有一些设计模式,比如观察者模式。
17、让你快速地改善代码质量的20条编程规范
18、如何发现代码质量问题,常规checklist
19、如何发现代码质量问题,业务需求checklist
20、程序出错该返回啥?错误码、NULL值、空对象、异常对象?
对于查找函数,数据不存在并非是异常情况,它是一种正常行为,所以返回不存在语义的NULL值比返回异常更加合理。
对于函数抛出的异常,我们有三种处理方法:直接吞掉、直接往上抛出、包裹成新的异常抛出。如果调用者关心该异常就往上抛出。如果向上抛出的异常和业务概率没有太大相关性,或者暴露过多实现细节,或者调用者看到这个异常,并不能理解异常到底代表了什么,该如何处理,那就必须包裹成新的异常。
如果函数是private类私有的,只在类内部被调用,完全在自己的掌控中,不需要判断非空。否则,为了尽可能提高代码的健壮性,就需要做NULL值或空字符串的判断。
21、单例模式
单例模式通常用来避免资源访问冲突和表示业务概念的全局唯一性。但是也存在一些缺点,比如对OOP特性的支持不友好、隐藏类之间的依赖关系、对代码的扩展性不友好(如资源池设计动态修改参数)、对代码的可测性不友好。基于此,我们可以使用工厂模式、IOC容器等解决方案替换单例模式。
单例中对象的唯一性的作用范围内是进程内的。更严格地说,对于Java语言来说,单例类对象的唯一性的作用范围是类加载器。因为在Java中,两个类来源于同一个Class文件,被同一个虚拟机加载,只要加载它的类加载器不同,那这两个类就必定不相等。
其他单例模式的问题:如何线程唯一的单例?可以利用Java语言本身的ThreadLocal工具类。如何实现集群环境下的单例?将单例对象序列化存储到外部空间,使用时进行反序列化。如何实现一个多例模式?可以利用ConcurrentHashMap的putIfAbsent方法。多例模式有点类似枚举单例和享元模式。
22、工厂模式
当创建逻辑比较复杂,是一个”大工程”的时候,我们就考虑使用工厂模式,封装对象的创建过程,将对象的创建和使用相分离。
判断要不要使用工厂模式的参考指标:封装变化(创建逻辑的变更对调用者透明)、代码复用、隔离复杂性(调用者无需了解过程)、控制复杂度(让原本的类职责更单一)。
23、DI容器
配置解析、对象创建、对象生命周期管理、提供执行入口。
24、建造者模式
针对类属性有默认值、不可变性、有依赖关系、有约束条件的复杂型对象,通常使用建造者模式来构建,然后在build方法中进行合法性校验。
25、原型模式
原型设计模式:如果对象的创建成本比较大,而同一个类的不同对象之间区别不大,在这种情况下,我们可以利用对已有对象进行复制的方式来创建对象,以达到节省创建时间的目的。
不过复制对象时尽量使用深拷贝的方式,比如先将对象序列化,然后再序列化成新的对象。
26、代理模式
代理模式在不改变原始类代码的情况下,通过引入代理类来给原始类附加功能,比如在代理类中通过委托进行增强。普通的代理模式都需要为每个原始类创建代理类,过于麻烦。所以我们可以使用动态代理来进行优化。
代理模式的应用场景,包括业务系统的非功能性需求开发(监控、统计、限流、事务、日记)、在RPC和缓存中的应用等。可以看到,它的目的主要是控制访问。
27、桥接模式
桥接模式可以理解为将抽象和实现解藕,让它们可以独立变化。另外一种的理解是一个类存在多个独立变化的维度,我们就应该通过组合的方式,让这些维度可以独立进行扩展。
桥接模式的经典应用是JDBC驱动,我们只需要修改很少的配置或者代码,就可以从一种数据库切成另外一个数据库。
28、装饰器模式
通过将原始类以组合的方式注入到装饰器类中,以增强原始类的功能,而不是使用继承,避免继承结构爆炸。另外,装饰器类通常和原始类继承相同的抽象类或者接口,此时如果方法不需要增强,重新调用原始类的方法即可。
应用示例:Java IO操作中的BufferedInputStream、InputStream、FilterInputStream。
29、适配器模式
适配器是一种事后补救策略,它提供一个新的接口,将不兼容的接口转换为可兼容的接口,让原本由于接口不兼容不能一起工作的类可以一起工作。
适配器的类型分别为类适配器(基于继承)、对象适配器(甚于组合)。它的应用场景包括:封装有缺陷的接口设计、统一多个类的接口设计、替换依赖的外部系统、兼容老版本接口、适配不同格式的数据。比如兼容老版本接口时,不直接删除需要废弃的接口而是利用组合委托给新版本实现。
应用示例:JAVA的日志框架Slf4j。
31、享元模式
享元模式通过缓存技术,将重复或者相似的对象设计成不可变对象,让其他对象共享它们,从而达到节省空间的目的。不过,它对垃圾回收不友好。
应用示例:棋牌类游戏、文本编辑器(特点是格式有限)、Integer对象(特点是提前创建好缓存)、String字符串(特点是运行时根据需要创建和缓存)。
32、观察者模式
将频繁变更的方法后置处理逻辑独立成对象,这些对象都实现了消息接收接口。当方法执行后,依次遍历这些观察者对象的消息接收方法,或者以异步非阻塞的方式进行遍历。
应用示例:邮件订阅、RSS Feeds
33、模板方法
在一个方法中定义好算法骨架,并将某些步骤推迟到子类中实现,这样子类就可以在不改变整体行为的情况下将业务代码通过扩展点内嵌到骨架中。
应用示例:模板方法定义为final,避免被重写,需要子类重写的方法定义为abstract。
34、回调方法
回调是指将包含执行方法的类对象传递给另外一个通用类,由它来触发执行方法。这样可以复用通用类的功能,比如创建连接、关闭资源等。可以看出,同步回调类似模板方法,区别是回调是基于组合方式实现的。异步回调则类似于观察者模式。
应用示例:ShutdownHook优雅关闭、JdbcTemplate类。
35、策略模式
通过查表法动态获取对应的策略,而不是通过冗长的if-else条件判断。可以看到,这种模式解藕了策略的定义、创建、使用,有效控制了代码的复杂度,让每个部分都不至于过于复杂、代码量过多。
36、职责链模式
将多个处理器通过链表或者数组的方式组成一条链路,然后请求依次经过处理器的逻辑处理,或者某个处理器中断后不再往后执行,另外,我们还可以基于配置文件动态加载处理器列表。显然,这种模式能让我们的代码易于扩展。
应用示例:校验链Javax.servlet.Filter、拦截器Spring Interceptor。
37、状态模式
状态模式的过程为,满足其中一种条件后就将当前状态转移为新的状态并执行相关动作。一般情况下借助二维数组来表示状态转移图。
应用示例:游戏状态机引擎。
38、迭代器模式
迭代器的设计思路如下,迭代器定义 hasNext()、currentItem()、next() 三个最基本的方法。待遍历的容器对象通过依赖注入传递到迭代器类中。容器通过 iterator() 方法来创建迭代器。可以看到,这种模式将容器和迭代操作分离,各自职责更加单一,而且我们可以轻松替换迭代算法。
不过,在通过迭代器来遍历集合元素的同时,增加或者删除集合中的元素,有可能会导致某个元素被重复遍历或遍历不到。但是,我们可以利用快照版本进行优化,每次新增或者删除容器元素时不真正执行操作,而是标注操作时间,当遍历时根据时间进行过滤。
应用示例:数据库中的游标。
持续更新中……。更多精彩,欢迎关注公众号:松花皮蛋的黑板报