CH01_重构、第一个示例

这篇具有很好参考价值的文章主要介绍了CH01_重构、第一个示例。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

概述

在这一章节,作者给出了一个戏剧演出团售票的示例:剧目有悲剧(tragedy)和喜剧(comedy);为了卖出更多的票,剧团则更具观众的数量来为下次演出打折扣(大致意思是这次的观众越多,下次的票价越低)

设计

采用JSON存储数据(因为代码是用Javascript写的),plays.json 存储剧目,invoices.json存储账单

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

函数statement实现的打印账单的功能,能正常工作;但是其结构“不甚清晰”。对于这个几十行的代码,我们要读懂、理解并不困难,如果业务比较复杂、函数很长(上百行)时(笔者在维护既有项目时见过2000多行的函数,阅读代码就像是在考古)再去阅读,或者让其他人阅读,要弄清逻辑确实需要花费一番功夫。

如果没有新的需求导入,statement保持现状也可忍受!

新的需求导入

  • 要求输出部分以HTML的格式
  • 扩充新的剧目类型(如:历史剧、田园剧等)
  • ……

需求会源源不断的导入,如果每次导入一个的需求,都在statement函数中修改,随着时间的推移,statement函数将变得臃肿不堪、难以阅读和理解;这也是我们工作中经常做的(目的是懒省事儿,需求来了,在既有的函数中改改能实现功能就完事)。

分解函数statement

  1. 提取amountFor函数(for循环中的代码,注意提取参数啊Performance、play)
  2. 提取playFor函数(play参数不是for循环的变量,而是计算出来的)
  3. 修改amountFor函数,减去play参数
  4. volumeCredits累加变量提取volumeCreditsFor函数
  5. 提取format函数(格式化数据)
  6. ……

具体过程参考《重构改善既有代码设计第二版》第一章

function statement(invoice, plays) {
	let result = `Statement for ${invoice.customer}\n`;
	for (let perf of invoice.performances) {
		result += ` ${playFor(perf).name}: ${usd(amountFor(perf))} (${perf.audience} s
eats)\n`;
	}
	result += `Amount owed is ${usd(totalAmount())}\n`;
	result += `You earned ${totalVolumeCredits()} credits\n`;
	return result;

	function totalAmount() {
		let result = 0;
		for (let perf of invoice.performances) {
			result += amountFor(perf);
		}
		return result;
	}

	function totalVolumeCredits() {
		let result = 0;
		for (let perf of invoice.performances) {
			result += volumeCreditsFor(perf);
		}
		return result;
	}

	function usd(aNumber) {
		return new Intl.NumberFormat("en-US", {
				style: "currency",
				currency: "USD",
				minimumFractionDigits: 2
			})
			.format(aNumber / 100);
	}

	function volumeCreditsFor(aPerformance) {
		let result = 0;
		result += Math.max(aPerformance.audience - 30, 0);
		if ("comedy" === playFor(aPerformance)
			.type) result += Math.floor(aPerformance
			.audience / 5);
		return result;
	}

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

	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 - 20);
				}
				result += 300 * aPerformance.audience;
				break;
			default:
				throw new Error(`unknown type: ${playFor(aPerformance).type}`);
		}
		return result;
	}
}
  • 拆分计算阶段与格式化阶段
  • 分离到两个文件
  • ……

总结

一般来说,重构早期的主要动力是尝试理解代码如何工作。通常我们需要先通读代码,找到一些感觉,然后再通过重构将这些感觉从脑海里搬回到代码中。清晰的代码更容易理解,使你能够发现更深层次的设计问题,从而形成积极正向的反馈环。

所谓重构(refactoring)是这样一个过程:在不改变代码外在行为的前提下,对代码做出修改,以改进程序的内部结构。重构是一种经千锤百炼形成的有条不紊的程序整理方法,可以最大限度地减小整理过程中引入错误的概率。本质上说,重构就是在代码写好之后改进它的设计。

有了重构以后,工作的平衡点开始发生变化。设计不是在一开始完成的,而是在整个开发过程中逐渐浮现出来。在系统构筑过程中,我们需要学会如何不断改进设计。这个“构筑-设计”的反复互动,可以让一个程序在开发过程中持续保有良好的设计。文章来源地址https://www.toymoban.com/news/detail-659312.html

