【重构】一、示例代码

一、需求

为戏剧演出团实现一套剧目表演收费逻辑,输出收费单
费用计算:根据观众人数、剧目类型收费,同时减去相应的观众量积分
观众量积分:根据到场观众人数给出,客户用户下次付款时抵扣

二、改造前代码

存在问题:
1、 代码组织不甚清晰
2、 如果打印的结果展现形式有变,如使用HTML 格式输出账单,则 返回结果 result 需要做多个分支逻辑,或者复制多一份代码,用以返回html 格式的result
3、 如果剧目类型增加,则戏剧场次的计费方式、积分计算方式不同,代码会变得更加冗长

/*
* @Name: Index
* @Description: 打印账单详情, 根据观众人数和剧目类型收费, 发出账单时还会根据观众人数给出客户积分
* @Copyright: 广州银云信息科技有限公司
* @LastEditors: lisp
* @LastEditTime: 2022-06-03 14:07:27
*/
const plays = require("./data/plays")
const invoice = require("./data/invoice")

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) \n`
    totalAmount += thisAmount
  }
  result += `Amount owed is ${format(totalAmount / 100)} \n`
  result += `You earned ${volumeCredits} credits\n`
  console.log(result)
  
  return result
}

statement(invoice, plays)
/* 
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
*/


三、改造后代码

把主要计算逻辑封装在createStatementData.js 文件里

主要解决问题

  1. 抽取与主函数业务逻辑关联性不强的代码为独立函数,使主函数结构清晰明了
  2. 独立函数严格遵循单一功能原则,一个函数只做一件事
  3. 把计算剧目表演相关的逻辑封装成类,建立一套继承体系,类的多态功能代替多重计算分支,由子类来实现主要主要计算金额、积分功能,如若需增加剧目类型,只需增加子类即可

输出打印单–改造后–statement.js

/*
* @Name:
* @Description:
* @Copyright: 广州银云信息科技有限公司
* @LastEditors: lisp
* @LastEditTime: 2022-06-03 17:41:31
*/

module.exports = createStatementData = (invoice, plays) => {
	const result = {}
	result.customer = invoice.customer
	result.performances = invoice.performances.map(enrichPerformance)
	result.totalAmount = calcAmountTotal(result)
	result.totalVolumeCredits = calcVolumeCreditsTotal(result)
	return result
	
	function enrichPerformance(aPerformance) {
		const calculator = createPerformanceCalculator(
			aPerformance,
			playFor(aPerformance)
		)
		const result = Object.assign({}, aPerformance)
		result.play = calculator.play
		result.amount = calculator.amount
		result.volumeCredits = calculator.volumeCredits
		return result
	}
	
	function playFor(aPerformance) {
		return plays[aPerformance.playID]
	}
	
	function calcAmountTotal(data) {
		return data.performances.reduce((total, p) => total + p.amount, 0)
	}
	function calcVolumeCreditsTotal(data) {
		return data.performances.reduce((total, p) => total + p.volumeCredits, 0)
	}
	
	function createPerformanceCalculator(aPerformance, aPlay) {
		switch (aPlay.type) {
			case "tragedy":
				return new TragedyCalculator(aPerformance, aPlay)
			case "comedy":
				return new ComedyCalculator(aPerformance, aPlay)
			default:
				throw new Error(`unknown type: ${aPlay.type}`)
		}
	}
}
class PerformanceCalculator {
	constructor(aPerformance, aPlay) {
		this.performance = aPerformance
		this.play = aPlay
	}
	get amount() {
		return new Error("subclass responsibility")
	}
	get volumeCredits() {
		return Math.max(this.performance.audience - 30, 0)
	}
}
class TragedyCalculator extends PerformanceCalculator {
	get amount() {
		let result = 40000
		if (this.performance.audience > 30) {
			result += 1000 * (this.performance.audience - 30)
		}
		return result
	}
}

class ComedyCalculator extends PerformanceCalculator {
	get amount() {
		let result = 40000
		result = 30000
		if (this.performance.audience > 20) {
			result += 10000 + 500 * (this.performance.audience - 20)
		}
		result += 300 * this.performance.audience
		return result
	}
	get volumeCredits() {
		return super.volumeCredits + Math.floor(this.performance.audience / 5)
	}
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值