专业的JAVA编程教程与资源

网站首页 > java教程 正文

JAVA代码规范与编写高质量代码的建议(1)

temp10 2024-12-18 17:12:26 java教程 10 ℃ 0 评论

目录

  • 1. 创建对象使用@Builder
  • 2. 让接口职责单一
  • 3. 增强类的可替换性
  • 4. 依赖抽象而不是实现
  • 5. 策略模式优化if-else
  • 6. 行为参数化传递代码
  • 7. 接口验证优化与自定义注解
  • 8. 原型模式提高创建对象的性能
  • 9. CompleteFuture异步编程提高RPC效率
  • 10. AOP实现日志记录

1. 创建对象使用lombok的@Builder,代码更加简洁

package com.marion.codestandard.demo001.v1;

/**
 * @author Marion
 */
public class User001 {

    private int id;

    private String name;

    private String email;

    private String password;

    public User001() {
    }

    public User001(int id, String name, String email, String password) {
        this.id = id;
        this.name = name;
        this.email = email;
        this.password = password;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    /**
     * com.marion.codestandard.demo001.v1.User001@26ba2a48
     * User002(id=1, name=marion, email=email, password=123456)
     */
    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", email='" + email + '\'' +
                ", password='" + password + '\'' +
                '}';
    }
}
package com.marion.codestandard.demo001.v2;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * @author Marion
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class User002 {

    private int id;

    private String name;

    private String email;

    private String password;

}
package com.marion.codestandard.demo001;

import com.marion.codestandard.demo001.v1.User001;
import com.marion.codestandard.demo001.v2.User002;

/**
 * 1. 创建对象使用@Builder
 * @author Marion
 */
public class Demo001 {

    public static void main(String[] args) {

        User001 user001 = new User001();
        user001.setId(1);
        user001.setName("marion");
        user001.setEmail("email");
        user001.setPassword("123456");

        System.out.println(user001);

        User002 user002 = User002.builder()
                .id(1)
                .name("marion")
                .email("email")
                .password("123456")
                .build();

        System.out.println(user002);

    }

}

2. 让接口职责单一

一个类所对应的需求功能越多,引起变化的可能性就越大,单一职责原则(Single ResponsibilityPrinciple,简称SRP)就是要求我们的接口(或类)尽可能保持单一,它的定义是说“一个类有且仅有一个变化的原因(There should never be more than one reason for a class to change)”,那问题是什么是职责呢?

职责是一个接口(或类)要承担的业务含义,或是接口(或类)表现出的意图,例如一个User类可以包含写入用户信息到数据库、删除用户、修改用户密码等职责,而一个密码工具类则可以包含解密职责和加密职责。明白了什么是类的职责单一,再来看看它有什么好处。单一职责有以下三个优点:

JAVA代码规范与编写高质量代码的建议(1)

(1)类的复杂性降低

(2)可读性和可维护性提高

(3)降低变更风险

既然单一职责有这么多的优点,那我们该如何应用到设计和编码中呢?下面以电话通信为例子来说明如何实施单一职责原则

(1)分析职责

一次电话通信包含四个过程:拨号、通话、回应、挂机,我们来思考一下该如何划分职责,这四个过程包含了两个职责:一个是拨号和挂机表示的是通信协议的链接和关闭,另外一个是通话和回应所表示的数据交互。

(2)设计接口

(3)合并实现

package com.marion.codestandard.demo002;

import com.marion.codestandard.demo002.v1.Phone001;
import com.marion.codestandard.demo002.v1.Phone002;
import com.marion.codestandard.demo002.v1.Phone003;
import com.marion.codestandard.demo002.v2.Phone2G;
import com.marion.codestandard.demo002.v2.Phone3G;
import com.marion.codestandard.demo002.v2.Phone4G;

/**
 * 2. 让接口职责单一
 * @author Marion
 * @date 2022/5/13 15:50
 */
public class Demo002 {

