重构 改善既有代码的设计 第二版 - A First Example

本文通过重构一个戏剧公司账单计算的代码,逐步展示了如何分解函数、消除冗余变量、提取方法,以及利用多态性来组织计算逻辑。通过将代码重构为更小、更具表达性的函数和类,提高了代码的可读性和可维护性。
摘要由CSDN通过智能技术生成

THE STARTING POINT

一家戏剧公司,演员会参加表演。戏剧分为喜剧和悲剧。公司根据观众的规模和戏剧的类型收费。除了账单,还有volume credits,未来的折扣。
plays.json的数据是这样的:

{
   
    "hamlet": {
   "name": "Hamlet", "type": "tragedy"},
    "as­like": {
   "name": "As You Like It", "type": "comedy"},
    "othello": {
   "name": "Othello", "type": "tragedy"}
}

账单,invoices.json文件是这样的:

[
    {
   
        "customer": "BigCo",
        "performances": [
            {
   
                "playID": "hamlet",
                "audience": 55
            },
            {
   
                "playID": "as­like",
                "audience": 35
            },
            {
   
                "playID": "othello",
                "audience": 40
            }
        ]
    }
]

打印账单的函数:

function statement (invoice, plays) {
   
    let totalAmount = 0;
    let volumeCredits = 0;
    let result = `Statement for ${
     invoice.customer}\n`;
    const format = new Intl.NumberFormat("en­US",
            {
    style: "currency", currency: "USD",
            minimumFractionDigits: 2 }).format;
            
    for (let perf of invoice.performances) {
   
        const play = plays[perf.playID];
        let thisAmount = 0;
        switch (play.type) {
   
            case "tragedy":
                thisAmount = 40000;
                if (perf.audience > 30) {
   
                    thisAmount += 1000 * (perf.audience ­ 30);
                }
                break;
            case "comedy":
                thisAmount = 30000;
                if (perf.audience > 20) {
   
                    thisAmount += 10000 + 500 * (perf.audience ­ 20);
                }
                thisAmount += 300 * perf.audience;
                break;
            default:
                throw new Error(`unknown type: ${
     play.type}`);
        }
        
        // add volume credits
        volumeCredits += Math.max(perf.audience ­ 30, 0);
        // add extra credit for every ten comedy attendees
        if ("comedy" === play.type) volumeCredits += Math.floor(perf.audience / 5);
        // print line for this order
        result += ` ${
     play.name}: ${
     format(thisAmount/100)} (${
     perf.audience} seats);
        totalAmount += thisAmount;
    }
    result += `Amount owed is ${
   format(totalAmount/100)}\n`;
    result += `You earned ${
   volumeCredits} credits\n`;
    return result;
}

输出是这样的:

Statement for BigCo
    Hamlet: $650.00 (55 seats)
    As You Like It: $580.00 (35 seats)
    Othello: $500.00 (40 seats)
Amount owed is $1,730.00
You earned 47 credits

DECOMPOSING THE STATEMENT FUNCTION

先从switch开始重构(Extract Function),其中,把thisAmount重命名为result,把perf重命名为aPerformance。

function amountFor(aPerformance, play) {
   
    let result = 0;
    switch (play.type) {
   
        case "tragedy":
            result = 40000;
            if (aPerformance.audience > 30) {
   
                result += 1000 * (aPerformance.audience ­ 30);
            }
            break;
        case "comedy":
            result = 30000;
            if (aPerformance.audience > 20) {
   
                result += 10000 + 500 * (aPerformance.audience ­ 20);
            }
            result += 300 * aPerformance.audience;
            break;
        default:
            throw new Error(`unknown type: ${
     play.type}`);
    }
    return result;
}

这样,statement修改为:

let thisAmount = amountFor(perf, play);

Removing the play Variable

play可以在amountFor内重新计算。
重构长函数的时候,喜欢摆脱像play这样的变量。因为临时变量会创建很多本地范围内的名称,这些名称使得提取变得负责。这叫Replace Temp with Query。

function playFor(aPerformance) {
   
    return plays[aPerformance.playID];
}

statement修改成:

const play = playFor(perf);

然后,去掉play变量:

for (let perf of invoice.performances) {
   

    let thisAmount = amountFor(perf, playFor(perf));
    // add volume credits
    volumeCredits += Math.max(perf.audience ­ 30, 0);
    // add extra credit for every ten comedy attendees
    if ("comedy" === playFor(perf).type) volumeCredits += Math.floor(perf.audience / 5);
    // print line for this order
    result += ` ${
   playFor(perf).name}: ${
   format(thisAmount/100)} (${
   perf.audience} seats);
    totalAmount += thisAmount;
}

然后可以Change Function Declaration,删除play参数:

function amountFor(aPerformance) {
   
    let result = 0;
    switch (playFor(aPerformance).type) {
   
        case "tragedy":
            result = 40000;
            if (aPerformance.audience > 30) {
   
                result += 1000 * (aPerformance.audience ­ 30);
            }
            break;
        case "comedy":
            result = 30000;
            if (aPerformance.audience > 20) {
   
                result += 10000 + 500 * (aPerformance.audience
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值