如何让代码看起来高大上(六)

背景

不同发展阶段的公司对代码质量的要求有所不同,往往在公司逐步走向壮大之后,技术中心越来越内卷,这就要求程序员不断完善代码,使其变得更灵活、更抽象!
实际情况是:程序员之间相互攀比,谁的代码越吊、越暗含一些所谓的思想;坐卧键盘间,笑谈天下事

代码优化

程序中有一段用于组合dto入参的代码,其用于请求第三方电子发票的入参,如下:

public void innerForInvoiceRsq(InvoiceTemplateDTO dto) {
        ApplyInvoiceReqDto applyInvoiceReqDto = new ApplyInvoiceReqDto();
        applyInvoiceReqDto.setStationId(dto.getStationId());
        applyInvoiceReqDto.setPayOrderNo(dto.getWebOrderId());
        applyInvoiceReqDto.setJshj(dto.getTicketPrice().add(dto.getInsuranceFee()).toPlainString());
        StringBuilder sb = new StringBuilder();
        sb.append("线路:" + dto.getLineName() + "\n");
        sb.append("时间:" + dto.getFactStartTime() + "\n");
        sb.append("票价:" + dto.getTicketPrice() + "\n");
        sb.append("乘车人:");
        switch (dto.getTicketType()) {
            case "1":
                sb.append("儿童");
                break;
            case "3":
                sb.append("携童");
                break;
            default:
                String name = StringUtils.isBlank(dto.getName()) ? "" : dto.getName();
                String cardNo = StringUtils.isBlank(dto.getCardNo()) ? "" : dto.getCardNo();
                sb.append(name + " ");
                sb.append(cardNo);
                break;
        }
        applyInvoiceReqDto.setBz(sb.toString());
        ApplyInvoiceReqDto.Item item = new ApplyInvoiceReqDto.Item();
        item.setDw("张");
        item.setXmsl("1");
        String projectName = parameterService.getParameterValueByCode(TicketConstant.H_InvoiceProjectName);
        projectName = StringUtils.isBlank(projectName) ? "客运场站服务费" : projectName;
        item.setXmmc(projectName);
        item.setXmjshj(dto.getTicketPrice().add(dto.getInsuranceFee()).toString());
        List<ApplyInvoiceReqDto.Item> items = new ArrayList<ApplyInvoiceReqDto.Item>();
        items.add(item);
        applyInvoiceReqDto.setItems(items);
    }

以上代码,处于程序员鄙视链的底端,其本身功能是没有问题了(已经上了生产),其代码总行数为33行。但是,这份代码并没有更灵活、更抽象!会被其他程序员所鄙视!所以为了让代码看起来高大上,我们开始改造。

  • 代码是构建发票请求的入参,考虑搞一个建造者,统一交给建造者去建造
  • 针对第三方的发票入参都差不多,考虑用一个模版模式,模版方法去生产
  • 代码内部有一个乘车人的信息拼装,考虑用一个策略模式,不同策略产生不同的乘车人信息

实际情况是:上面都是我乱说的,主要目的就是让这段代码看上去高大上!

一、创建目录

在这里插入图片描述

二、模版方法

**
 * InvoiceGenerator
 * InvoiceGenerator作为抽象基类,定义了通用的发票生成逻辑。它接受一个泛型参数T,表示输入信息类型,要求该类型实现InvoiceInput接口。
 * @author cll
 * @Date v. 2024/4/25 17:33
 * @Description 发票生产模版方法
 **/
@Component
public abstract class InvoiceGenerator<T extends InvoiceInput> {

    protected static final String DEFAULT_PROJECT_NAME = "客运场站服务费";
    /**
     * 策略模式,接口定义
     * 抽象方法getPassengerInfoStrategy(),由子类实现以提供具体的乘车人信息生成策略
     * */
    protected abstract PassengerInfoStrategy getPassengerInfoStrategy();
    /**
     * 根据输入信息生成内部的发票请求对象。
     *
     * @param input 输入信息对象
     * @return 应用发票请求DTO
     */
    public ApplyInvoiceReqDto generateInvoice(T input) {
        if (input == null) {
            throw new IllegalArgumentException("输入信息不能为空");
        }

        ApplyInvoiceReqDto applyInvoiceReqDto = new ApplyInvoiceReqDto();
        applyInvoiceReqDto.setType(input.getType());
        applyInvoiceReqDto.setStationId(input.getStationId());
        applyInvoiceReqDto.setPayOrderNo(input.getWebOrderId());

        // 票价和保险费相加并处理精度
        BigDecimal totalAmount = input.getTicketPrice().add(input.getInsuranceFee()).setScale(2, BigDecimal.ROUND_HALF_UP);
        applyInvoiceReqDto.setJshj(totalAmount.toPlainString());

        StringBuilder sb = new StringBuilder(100);
        sb.append("线路:" + input.getLineName() + "\n");
        sb.append("时间:" + input.getFactStartTime() + "\n");
        sb.append("票价:" + input.getTicketPrice() + "\n");

        sb.append("乘车人:");
        String passengerInfo = getPassengerInfoStrategy().generatePassengerInfo(input);
        sb.append(passengerInfo);

        applyInvoiceReqDto.setBz(sb.toString());

        // 使用建造者模式创建Item对象
        ApplyInvoiceReqDto.Item item = new ApplyInvoiceReqDto.Item()
                .setDw("张")
                .setXmsl("1")
                .setXmmc(getProjectName(input.getProjectName()))
                .setXmjshj(totalAmount.toString());

        List<ApplyInvoiceReqDto.Item> items = new ArrayList<>();
        items.add(item);
        applyInvoiceReqDto.setItems(items);

        return applyInvoiceReqDto;
    }