    /**
     * 一次电话通信包含四个过程:拨号、通话、回应、挂机,我们来思考一下该如何划分职责,
     * 这四个过程包含了两个职责:一个是拨号和挂机表示的是通信协议的链接和关闭,
     * 另外一个是通话和回应所表示的数据交互。
     */
    public static void main(String[] args) {

        /**
         * v1:
         * 1. 很多重复代码,难以维护和扩展
         * 2. 面向过程开发
         */
        Phone001 phone001 = new Phone001();
        phone001.start();

        Phone002 phone002 = new Phone002();
        phone002.start();

        Phone003 phone003 = new Phone003();
        phone003.start();

        /**
         * v2:
         * 1. 扩展方便,结构清晰
         */
        Phone2G phone2G = new Phone2G();
        phone2G.start();
        Phone3G phone3G = new Phone3G();
        phone3G.start();
        Phone4G phone4G = new Phone4G();
        phone4G.start();

    }


}

3. 增强类的可替换性

java的三大特征:封装、继承、多态,这是每个初学者都会学习到的知识点,这里暂且不说封装和继承,单单说说多态。一个接口可以有多个实现方式,一个父类可以有多个子类,并且可以把不同的实现或子类赋给不同的接口或父类。多态的好处非常多,其中有一点就是增强了类的可替换性,但是单单一个多态特性,很难保证我们的类是完全可以替换的,幸好还有一个里氏替换原则来约束。

里氏替换原则是说“所有引用基类的地方必须能透明地使用其子类的对象”,通俗点讲,只要父类型能出现的地方子类型就可以出现,而且将父类型替换为子类型还不会产生任何错误或异常,使用者可能根本就不需要知道是父类型还是子类型。但是,反过来就不行了,有子类型出现的地方,父类型未必就能适应。

为了增强类的可替换性,就要求我们在设计类的时候考虑以下三点:

(1)子类型必须完全实现父类型的方法

(2)前置条件可以被放大

方法中的输入参数称为前置条件,它表达的含义是你要让我执行,就必须满足我的条件。前置条件是允许放大的,这样可以保证父类型行为逻辑的继承性

(3)后置条件可以被缩小

package com.marion.codestandard.demo003;

import com.marion.codestandard.demo002.v2.Phone;
import com.marion.codestandard.demo002.v2.Phone2G;
import com.marion.codestandard.demo002.v2.Phone3G;
import com.marion.codestandard.demo002.v2.Phone4G;

/**
 * 3. 增强类的可替换性
 * @author Marion
 * @date 2022/5/13 16:15
 */
public class Demo003 {

    public static void main(String[] args) {

        Phone phone2G = new Phone2G();
        phoneCall(phone2G);

        Phone phone3G = new Phone3G();
        phoneCall(phone3G);

        Phone phone4G = new Phone4G();
        phoneCall(phone4G);

    }

    public static void phoneCall(Phone phone) {
        phone.start();
    }
}

4. 依赖抽象而不是实现

在面向过程开发中,我们考虑的是如何实现,依赖的是每个具体实现,而在OOP中,则需要依赖每个接口,而不能依赖具体的实现,比如我们要到北京出差,应该依赖交通工具,而不是依赖的具体飞机或火车,也就是说我们依赖的是交通工具的运输能力,而不是具体的一架飞机或某一列火车。这样的依赖可以让我们实现解耦,保持代码间的松耦合,提高代码的复用率,这也是依赖倒置原则(Dependence Inversion Principle,简称DIP)提出的要求。

依赖倒置原则的原始定义是:High level modules should not depend upon low level modules.Both should depend upon abstractions. Abstractions should not depend upon details. Detailsshould depend upon abstractions。翻译过来,包含三层含义:

  1. 高层模块不应该依赖低层模块,两者都应该依赖其抽象。
  2. 抽象不应该依赖细节。
  3. 细节应该依赖抽象。

高层模块和低层模块容易理解,每一个逻辑的实现都是由原子逻辑组成的,不可分割的原子逻辑就是低层模块,原子逻辑的再组装就是高层模块。那什么是抽象,什么又是细节呢?在Java语言中,抽象就是指接口或抽象类,两者都是不能直接被实例化的;而细节就是实现类,实现接口或继承抽象类而产生的类就是细节,其特点是可以直接被实例化,也就是可以加上一个关键字new产生一个对象。依赖倒置原则在Java语言中的表现就是:

  1. 模块间的依赖是通过抽象发生的,实现类之间不发生直接的依赖关系,其依赖关系是通过接口或抽象类产生的。
  2. 接口或抽象类不依赖于实现类。
  3. 实现类依赖接口或抽象类。

更加精简的定义就是“面向接口编程”,它的本质就是通过抽象(接口或抽象类)使各个类或模块的实现彼此独立,互不影响,从而实现模块间的松耦合。那我们怎么在项目中使用这个规则呢?只要遵循以下的几个规则就可以。

