【Java后台开发规范】--- 设计原则

前言

做Java开发的,大多数可能都有看过阿里的Java后台开发手册,里面有关于Java后台开发规范的一些内容,基本覆盖了一些通用、普适的规范,但大多数都讲的比较简洁,本文主要会用更多的案例来对一些规范进行解释,以及结合自己的经验做补充!

其他类型的规范

【Java后台开发规范】— 不简单的命名
【Java后台开发规范】— 日志的输出
【Java后台开发规范】— 线程与并发
【Java后台开发规范】— 长函数、长参数
【Java后台开发规范】— 圈复杂度
【Java后台开发规范】— Null值处理
【Java后台开发规范】— 异常的处理

设计原则

单一职责

一个类或者模块只负责完成一个职责,说明白点就是方法的功能要单一,上升到微服务就是业务领域的能力要单一。

单一职责概念上很简单,但要真正的去应用却不容易,如何判断一个职责是否单一本身就没有统一的标准,每个人处在不同的认知层次,那么对于单一职责的理解也就不一样,甚至有的时候,只要作用于不同业务场景下,单一职责的范围也会发生变化。

参考可能破坏单一职责的情况:

  • 一个类或者一个函数的代码行数过多。

  • 一个类或者一个函数依赖其他的地方过多。

  • 有多个动机,但修改却是同一个类。

JDBC大家都了解,一般通过JDBC连接并操作数据库都有几步标准的流程

1、Connection负责与数据库的交互。
2、Statement负责执行SQL、返回结果。
3、ResultSet负责结果集处理。

上面的业务流程不经意间就可能被一气呵成的写完,我想大多数人平时在写业务代码时一定也是这样,想想JDBC这样设计的好处吧!

开闭原则

软件实体(模块、类、函数等等)应该是对扩展开放、对修改关闭的,大部分的设计模式都是为了解决代码扩展性的问题,遵从的就是开闭原则,所谓对扩展开放、对修改关闭指的就是当要添加一个新功能时,应该在已有代码的基础上添加新的类或者方法,而不是修改原来的类或者方法。

扩展开放:意味着当有需求变更时,可以对模块、类、函数等进行扩展,使其满足需求。

修改关闭:意味着当要对对模块、类、函数等进行扩展时,不需要修改原代码;对于已经完成的类文件不需要重新编辑;对于已经编译打包好的模块,不需要再重新编译。

唯一不变的就是变化,我们应该在设计时让代码尽量能够适应变化,因此要求我们要有良好的抽象意识、封装意识,真正的面向对象开发,而不是面向过程,将可变的部分封装起来,隔离变化,对外提供抽象不变的接口,真正体会到多态的特性所带来的好处,真正做到面向接口编程。

面向接口,无非具体实现

// 接口
public interface MQService{}

// 实现类
public class KafkaMQService implements MQService{}

// 实现类
public class RocketMQService implements MQService{}
// 接口
public interface CacheService{}

// 实现类
public class RedisService implements CacheService{}

// 实现类
public class MemcachedService implements CacheService{}

里式替换原则

子类对象可以替换父类对象,并不影响原来的业务逻辑。里式替换原则强调的是子类无差异替换父类,应当按照协议来设计,父类定义标准协议,子类改变的是内部实现逻辑,但不改变协议本身的约定。

class A{
	public void sendMessage(String message){
        mqService.send(message);
    } 
}

class B extends A{
    @Override
    public void sendMessage(String message){
        if(message.length() > 200){
            // 压缩
            byte[] msgBytes = compress(message);
            mqService.send(msgBytes);
            return;
        }
        mqService.send(message);
    } 
}

class Demo{
    public void method(A a){
        a.sendMessage("abc");
    }
}

class Main{
    public static void main(String[] args) {
    	Demo demo = new Demo();
        demo.method(new A());
        // 子类替换父类
        demo.method(new B());
	}
}

这样看起来,就是利用了多态的特性,但多态是面向对象语言的一大特性,而里式替换则是一种设计原则,比如:如果B类改成如下这样,就不符合里式替换设计原则了。

class B extends A{
    @Override
    public void sendMessage(String message){
        if(message.length() > 200){
            // 丢弃
            return;
        }
        mqService.send(message);
    } 
}

class B extends A{
    @Override
    public void sendMessage(String message){
        if(message.length() > 200){
            // 压缩
            try{
                byte[] msgBytes = compress(message);
            	mqService.send(msgBytes);
            	return;
            }catch(Exception e){
                throw new RuntimeException("...");
            }
            
        }
        mqService.send(message);
    } 
}

第一种不符合是因为修改了业务逻辑,第二种不符合是因为违反了父类协议。

