优化重复冗余代码的8种方式


前言

日常开发中,我们经常会遇到一些重复代码。大家都知道重复代码不好,它主要有这些缺点:可维护性差、可读性差、增加错误风险等等。这里给大家讲讲优化重复代码的几种方式。


1、抽取公用方法

抽取公用方法,是最常用的代码去重方法~

比如这个例子,分别遍历names列表,然后各自转化为大写和小写打印出来:

public class TianLuoExample {

    public static void main(String[] args) {
        List<String> 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(List<String> names, Function<String, 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) {
        List<String> 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, Map<String, List<TransferBalanceDTO>> compareResultListMap,
List<TransferBalanceDTO> balanceDTOs) {
    List<TransferBalanceDTO> tempList = compareResultListMap.getOrDefault(key, new ArrayList<>());
    tempList.addAll(balanceDTOs);
    compareResultListMap.put(key, tempList);
}

private void getAndUpdateDetailResultMap(String key, Map<String, List<TransferDetailDTO>> compareResultListMap,
                                          List<TransferDetailDTO> detailDTOS) {
    List<TransferDetailDTO> tempList = compareResultListMap.getOrDefault(key, new ArrayList<>());
    tempList.addAll(detailDTOS);
    compareResultListMap.put(key, tempList);
}

这两块代码,流程功能看着很像,但是就是不能直接合并抽取一个公用方法,因为类型不一致。单纯类型不一样的话,我们可以结合泛型处理,因为泛型的本质就是参数化类型.优化为这样:

private <T> void getAndUpdateResultMap(String key, Map<String, List<T>> compareResultListMap, List<T> accountingDTOS) {
List<T> tempList = compareResultListMap.getOrDefault(key, new ArrayList<>());
tempList.addAll(accountingDTOS);
compareResultListMap.put(key, tempList);
}

5、继承与多态

假设你正在开发一个电子商务平台,需要处理不同类型的订单,例如普通订单和折扣订单。每种订单都有一些共同的属性(如订单号、购买商品列表)和方法(如计算总价、生成订单报告),但折扣订单还有特定的属性和方法

在没有使用继承和多态的话,会写出类似这样的代码:

//普通订单
public class Order {
    private String orderNumber;
    private List<Product> products;

    public Order(String orderNumber, List<Product> 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 List<Product> products;
    private double discountPercentage;

    public DiscountOrder(String orderNumber, List<Product> 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();
    }
}

显然,看到在OrderDiscountOrder类中,generateOrderReport() 方法的代码是完全相同的。calculateTotalPrice()则是有一点点区别,但也大相径庭。

我们可以使用继承和多态去除重复代码,让DiscountOrder去继承Order,代码如下:

public class Order {
    private String orderNumber;
    private List<Product> products;

    public Order(String orderNumber, List<Product> 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, List<Product> products, double discountPercentage) {
        super(orderNumber, products);
        this.discountPercentage = discountPercentage;
    }

    @Override
    public 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");
    }
}

这个代码例子,我们可以发现,烧水和倒入杯子的步骤代码,在CoffeeTea类中是重复的。

使用模板方法模式,代码可以优化成这样:

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 {
    @Override
    void brew() {
        System.out.println("Brewing coffee grinds");
    }

    @Override
    void addCondiments() {
        System.out.println("Adding sugar and milk");
    }
}

class Tea extends Beverage {
    @Override
    void brew() {
        System.out.println("Steeping the tea bag");
    }

    @Override
    void 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 List<Integer> filterPositiveNumbers(List<Integer> numbers) {
        List<Integer> result = new ArrayList<>();
        for (Integer number : numbers) {
            if (number > 0) {
                result.add(number);
            }
        }
        return result;
    }

    public List<Integer> filterEvenNumbers(List<Integer> numbers) {
        List<Integer> result = new ArrayList<>();
        for (Integer number : numbers) {
            if (number % 2 == 0) {
                result.add(number);
            }
        }
        return result;
    }
}

在这个例子中,我们有两个不同的方法来过滤一组数据,但是基本的循环和条件判断逻辑是重复的,我们可以使用使用函数式接口和Lambda表达式,去除重复代码,如下:

public class DataFilter {
    public List<Integer> filterNumbers(List<Integer> numbers, Predicate<Integer> predicate) {
        List<Integer> result = new ArrayList<>();
        for (Integer number : numbers) {
            if (predicate.test(number)) {
                result.add(number);
            }
        }
        return result;
    }
}

我们将过滤的核心逻辑抽象出来。该方法接受一个 Predicate函数式接口作为参数,以便根据不同的条件来过滤数据。然后,我们可以使用Lambda表达式来传递具体的条件,这样最终也达到去除重复代码的效果啦.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
统名称:仿263、58分类信息系统,ZHEW-CMS-MSSQL-v1.8正式商业版 运行环境: ASP+MSSQL 系统运行在 WINDOWS 2000/XP/2003 操作系统 需要 IIS5.x或IIS6.x WEB服务器 + MSSQL 2000/2005 数据库 授权方式: 商业收费 软件大小: 34MB 程序作者: SUPERBING 官方网站: http://www.zhew.com 程序演示: 仿263演示: http://263.zhew.com 仿58演示:http://58.zhew.com ZHEW-CMS-MSSQL-v1.8正式商业版功能 最新几大功能: 采用ASP+MSSQL,再大的数据量也能满足,也能正常运转 可生成HMTL静态页面,减轻了服务器的负担,发布信息后自行生成 整合论坛,实现一站式登陆和注销和注册 可做全国分类大型分类信息网站,包含全国各地城市,可在后台自由添加 重新整理代码代码经过多重优化,去掉冗余重复,优惠后速度一流,并最新增减了防SQL注入功能,让你的网站更安全,去除漏洞 新系统可扩展性强,方便二次开发,集中标签和模板 界面简洁美观大方,模仿了大网站的优点 最新添加了竞价、推荐信息等功能,来确定信息的排名先后来收费,更加商业化 本系统还具有VIP会员和商家黄页功能,可在后台设定消费积分,有在线充值接口,并且管理员可在后台对会员积分进行修改操作 信息栏目可做 N级分类,一级栏目可增设二级栏目。。。可扩展性很强 新系统增加了模板功能和标签功能,非常方便了后台编辑,真正实现了傻瓜式修改和布局,也给专业人员留了二次开发的接口 本系统具有分站管理的功能,可单独设立分站站长,单独分配密码,单独设定分站LOGO,联系信息、分站公告等,极大自主性和人性化 另外本站还对发布信息增加了地图标注功能,MAPBAR正式链接,方便访客查看地址位置 新增来访IP限制功能,可设置IP号码段,也可对管理员设置IP登陆功能,防止非法登陆管理,又增加了一层保护 后台可设置信息内容过滤字符,包含各不良的文字的信息一概发不出去 审核功能,后台可设置是否审核信息,增加的信息的可信度和安全度 本站有强大的公告在线管理功能,可生成js广告,更大的调用功能,在线上传图片 更强大的功能待升级中体现。。。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值