(1)尽量抽象

(2)表面类型必须是抽象的

(3)任何类都不应该从具体类派生

(4)尽量不要覆写基类的方法

(5)抽象不关注细节

package com.marion.codestandard.demo004;

import com.marion.codestandard.demo001.v2.User002;

import java.util.Optional;

/**
 * @author Marion
 * @date 2022/5/13 16:24
 */
public interface UserService {

   Optional<User002> findById(int id);

}

5. 策略模式优化if-else

在策略模式(Strategy Pattern)中,一个类的行为或其算法可以在运行时更改。这种类型的设计模式属于行为型模式。

在策略模式中,我们创建表示各种策略的对象和一个行为随着策略对象改变而改变的 context 对象。策略对象改变 context 对象的执行算法。

介绍

意图:定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换。

主要解决:在有多种算法相似的情况下,使用 if...else 所带来的复杂和难以维护。

何时使用:一个系统有许多许多类,而区分它们的只是他们直接的行为。

如何解决:将这些算法封装成一个一个的类,任意地替换。

关键代码:实现同一个接口。

应用实例: 1、诸葛亮的锦囊妙计,每一个锦囊就是一个策略。 2、旅行的出游方式,选择骑自行车、坐汽车,每一种旅行方式都是一个策略。 3、JAVA AWT 中的 LayoutManager。

优点: 1、算法可以自由切换。 2、避免使用多重条件判断。 3、扩展性良好。

缺点: 1、策略类会增多。 2、所有策略类都需要对外暴露。

使用场景: 1、如果在一个系统里面有许多类,它们之间的区别仅在于它们的行为,那么使用策略模式可以动态地让一个对象在许多行为中选择一种行为。 2、一个系统需要动态地在几种算法中选择一种。 3、如果一个对象有很多的行为,如果不用恰当的模式,这些行为就只好使用多重的条件选择语句来实现。

注意事项:如果一个系统的策略多于四个,就需要考虑使用混合模式,解决策略类膨胀的问题。

package com.marion.codestandard.demo005.v1;

import com.marion.codestandard.demo005.enums.PaymentType;

/**
 * 商城购买商品,选择支付方式:微信支付、支付宝支付、银行卡支付、白条
 * @author Marion
 * @date 2022/5/13 16:33
 */
public class Shop001 {

    public void buyGoods(PaymentType paymentType) {

        if (paymentType == PaymentType.ALIPAY) {
            alipayBuyGoods();
        } else if(paymentType == PaymentType.WEXIN) {
            wxBuyGoods();
        } else if(paymentType == PaymentType.BANKCARD) {
            bankcardBuyGoods();
        } else if (paymentType == PaymentType.WHITE) {
            whiteBuyGoods();
        }

    }

    public void alipayBuyGoods() {
        System.out.println("[buyGoods] alipayBuyGoods");
    }

    public void wxBuyGoods() {
        System.out.println("[buyGoods] wxBuyGoods");

    }

    public void bankcardBuyGoods() {
        System.out.println("[buyGoods] bankcardBuyGoods");
    }

    public void whiteBuyGoods() {
        System.out.println("[buyGoods] whiteBuyGoods");
    }

}
package com.marion.codestandard.demo005.v2;

import com.marion.codestandard.demo005.enums.PaymentType;
import com.marion.codestandard.demo005.v2.strategy.AliPaymentStrategy;
import com.marion.codestandard.demo005.v2.strategy.BankCardPaymentStrategy;
import com.marion.codestandard.demo005.v2.strategy.WxPaymentStrategy;

/**
 * @author Marion
 * @date 2022/5/13 16:46
 */
public class GoodsContext {

    private Payment payment;

    public GoodsContext(Payment payment) {
        this.payment = payment;
    }

    public void buy() {
        payment.buy();
    }

    public static Payment getGoodsContext(PaymentType type) {
        Payment payment = null;
        switch (type) {
            case WEXIN:
                payment = new WxPaymentStrategy();
                break;
            case ALIPAY:
                payment = new AliPaymentStrategy();
                break;
            case BANKCARD:
                payment = new BankCardPaymentStrategy();
                break;
            default:
                break;
        }
        return payment;
    }
}


代码地址

https://gitee.com/zeus-maker/marion-code-standard

编写高质量代码:改善Java程序的151个建议

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表