建议:如果子类不能直接替换掉父类,那么建议通过依赖、组合等方式替代。

接口隔离原则

调用者不应该依赖于它不需要的接口,这条原则主要是让我们注意接口的设计,避免大而全的接口,接口的职责划分应该明确,如果调用者每次只使用部分接口、那很有可能这个接口的设计就不太合理。

public interface UserService{
	User getUserById(String userId);
}

public interface LoginService{
	boolean login(User user);
}

// 不应该用一个实现类实现两个接口
public class UserLoginServiceImpl implements UserService, LoginService{
    // ...
}

注意:接口隔离原则告诫我们要避免大而全的接口,尽量细化接口的功能,以此提供代码的灵活性,但也要注意不要过于细化,导致接口数量过多,理解高内聚、低耦合的意义,只专注为一个模块提供应有的功能(高内聚),暴露尽可能少的方法(低耦合)。

依赖倒置原则

高层模块不依赖于底层模块,两者应该通过抽象来互相依赖,抽象不要依赖于具体实现,具体实现依赖于抽象。

所谓高层模块不依赖底层模块指的就是,调用者不依赖于被调用者,就好像JDBC定义的规范一样,各个数据库厂商在开发设计时和具体的业务逻辑并没有任何依赖关系,而是依赖一种抽象,这个抽象实际上就是JDBC规范,JDBC规范不依赖于具体实现。

Servlet规范定义了Servlet接口

public interface Servlet {

    public void init(ServletConfig config) throws ServletException;
    
    public ServletConfig getServletConfig();
   
    public void service(ServletRequest req, ServletResponse res)
	throws ServletException, IOException;
	
    public String getServletInfo();
   
    public void destroy();
}

虽然定义了接口,但并没有让各个Web Server直接去实现,而是又抽象了HttpServlet类,现在Web Server直接依赖HttpServlet类进行扩展。

  • Servlet:高层模块

  • HttpServlet:抽象

  • SpringMVC(DispatcherServlet)、Undertow(DefaultServlet):底层模块

迪米特法则

迪米特法则又叫最少知识原则,改原则的目的是为了减少代码之间的耦合度,减少类与类之间的依赖,减少细节的暴露,使得功能更加的独立。

改原则主要包含如下三个方面:

  • 每个单元对其它单元只拥有有限的知识,而且这些单元是与当前单元有紧密联系的.
  • 每个单元只能与其朋友交谈,不与陌生人交谈.
  • 只与自己最直接的朋友交谈.

DRY原则

因为描述为:Don‘t Repeat Yourself,在编程中可以理解为不要写重复的代码,比如同样的业务需求,存在多处不同的实现逻辑,这就会造成代码的阅读困难、维护困难。

比如对于list集合去重,实现方式有很多。

通过Set特性实现

public static <T> List<T> distinct(List<T> list) {
    return new ArrayList<>(new LinkedHashSet<>(list));
}

通过Java8提供的stream特性实现

public static <T> List<T> distinct(List<T> list) {
    return list.stream().distinct().collect(Collectors.toList());
}

在没有明显的性能差异时,使用哪种方式都可以,但请选择其中一种,不要采用多种方法实现同样的业务逻辑,例子比较简单,你可能还感触不深,试想,如果这是一段复杂的业务逻辑,但却有多种不同的实现方式,肯定会让阅读的人感到很诧异,他肯定会想,这几种实现方式到底有什么不同呢?如果要修改业务逻辑,到底需要改几处呢?还有没有其他被遗漏的地方?

KISS原则

KISS 是英文 Keep it Simple and Stupid的缩写,意思就是保持简单和愚蠢,这个原则在很多行业都适用,比如产品设计要保持简单,企业管理要保持简单,化繁为简。

实践在编码中,就是要保持代码的可读性和可维护性,让代码容易读懂,容易修改。

何为简单?

  • 尽量不要使用一些冷门的、奇淫技巧的方式来实现你的业务逻辑。(类似:某种高级语法、正则表达式等)

  • 善于使用已有的工具类,不需要重复造轮子。

  • 不要过渡优化,比如用位运算替代算术方法,简单的业务逻辑,还要使用设计模式。

总结

设计原则是一块比较难以理解与统一的编程规范,因为它不像其他规范一样,都有明确的准则、模板、公式等,设计原则更多的是理解,不同的人理解可能不一样,一段代码,有些人可能认为已经符合设计原则了,有些人则认为不符合,有些人认为封装的合适,有些人则认为是过渡封装,所以对于设计原则更重要的应该是保持团队中的认知统一,让团队中的成员保持统一的衡量尺度,不断的提高团队的整体认知水平,这样才能充分发挥出设计原则的作用。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

码拉松

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值