    private String getProjectName(String projectName) {
        return StringUtils.defaultIfBlank(projectName, DEFAULT_PROJECT_NAME);
    }
}

三、InvoiceInput接口定义

public interface InvoiceInput {
    String getStationId();
    String getWebOrderId();
    BigDecimal getTicketPrice();
    BigDecimal getInsuranceFee();
    String getLineName();
    String getFactStartTime();
    String getTicketType();
    String getName();
    String getCardNo();
    String getType();
    String getProjectName();
}

四、建造者

/**
 * SellTicketInvoiceGenerator
 *
 * @author cll
 * @Date v. 2024/4/25 17:38
 * @Description 售票发票建造者,由该建造者去创建售票发票
 **/
@Component
public class SellTicketInvoiceGenerator extends InvoiceGenerator<InvoiceTemplateDTO> {
    /**
     * 策略模式
     * 支持处理不同类型的乘车人信息,返回的对象不同,对象内部实现的方法就可以不同
     * */
    @Override
    protected PassengerInfoStrategy getPassengerInfoStrategy() {
        return new SellInvoicePassengerInfoStrategy();
    }
}

四、乘客策略接口

/**
 * PassengerInfoStrategy
 * 引入PassengerInfoStrategy接口,用于处理不同类型的乘车人信息展示
 * @author cll
 * @Date v. 2024/4/25 17:38
 * @Description
 **/
public interface PassengerInfoStrategy {
    String generatePassengerInfo(InvoiceInput input);
}

五、售票乘客策略实现类

/**
 * SellInvoicePassengerInfoStrategy
 * 售票乘客信息策略service
 * @author cll
 * @Date v. 2024/4/25 18:07
 * @Description
 **/
@Component
public class SellInvoicePassengerInfoStrategy implements PassengerInfoStrategy {

    @Override
    public String generatePassengerInfo(InvoiceInput input) {
        InvoiceTemplateDTO dto = (InvoiceTemplateDTO) input;
        switch (dto.getTicketType()) {
            case "1":
                return "儿童";
            case "3":
                return "携童";
            default:
                String name = StringUtils.defaultIfBlank(dto.getName(), "");
                String cardNo = StringUtils.defaultIfBlank(dto.getCardNo(), "");
                return name + " " + cardNo;
        }
    }
}

业务代码调用方式

上述的代码总计60行,完成了模版方法、售票发票入参建造者类、售票乘客信息策略类。那么我们的业务代码,只需要用建造者去调用模版方法即可。如下:

	@Resource
    SellTicketInvoiceGenerator sellTicketInvoiceGenerator;


  public ApplyInvoiceReqDto innerForInvoiceRsq(InvoiceTemplateDTO dto) {
       invoiceTemplateDTO.setProjectName(parameterService.getParameterValueByCode(TicketConstant.H_InvoiceProjectName));
        // 使用建造者调用模版方法,即可直接生成发票入参的请求类
        return sellTicketInvoiceGenerator.generateInvoice(invoiceTemplateDTO);
    }

代码描述

建造者 SellTicketInvoiceGenerator 是一个子类,继承父类 InvoiceGenerator,传入一个InvoiceTemplateDTO类,因此建造者具备了父类的模版方法generateInvoice(),建造者又去实现了父类的抽象方法getPassengerInfoStrategy(),在建造者的内部返回new SellInvoicePassengerInfoStrategy()对象,让这个对象去生成乘客信息。这里返回不同的对象就能获得不同的乘客信息(为策略模式)。

优点

  1. 业务service创建发票请求入参,只需要一行代码;屏蔽了创建过程;
  2. 售票建造者、退票手续费建造者,都可以通过模版方法来创建发票请求入参;
  3. 乘客信息采用策略模式,可以灵活修改和变化;

实际优点

  1. 可以在程序员同事之间装X;
  2. 对初级的程序员,增加了代码的可读难度,拔高了自身地位;
  3. 感觉自己是一个有思想有品味的程序员;
  4. 飒爽、孤傲的背影、用了海飞丝、打了个冷颤;
  • 20
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值