文章目录
概述
优雅的代码就是那种看起来简单、易懂,而且好维护的代码。它的设计让人一眼就能明白每部分的作用,避免了复杂和冗余的部分。它使用清晰的变量和方法名称,能让你很快理解代码在做什么,而不是去猜测。
优雅的代码还遵循一些编程的好习惯,比如把代码分成小块,每块负责一个特定的功能,这样更容易修改和扩展。
优雅处理if-else
在实际开发中,要想代码变的更优雅,对if-else
的处理是绝对绕不过去的。举个例子,当你需要处理多种不同类型的请求时,直接在一个方法中使用一大堆if-else
语句来区分不同的逻辑会使代码显得臃肿且难以维护。
public class PaymentService {
public void processPayment(String type) {
if (type.equals("creditcard")) {
// 处理信用卡支付
System.out.println("Processing credit card payment.");
} else if (type.equals("paypal")) {
// 处理 PayPal 支付
System.out.println("Processing PayPal payment.");
} else if (type.equals("bitcoin")) {
// 处理比特币支付
System.out.println("Processing Bitcoin payment.");
} else if (type.equals("banktransfer")) {
// 处理银行转账支付
System.out.println("Processing Bank Transfer payment.");
} else {
throw new IllegalArgumentException("Unsupported payment type");
}
}
}
替换if-else
并不会降低代码的复杂度,相反比较少见的写法可能会增加认知负荷,从而进一步增加了复杂度。之所以要替换过多的if-else
是为了对代码进行解耦合,方便扩展代码,最终方便对代码的维护。
以下有几种常见的方法来处理if-else
。
尽早结束if
避免深层嵌套的if-else
结构,通过在方法的开头处理特殊情况或边界条件,然后直接返回,可以让主要逻辑保持平坦,便于阅读和理解。
public void processOrder(Order order) {
if (order == null) {
throw new IllegalArgumentException("Order cannot be null");
}
if (order.isCancelled()) {
// 处理已取消的订单
return;
}
if (order.isShipped()) {
// 处理已发货的订单
return;
}
// 处理未处理的订单
}
使用switch-case
使用switch-case
语句可以帮助简化多个if-else
语句,使代码更清晰和易读。为了使代码更加优雅和易于维护,可以利用枚举类型来提高代码的安全性,避免重复代码,并在default
分支中处理异常情况。
public enum Command {
START, STOP, PAUSE;
}
public void handleCommand(Command command) {
switch (command) {
case START:
start();
break;
case STOP:
stop();
break;
case PAUSE:
pause();
break;
default:
throw new IllegalArgumentException("Unknown command: " + command);
}
}
private void start() {
System.out.println("Starting...");
}
private void stop() {
System.out.println("Stopping...");
}
private void pause() {
System.out.println("Pausing...");
}
使用枚举替代if-else
如果if-else
语句是基于对象类型或状态的,考虑使用多态。将不同的行为封装到不同的类中,可以避免大量的条件判断。这种方法在许多设计模式中得到了应用,如策略模式、命令模式和状态模式等。
使用枚举代替if-else
的方式也是多态的一种应用,这种方法通过将不同的行为封装在枚举常量中,实现了多态的效果,从而简化了条件判断逻辑。
public class MyTest {
public static void main(String[] args) {
String val = "FOUR";
condition(val);
System.out.println("--------------");
newCondition(val);
}
public static void condition(String val) {
if ("ONE".equals(val)) {
System.out.println("val:" + 1111111);
} else if ("TWO".equals(val)) {
System.out.println("val:" + 2222222);
} else if ("THREE".equals(val)) {
System.out.println("val:" + 3333333);
} else {
System.out.println("val:" + val);
}
}
public static void newCondition(String val) {
ConditionEnum conditionEnum;
try {
conditionEnum = ConditionEnum.valueOf(val);
} catch (IllegalArgumentException e) {
System.out.println("val:" + val);
return;
}
exec(conditionEnum);
}
public static void exec(ConditionEnum conditionEnum) {
conditionEnum.context();
}
enum ConditionEnum {
ONE {
@Override
public void context() {
System.out.println("val:" + 1111111);
}
},
TWO {
@Override
public void context() {
System.out.println("val:" + 2222222);
}
},
THREE {
@Override
public void context() {
System.out.println("val:" + 3333333);
}
};
public abstract void context();
}
}
也可用枚举替换成这样:
public static void newCondition(String val) {
ConditionEnum condition = ConditionEnum.getCondition(val);
System.out.println("val:" + (condition == null ? val : condition.value));
}
enum ConditionEnum {
ONE("ONE",1111111),
TWO("TWO",2222222),
THREE("THREE",3333333)
;
private String key;
private Integer value;
ConditionEnum(String key,Integer value){
this.key = key;
this.value = value;
}
public static ConditionEnum getCondition(String key){
return Arrays.stream(ConditionEnum.values()).filter(x -> Objects.equals(x.key, key)).findFirst().orElse(null);
}
}
使用Map替代if-else
当条件逻辑是基于键值对时,可以使用Map可以替代多个if-else
语句,简化代码结构。
public class MyTest {
public static void main(String[] args) {
String val = "1";
condition(val);
System.out.println("--------------");
newCondition(val);
}
public static void condition(String val) {
if ("1".equals(val)){
System.out.println("val:" + 1111111);
}else if ("2".equals(val)){
System.out.println("val:" + 2222222);
}else if ("3".equals(val)){
System.out.println("val:" + 3333333);
}else {
System.out.println("val:" + val);
}
}
public static void newCondition(String val){
Map<String, Function<?,?>> map = new HashMap<>();
map.put("1",(action) -> 1111111);
map.put("2",(action) -> 2222222);
map.put("3",(action) -> 3333333);
System.out.println("val:" + (map.get(val) != null ? map.get(val).apply(null) : val));
}
}
使用职责链模式替代if-else
职责链模式是一种行为型设计模式,它允许将请求沿着处理对象的链传递,直到一个对象处理它。这个模式避免了使用大量的if-else
或switch-case
语句,尤其适合用于处理多个条件和动态的请求处理流程。
// 处理者接口
public abstract class Approver {
protected Approver nextApprover;
public void setNextApprover(Approver nextApprover) {
this.nextApprover = nextApprover;
}
public abstract void handleRequest(int amount);
}
// 具体处理者:经理
public class Manager extends Approver {
@Override
public void handleRequest(int amount) {
if (amount <= 1000) {
System.out.println("Manager approved $" + amount);
} else if (nextApprover != null) {
nextApprover.handleRequest(amount);
}
}
}
// 具体处理者:总监
public class Director extends Approver {
@Override
public void handleRequest(int amount) {
if (amount <= 5000) {
System.out.println("Director approved $" + amount);
} else if (nextApprover != null) {
nextApprover.handleRequest(amount);
}
}
}
// 具体处理者:副总裁
public class VicePresident extends Approver {
@Override
public void handleRequest(int amount) {
if (amount <= 10000) {
System.out.println("Vice President approved $" + amount);
} else {
System.out.println("Request amount is too large.");
}
}
}
// 客户端
public class Main {
public static void main(String[] args) {
// 创建处理者链
Approver manager = new Manager();
Approver director = new Director();
Approver vp = new VicePresident();
manager.setNextApprover(director);
director.setNextApprover(vp);
// 发起请求
int[] amounts = {500, 1500, 6000, 15000};
for (int amount : amounts) {
System.out.println("Requesting $" + amount);
manager.handleRequest(amount);
System.out.println();
}
}
}
使用模板方法模式替代if-else
模板方法模式是一种行为型设计模式,它允许在父类中定义一个算法的骨架,并将一些步骤的实现推迟到子类中。这种模式可以通过定义一个模板方法,将固定的算法步骤与可变的步骤分开,从而减少if-else
语句的使用,简化代码结构。
// 抽象类
public abstract class DocumentProcessor {
// 模板方法
public final void processDocument() {
readDocument();
processContent();
saveDocument();
}
// 固定步骤:读取文档
private void readDocument() {
System.out.println("Reading document...");
}
// 固定步骤:保存文档
private void saveDocument() {
System.out.println("Saving document...");
}
// 可变步骤:处理文档内容,子类实现
protected abstract void processContent();
}
// 具体类:处理PDF文档
public class PDFDocumentProcessor extends DocumentProcessor {
@Override
protected void processContent() {
System.out.println("Processing PDF content...");
}
}
// 具体类:处理Word文档
public class WordDocumentProcessor extends DocumentProcessor {
@Override
protected void processContent() {
System.out.println("Processing Word content...");
}
}
// 客户端
public class Main {
public static void main(String[] args) {
DocumentProcessor pdfProcessor = new PDFDocumentProcessor();
DocumentProcessor wordProcessor = new WordDocumentProcessor();
System.out.println("Processing PDF Document:");
pdfProcessor.processDocument();
System.out.println();
System.out.println("Processing Word Document:");
wordProcessor.processDocument();
}
}
使用工厂方法模式替代if-else
工厂方法模式是一种创建型设计模式,它定义了一个创建对象的接口,但由子类决定实例化哪一个类。工厂方法模式通过将对象的创建推迟到子类中,避免了在代码中出现大量的if-else
或switch-case
语句,从而简化了代码结构。
// 产品接口
public interface DatabaseConnection {
void connect();
}
// 具体产品:MySQL数据库连接
public class MySQLConnection implements DatabaseConnection {
@Override
public void connect() {
System.out.println("Connecting to MySQL database...");
}
}
// 具体产品:Oracle数据库连接
public class OracleConnection implements DatabaseConnection {
@Override
public void connect() {
System.out.println("Connecting to Oracle database...");
}
}
// 创建者接口
public abstract class DatabaseFactory {
public abstract DatabaseConnection createConnection();
}
// 具体创建者:MySQL工厂
public class MySQLFactory extends DatabaseFactory {
@Override
public DatabaseConnection createConnection() {
return new MySQLConnection();
}
}
// 具体创建者:Oracle工厂
public class OracleFactory extends DatabaseFactory {
@Override
public DatabaseConnection createConnection() {
return new OracleConnection();
}
}
// 客户端
public class Main {
public static void main(String[] args) {
// 根据需求选择具体的工厂
DatabaseFactory factory = getDatabaseFactory("MySQL");
DatabaseConnection connection = factory.createConnection();
connection.connect();
}
private static DatabaseFactory getDatabaseFactory(String dbType) {
if (dbType.equalsIgnoreCase("MySQL")) {
return new MySQLFactory();
} else if (dbType.equalsIgnoreCase("Oracle")) {
return new OracleFactory();
} else {
throw new IllegalArgumentException("Unknown database type");
}
}
}
使用策略模式替代if-else
策略模式是一种行为型设计模式,它定义了一系列的算法,将每一个算法封装起来,并使它们可以相互替换。策略模式使得算法可以独立于使用它的客户端而变化,从而减少了if-else
或switch-case
语句的使用,提供了更灵活的代码结构。
// 策略接口
public interface TextFormatter {
String format(String text);
}
// 具体策略:大写格式化
public class UpperCaseFormatter implements TextFormatter {
@Override
public String format(String text) {
return text.toUpperCase();
}
}
// 具体策略:小写格式化
public class LowerCaseFormatter implements TextFormatter {
@Override
public String format(String text) {
return text.toLowerCase();
}
}
// 上下文
public class TextEditor {
private TextFormatter formatter;
public void setFormatter(TextFormatter formatter) {
this.formatter = formatter;
}
public void printFormattedText(String text) {
if (formatter != null) {
String formattedText = formatter.format(text);
System.out.println(formattedText);
} else {
System.out.println("No formatter set.");
}
}
}
// 客户端
public class Main {
public static void main(String[] args) {
TextEditor editor = new TextEditor();
// 使用大写格式化策略
editor.setFormatter(new UpperCaseFormatter());
editor.printFormattedText("Hello, World!");
// 使用小写格式化策略
editor.setFormatter(new LowerCaseFormatter());
editor.printFormattedText("Hello, World!");
}
}
使用注解替代if-else
使用注解替代if-else
语句是一种现代化的编程方式,可以简化代码并减少条件判断的复杂性。注解可以用来描述程序的各种元数据,这些元数据可以在运行时或编译时被处理,从而决定某些行为。通过注解和相关的处理器,可以将决策逻辑从代码中抽离出来,减少if-else
语句的使用。
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface PayCode {
String value();
String name();
}
@PayCode(value = "alia", name = "支付宝支付")
@Service
public class AliaPay implements IPay {
@Override
public void pay() {
System.out.println("===发起支付宝支付===");
}
}
@PayCode(value = "weixin", name = "微信支付")
@Service
public class WeixinPay implements IPay {
@Override
public void pay() {
System.out.println("===发起微信支付===");
}
}
@PayCode(value = "jingdong", name = "京东支付")
@Service
public class JingDongPay implements IPay {
@Override
public void pay() {
System.out.println("===发起京东支付===");
}
}
@Service
public class PayService2 implements ApplicationListener<ContextRefreshedEvent> {
private static Map<String, IPay> payMap = null;
@Override
public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
ApplicationContext applicationContext = contextRefreshedEvent.getApplicationContext();
Map<String, Object> beansWithAnnotation = applicationContext.getBeansWithAnnotation(PayCode.class);
if (beansWithAnnotation != null) {
payMap = new HashMap<>();
beansWithAnnotation.forEach((key, value) ->{
String bizType = value.getClass().getAnnotation(PayCode.class).value();
payMap.put(bizType, (IPay) value);
});
}
}
public void pay(String code) {
payMap.get(code).pay();
}
}
优雅判空
在开发中,处理空值是一个常见而且重要的任务。不当的处理不仅会导致代码变得复杂和难以维护,还可能引发运行时错误。特别是当我们的代码中充斥着大量的null
检查和条件判断时,程序的可读性和可维护性会大打折扣。优雅地处理空值,不仅是为了提高代码的健壮性,更是为了提升代码的可读性和设计质量。
使用工具类判空
使用工具类来处理空值是一种常见的做法,工具类可以集中处理空值检查逻辑,使代码更加简洁和一致。常见工具类有,Spring中的Assert
类、JDK中的Objects
类、Apache Commons Lang库的StringUtils
类。
Apache Commons Lang库的StringUtils
提供了许多用于处理字符串和空值的方法。
public class Example {
public void printLength(String str) {
// 使用 StringUtils.defaultIfEmpty 提供默认值
String nonNullStr = StringUtils.defaultIfEmpty(str, "Default Value");
System.out.println("Length of string: " + nonNullStr.length());
}
}
Objects
类是 Java7引入的,它提供了一些静态方法来简化对null的检查。
public class Example {
public void printLength(String str) {
// 使用 Objects.requireNonNullElse 提供默认值
String nonNullStr = Objects.requireNonNullElse(str, "Default Value");
System.out.println("Length of string: " + nonNullStr.length());
}
}
在Spring框架中,Assert
类是一个工具类,提供了一组静态方法用于验证条件。它可以帮助开发者在代码中做各种检查,如非空检查、范围检查等,若条件不满足则抛出相应的异常。
public class MyTest {
public static void main(String[] args) {
Object obj = new Object();
Assert.notNull(obj,"对象不能为空");
Assert.isTrue(obj != null,"对象不能为空");
ArrayList<Object> list = new ArrayList<>();
Assert.notEmpty(list,"list不能为空");
}
}
使用Optional判空
Optional是Java8引入的一个容器类,用于表示可能为空的值。它提供了多种方法来处理空值情况,避免了显式的null
检查。
public class UserService {
public Optional<User> findUserById(String userId) {
// 模拟查询用户,返回可能为空的结果
User user = database.findUserById(userId);
return Optional.ofNullable(user);
}
public void processUser(String userId) {
Optional<User> userOpt = findUserById(userId);
userOpt.ifPresent(user -> {
// 处理用户
System.out.println("User found: " + user.getName());
});
}
}
自定义链式调用,对Optional
扩展。
/**
* @author Axin
* @since 2020-09-10
* @summary 链式调用 bean 中 value 的方法
*/
public final class OptionalBean<T> {
private static final OptionalBean<?> EMPTY = new OptionalBean<>();
private final T value;
private OptionalBean() {
this.value = null;
}
/**
* 空值会抛出空指针
* @param value
*/
private OptionalBean(T value) {
this.value = Objects.requireNonNull(value);
}
/**
* 包装一个不能为空的 bean
* @param value
* @param <T>
* @return
*/
public static <T> OptionalBean<T> of(T value) {
return new OptionalBean<>(value);
}
/**
* 包装一个可能为空的 bean
* @param value
* @param <T>
* @return
*/
public static <T> OptionalBean<T> ofNullable(T value) {
return value == null ? empty() : of(value);
}
/**
* 取出具体的值
* @param fn
* @param <R>
* @return
*/
public T get() {
return Objects.isNull(value) ? null : value;
}
/**
* 取出一个可能为空的对象
* @param fn
* @param <R>
* @return
*/
public <R> OptionalBean<R> getBean(Function<? super T, ? extends R> fn) {
return Objects.isNull(value) ? OptionalBean.empty() : OptionalBean.ofNullable(fn.apply(value));
}
/**
* 如果目标值为空 获取一个默认值
* @param other
* @return
*/
public T orElse(T other) {
return value != null ? value : other;
}
/**
* 如果目标值为空 通过lambda表达式获取一个值
* @param other
* @return
*/
public T orElseGet(Supplier<? extends T> other) {
return value != null ? value : other.get();
}
/**
* 如果目标值为空 抛出一个异常
* @param exceptionSupplier
* @param <X>
* @return
* @throws X
*/
public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X {
if (value != null) {
return value;
} else {
throw exceptionSupplier.get();
}
}
public boolean isPresent() {
return value != null;
}
public void ifPresent(Consumer<? super T> consumer) {
if (value != null)
consumer.accept(value);
}
@Override
public int hashCode() {
return Objects.hashCode(value);
}
/**
* 空值常量
* @param <T>
* @return
*/
public static<T> OptionalBean<T> empty() {
@SuppressWarnings("unchecked")
OptionalBean<T> none = (OptionalBean<T>) EMPTY;
return none;
}
}
@Data
public class User {
private String name;
private String gender;
private School school;
@Data
public static class School {
private String scName;
private String adress;
}
}
public static void main(String[] args) {
User axin = new User();
User.School school = new User.School();
axin.setName("hello");
// 1. 基本调用
String value1 = OptionalBean.ofNullable(axin)
.getBean(User::getSchool)
.getBean(User.School::getAdress).get();
System.out.println(value1);
// 2. 扩展的 isPresent方法 用法与 Optional 一样
boolean present = OptionalBean.ofNullable(axin)
.getBean(User::getSchool)
.getBean(User.School::getAdress).isPresent();
System.out.println(present);
// 3. 扩展的 ifPresent 方法
OptionalBean.ofNullable(axin)
.getBean(User::getSchool)
.getBean(User.School::getAdress)
.ifPresent(adress -> System.out.println(String.format("地址存在:%s", adress)));
// 4. 扩展的 orElse
String value2 = OptionalBean.ofNullable(axin)
.getBean(User::getSchool)
.getBean(User.School::getAdress).orElse("家里蹲");
System.out.println(value2);
// 5. 扩展的 orElseThrow
try {
String value3 = OptionalBean.ofNullable(axin)
.getBean(User::getSchool)
.getBean(User.School::getAdress).orElseThrow(() -> new RuntimeException("空指针了"));
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
使用Validator注解判空
在Java中,使用Validator
注解进行判空是一种优雅的方式来验证对象的状态,特别是在处理用户输入和表单数据时。javax.validation
包中的注解可以帮助确保对象的属性满足指定的条件,如非空、长度限制等。这些注解通常与 Java Bean
Validation
规范一起使用,并可以通过Spring的@Valid
注解在控制器中自动触发验证。
从SpringBoot2.3开始,校验包被独立成了一个starter
组件,所以需要引入validation
和web
,而springboot-2.3
之前的版本只需要引入web
依赖就可以了。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
/**
* 常用校验注解
* JSR提供的校验注解:
* @Null 被注释的元素必须为 null
* @NotNull 被注释的元素必须不为 null
* @AssertTrue 被注释的元素必须为 true
* @AssertFalse 被注释的元素必须为 false
* @Min(value) 被注释的元素必须是一个数字,其值必须大于等于指定的最小值
* @Max(value) 被注释的元素必须是一个数字,其值必须小于等于指定的最大值
* @DecimalMin(value) 被注释的元素必须是一个数字,其值必须大于等于指定的最小值
* @DecimalMax(value) 被注释的元素必须是一个数字,其值必须小于等于指定的最大值
* @Size(max=, min=) 被注释的元素的大小必须在指定的范围内
* @Digits (integer, fraction) 被注释的元素必须是一个数字,其值必须在可接受的范围内
* @Past 被注释的元素必须是一个过去的日期
* @Future 被注释的元素必须是一个将来的日期
* @Pattern(regex=,flag=) 被注释的元素必须符合指定的正则表达式
*
*/
@Data
public class UserEntity1 {
@NotNull(message = "id不能为空")
private Integer id;
@NotBlank(message = "用户名不能为空")
private String username;
@Size(max = 10,min = 7,message = "密码位数必须在7-10位")
@NotBlank(message = "密码不能为空")
private String password;
@NotBlank(message = "企业名称不能为空")
private String enterpriseName;
@Null
private Integer contactId;
@Null
private BigDecimal money;
@Email(message = "电子邮箱不能为空")
private String email;
@NotBlank(message = "手机号不能为空")
private String mobile;
}
@Controller
@RequestMapping("/user")
public class UserController {
@PostMapping("/create")
@ResponseBody
public String createUser(@Valid @RequestBody User user) {
// 如果 user 对象不符合验证规则,Spring 会自动返回错误响应
return "User created successfully";
}
}