kuler 网站,跳转网站正在建设中,深圳品牌网站设计专家,手机网站单页文章目录 前言1. 抽取公用方法2. 抽工具类3. 反射4.泛型5. 继承与多态6.使用设计模式7.自定义注解(或者说AOP面向切面)8.函数式接口和Lambda表达式 前言
日常开发中#xff0c;我们经常会遇到一些重复代码。大家都知道重复代码不好#xff0c;它主要有这些缺点#xff1a;可… 文章目录 前言1. 抽取公用方法2. 抽工具类3. 反射4.泛型5. 继承与多态6.使用设计模式7.自定义注解(或者说AOP面向切面)8.函数式接口和Lambda表达式 前言
日常开发中我们经常会遇到一些重复代码。大家都知道重复代码不好它主要有这些缺点可维护性差、可读性差、增加错误风险等等。最近呢我优化了一些系统中的重复代码用了好几种的方式。感觉挺有用的所以本文给大家讲讲优化重复代码的几种方式。 抽取公用方法 抽个工具类 反射 泛型 继承和多态 设计模式 函数式 AOP
1. 抽取公用方法
抽取公用方法是最常用的代码去重方法~ 比如这个例子分别遍历names列表然后各自转化为大写和小写打印出来
public class TianLuoExample {public static void main(String[] args) {ListString names Arrays.asList(Alice, Bob, Charlie, David, TianLuo);System.out.println(Uppercase Names:);for (String name : names) {String uppercaseName name.toUpperCase();System.out.println(uppercaseName);}System.out.println(Lowercase Names:);for (String name : names) {String lowercaseName name.toLowerCase();System.out.println(lowercaseName);}}
}显然都是遍历names过程代码是重复的只不过转化大小写不一样。我们可以抽个公用方法processNames优化成这样
public class TianLuoExample {public static void processNames(ListString names, FunctionString, String nameProcessor, String processType) {System.out.println(processType Names:);for (String name : names) {String processedName nameProcessor.apply(name);System.out.println(processedName);}}public static void main(String[] args) {ListString names Arrays.asList(Alice, Bob, Charlie, David, TianLuo);processNames(names, String::toUpperCase, Uppercase);processNames(names, String::toLowerCase, Lowercase);}
}2. 抽工具类
我们优化重复代码抽一个公用方法后如果发现这个方法有更多共性就可以把公用方法升级为一个工具类。比如这样的业务场景我们注册的时候修改邮箱重置密码等都需要校验邮箱 实现注册功能时用户会填邮箱需要验证邮箱格式
public class RegisterServiceImpl implements RegisterService{private static final String EMAIL_REGEX ^[A-Za-z0-9_.-](.)$;public boolean registerUser(UserInfoReq userInfo) {String email userInfo.getEmail();Pattern pattern Pattern.compile(EMAIL_REGEX);Matcher emailMatcher pattern.matcher(email);if (!emailMatcher.matches()) {System.out.println(Invalid email address.);return false;}// 进行其他用户注册逻辑比如保存用户信息到数据库等// 返回注册结果return true;}
}在密码重置流程中通常会向用户提供一个链接或验证码并且需要发送到用户的电子邮件地址。在这种情况下也需要验证邮箱格式合法性
public class PasswordServiceImpl implements PasswordService{private static final String EMAIL_REGEX ^[A-Za-z0-9_.-](.)$;public void resetPassword(PasswordInfo passwordInfo) {Pattern pattern Pattern.compile(EMAIL_REGEX);Matcher emailMatcher pattern.matcher(passwordInfo.getEmail());if (!emailMatcher.matches()) {System.out.println(Invalid email address.);return false;}//发送通知修改密码sendReSetPasswordNotify();}
}我们可以抽取个校验邮箱的方法出来又因为校验邮箱的功能在不同的类中因此我们可以抽个校验邮箱的工具类
public class EmailValidatorUtil {private static final String EMAIL_REGEX ^[A-Za-z0-9_.-](.)$;private static final Pattern pattern Pattern.compile(EMAIL_REGEX);public static boolean isValid(String email) {Matcher matcher pattern.matcher(email);return matcher.matches();}
}//注册的代码可以简化为这样啦
public class RegisterServiceImpl implements RegisterService{public boolean registerUser(UserInfoReq userInfo) {if (!EmailValidatorUtil.isValid(userInfo.getEmail())) {System.out.println(Invalid email address.);return false;}// 进行其他用户注册逻辑比如保存用户信息到数据库等// 返回注册结果return true;}
}3. 反射
我们日常开发中经常需要进行PO、DTO和VO的转化。所以大家经常看到类似的代码
//DTO 转VO
public UserInfoVO convert(UserInfoDTO userInfoDTO) {UserInfoVO userInfoVO new UserInfoVO();userInfoVO.setUserName(userInfoDTO.getUserName());userInfoVO.setAge(userInfoDTO.getAge());return userInfoVO;
}//PO 转DTO
public UserInfoDTO convert(UserInfoPO userInfoPO) {UserInfoDTO userInfoDTO new UserInfoDTO();userInfoDTO.setUserName(userInfoPO.getUserName());userInfoDTO.setAge(userInfoPO.getAge());return userInfoDTO;
}我们可以使用BeanUtils.copyProperties() 去除重复代码BeanUtils.copyProperties()底层就是使用了反射
public UserInfoVO convert(UserInfoDTO userInfoDTO) {UserInfoVO userInfoVO new UserInfoVO();BeanUtils.copyProperties(userInfoDTO, userInfoVO);return userInfoVO;}public UserInfoDTO convert(UserInfoPO userInfoPO) {UserInfoDTO userInfoDTO new UserInfoDTO();BeanUtils.copyProperties(userInfoPO,userInfoDTO);return userInfoDTO;}4.泛型
泛型是如何去除重复代码的呢给大家看个例子我有个转账明细和转账余额对比的业务需求有两个类似这样的方法
private void getAndUpdateBalanceResultMap(String key, MapString, ListTransferBalanceDTO compareResultListMap,
ListTransferBalanceDTO balanceDTOs) {ListTransferBalanceDTO tempList compareResultListMap.getOrDefault(key, new ArrayList());tempList.addAll(balanceDTOs);compareResultListMap.put(key, tempList);
}private void getAndUpdateDetailResultMap(String key, MapString, ListTransferDetailDTO compareResultListMap,ListTransferDetailDTO detailDTOS) {ListTransferDetailDTO tempList compareResultListMap.getOrDefault(key, new ArrayList());tempList.addAll(detailDTOS);compareResultListMap.put(key, tempList);
}这两块代码,流程功能看着很像,但是就是不能直接合并抽取一个公用方法,因为类型不一致。单纯类型不一样的话,我们可以结合泛型处理,因为泛型的本质就是参数化类型.优化为这样:
private T void getAndUpdateResultMap(String key, MapString, ListT compareResultListMap, ListT accountingDTOS) {
ListT tempList compareResultListMap.getOrDefault(key, new ArrayList());
tempList.addAll(accountingDTOS);
compareResultListMap.put(key, tempList);
}5. 继承与多态
假设你正在开发一个电子商务平台需要处理不同类型的订单例如普通订单和折扣订单。每种订单都有一些共同的属性如订单号、购买商品列表和方法如计算总价、生成订单报告但折扣订单还有特定的属性和方法。 在没有使用继承和多态的话,会写出类似这样的代码:
//普通订单
public class Order {private String orderNumber;private ListProduct products;public Order(String orderNumber, ListProduct products) {this.orderNumber orderNumber;this.products products;}public double calculateTotalPrice() {double total 0;for (Product product : products) {total product.getPrice();}return total;}public String generateOrderReport() {return Order Report for orderNumber : Total Price $ calculateTotalPrice();}
}//折扣订单
public class DiscountOrder {private String orderNumber;private ListProduct products;private double discountPercentage;public DiscountOrder(String orderNumber, ListProduct products, double discountPercentage) {this.orderNumber orderNumber;this.products products;this.discountPercentage discountPercentage;}public double calculateTotalPrice() {double total 0;for (Product product : products) {total product.getPrice();}return total - (total * discountPercentage / 100);}public String generateOrderReport() {return Order Report for orderNumber : Total Price $ calculateTotalPrice();}
}显然,看到在Order和DiscountOrder类中generateOrderReport() 方法的代码是完全相同的。calculateTotalPrice()则是有一点点区别但也大相径庭。 我们可以使用继承和多态去除重复代码,让DiscountOrder去继承Order,代码如下:
public class Order {private String orderNumber;private ListProduct products;public Order(String orderNumber, ListProduct products) {this.orderNumber orderNumber;this.products products;}public double calculateTotalPrice() {double total 0;for (Product product : products) {total product.getPrice();}return total;}public String generateOrderReport() {return Order Report for orderNumber : Total Price $ calculateTotalPrice();}
}public class DiscountOrder extends Order {private double discountPercentage;public DiscountOrder(String orderNumber, ListProduct products, double discountPercentage) {super(orderNumber, products);this.discountPercentage discountPercentage;}Overridepublic double calculateTotalPrice() {double total super.calculateTotalPrice();return total - (total * discountPercentage / 100);}
}6.使用设计模式
很多设计模式可以减少重复代码、提高代码的可读性、可扩展性.比如:
工厂模式: 通过工厂模式你可以将对象的创建和使用分开从而减少重复的创建代码。策略模式: 策略模式定义了一族算法将它们封装成独立的类并使它们可以互相替换。通过使用策略模式你可以减少在代码中重复使用相同的逻辑。模板方法模式:模板方法模式定义了一个算法的骨架将一些步骤延迟到子类中实现。这有助于避免在不同类中重复编写相似的代码。
我给大家举个例子,模板方法是如何去除重复代码的吧,业务场景:
假设你正在开发一个咖啡和茶的制作流程制作过程中的热水和添加物质的步骤是相同的但是具体的饮品制作步骤是不同的。
如果没有使用模板方法模式实现是这样的:
public class Coffee {public void prepareCoffee() {boilWater();brewCoffeeGrinds();pourInCup();addCondiments();}private void boilWater() {System.out.println(Boiling water);}private void brewCoffeeGrinds() {System.out.println(Brewing coffee grinds);}private void pourInCup() {System.out.println(Pouring into cup);}private void addCondiments() {System.out.println(Adding sugar and milk);}
}public class Tea {public void prepareTea() {boilWater();steepTeaBag();pourInCup();addLemon();}private void boilWater() {System.out.println(Boiling water);}private void steepTeaBag() {System.out.println(Steeping the tea bag);}private void pourInCup() {System.out.println(Pouring into cup);}private void addLemon() {System.out.println(Adding lemon);}
}这个代码例子,我们可以发现,烧水和倒入杯子的步骤代码,在Coffee和Tea类中是重复的。 使用模板方法模式代码可以优化成这样:
abstract class Beverage {public final void prepareBeverage() {boilWater();brew();pourInCup();addCondiments();}private void boilWater() {System.out.println(Boiling water);}abstract void brew();private void pourInCup() {System.out.println(Pouring into cup);}abstract void addCondiments();
}class Coffee extends Beverage {Overridevoid brew() {System.out.println(Brewing coffee grinds);}Overridevoid addCondiments() {System.out.println(Adding sugar and milk);}
}class Tea extends Beverage {Overridevoid brew() {System.out.println(Steeping the tea bag);}Overridevoid addCondiments() {System.out.println(Adding lemon);}
}在这个例子中我们创建了一个抽象类Beverage其中定义了制作饮品的模板方法 prepareBeverage()。这个方法包含了烧水、倒入杯子等共同的步骤而将制作过程中的特定步骤 brew() 和 addCondiments() 延迟到子类中实现。这样我们避免了在每个具体的饮品类中重复编写相同的烧水和倒入杯子的代码提高了代码的可维护性和重用性。
7.自定义注解(或者说AOP面向切面)
使用 AOP框架可以在不同地方插入通用的逻辑从而减少代码重复。 业务场景 假设你正在开发一个Web应用程序需要对不同的Controller方法进行权限检查。每个Controller方法都需要进行类似的权限验证但是重复的代码会导致代码的冗余和维护困难。
public class MyController {public void viewData() {if (!User.hasPermission(read)) {throw new SecurityException(Insufficient permission to access this resource.);}// Method implementation}public void modifyData() {if (!User.hasPermission(write)) {throw new SecurityException(Insufficient permission to access this resource.);}// Method implementation}
}你可以看到在每个需要权限校验的方法中都需要重复编写相同的权限校验逻辑,即出现了重复代码.我们使用自定义注解的方式能够将权限校验逻辑集中管理通过切面来处理,消除重复代码.如下:
Aspect
Component
public class PermissionAspect {Before(annotation(requiresPermission))public void checkPermission(RequiresPermission requiresPermission) {String permission requiresPermission.value();if (!User.hasPermission(permission)) {throw new SecurityException(Insufficient permission to access this resource.);}}
}public class MyController {RequiresPermission(read)public void viewData() {// Method implementation}RequiresPermission(write)public void modifyData() {// Method implementation}
}就这样不管多少个Controller方法需要进行权限检查你只需在方法上添加相应的注解即可。权限检查的逻辑在切面中集中管理避免了在每个Controller方法中重复编写相同的权限验证代码。这大大提高了代码的可读性、可维护性并避免了代码冗余。
8.函数式接口和Lambda表达式
业务场景: 假设你正在开发一个应用程序需要根据不同的条件来过滤一组数据。每次过滤的逻辑都可能会有些微的不同但基本的流程是相似的。 没有使用函数式接口和Lambda表达式的情况
public class DataFilter {public ListInteger filterPositiveNumbers(ListInteger numbers) {ListInteger result new ArrayList();for (Integer number : numbers) {if (number 0) {result.add(number);}}return result;}public ListInteger filterEvenNumbers(ListInteger numbers) {ListInteger result new ArrayList();for (Integer number : numbers) {if (number % 2 0) {result.add(number);}}return result;}
}在这个例子中我们有两个不同的方法来过滤一组数据但是基本的循环和条件判断逻辑是重复的,我们可以使用使用函数式接口和Lambda表达式,去除重复代码,如下:
public class DataFilter {public ListInteger filterNumbers(ListInteger numbers, PredicateInteger predicate) {ListInteger result new ArrayList();for (Integer number : numbers) {if (predicate.test(number)) {result.add(number);}}return result;}
}我们将过滤的核心逻辑抽象出来。该方法接受一个 Predicate函数式接口作为参数以便根据不同的条件来过滤数据。然后我们可以使用Lambda表达式来传递具体的条件,这样最终也达到去除重复代码的效果。