到了这里,关于CH01_重构、第一个示例的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • CH02_重构的原则(什么是重构、为什么重构、何时重构)

    重构(名词):对软件内部结构的一种调整,目的是在不改变软件可观察行为的前提下,提高其可理解性,降低其修改成本。 重构(动词):使用一系列重构手法,在不改变软件可观察行为的前提下,调整其结构。 重构的关键在于运用大量微小且保持软件行为的步骤,一步

    2024年02月11日
    浏览(36)
  • CH05_介绍重构名录

    每个重构手法都有5个部分。 名称(name) 要建造一个重构词汇表,名称是很重要的。 速写(sketch) 名称之后是一个简单的速写(sketch);这部分可以帮助你更快找到你所需要的重构手法。 速写部分会以代码示例的形式展示重构带来的转变。速写的用意不是解释重构的用途,

    2024年02月09日
    浏览(23)
  • [足式机器人]Part5 机械设计 Ch00/01 绪论+机器结构组成与连接 ——【课程笔记】

    本文仅供学习使用 本文参考: 《机械设计》 王德伦 马雅丽 课件与日常作业可登录网址 http://edu.bell-lab.com/manage/#/login ,选择观摩登录,查看 2023机械设计2 。 机械设计 Machines Design ,在传统课程中,更倾向于 机械零件设计 Machine Elements Design :预期 装置 (运动/结构)与 性能

    2024年02月12日
    浏览(62)
  • [足式机器人]Part4 机械设计 Ch00/01 绪论+机器结构组成与连接 ——【课程笔记】

    本文仅供学习使用 本文参考: 《机械设计》 王德伦 马雅丽 课件与日常作业可登录网址 http://edu.bell-lab.com/manage/#/login ,选择观摩登录,查看 2023机械设计2 。 机械设计 Machines Design ,在传统课程中,更倾向于 机械零件设计 Machine Elements Design :预期 装置 (运动/结构)与 性能

    2024年02月13日
    浏览(54)
  • CH06_第一组重构(上)

    曾用名:提炼函数(Extract Function) 反向重构:内联函数(115) 示例代码 动机 浏览一段代码,理解其作用,然后将其提炼到一个独立的函数中,并以这段代码的用途为这个函数命名。 何时应该把代码放进独立的函数(参考): 一个函数应该能在一屏中显示 只要被用过不止

    2024年02月09日
    浏览(30)
  • 【Java设计模式 规范与重构】 二 重构的保障:单元测试,以及如何提高代码可测试性

    其实之前的工作中强调过很多次自己做测试的重要性,例如讲单元测试的: 【C#编程最佳实践 一】单元测试实践 ,讲单元测试规范的 【阿里巴巴Java编程规范学习 四】Java质量安全规约 ,讲接口测试的: 【C#编程最佳实践 十三】接口测试实践 ,这里旧事重提就不再详细展开

    2023年04月25日
    浏览(64)
  • 【设计模式之美】重构(三)之解耦方法论:如何通过封装、抽象、模块化、中间层等解耦代码?

    重构可以分为大规模高层重构(简称“大型重构”)和小规模低层次重构(简称“小型重构”)。 通过解耦对代码重构,就是保证代码不至于复杂到无法控制的有效手段。   代码是否需要“解耦”? 看修改代码会不会牵一发而动全身。 依赖关系是否复杂 把模块与模块之间

    2024年01月16日
    浏览(54)
  • 通过OpenIddict设计一个授权服务器01-介绍

    https://dev.to/robinvanderknaap/setting-up-an-authorization-server-with-openiddict-part-i-introduction-4jid 本文是使用OpenIddict设置授权服务器系列文章的一部分。本系列中的文章将引导您完成使用OpenIddict在ASPNET Core平台上设置OAuth2+OpenID Connect授权服务器的过程。 通过设置授权服务器,可以支持基于

    2024年01月16日
    浏览(37)
  • 数据结构-如何巧妙实现一个栈?逐步解析与代码示例

    栈是一种基本但强大的数据结构,它在许多算法和系统功能中扮演着关键角色。在这篇文章中,我们将深入探讨如何在实现一个栈,从基本概念到具体的代码实现,再到实际应用场景的探讨。 在深入代码之前,先简单介绍栈的概念。栈是一个项的有序集合,其中添加(推入)

    2024年02月04日
    浏览(46)
  • 数据结构-如何实现一个队列?逐步解析与代码示例(超详细)

    在计算机科学中,队列是一种非常基础且广泛使用的数据结构。它的工作原理类似于现实生活中的排队:先来的先服务(FIFO, First-In-First-Out)。在本文中,我们将深入探讨如何在C语言中使用链表实现一个队列,并解析相关的代码实现。 队列是一种遵循先进先出原则的线性数

    2024年02月03日
    浏览(46)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包