### Get Billing Plan for Customer (JavaScript) Source: https://book-refactoring2.ifmicro.com/docs/ch10 Determines the billing plan for a customer. If the customer is unknown, it defaults to a basic plan. ```javascript const plan = isUnknown(aCustomer) ? registry.billingPlans.basic : aCustomer.billingPlan; ``` ```javascript const plan = aCustomer === "unknown" ? registry.billingPlans.basic : aCustomer.billingPlan; ``` ```javascript const plan = aCustomer.billingPlan; ``` -------------------------------- ### Clone Repository and Install Dependencies (Shell) Source: https://book-refactoring2.ifmicro.com/index This command clones the Git repository for the Book Refactoring 2 project and installs the necessary Node.js dependencies. ```shell $ git clone https://github.com/MwumLi/book-refactoring2.git $ npm i ``` -------------------------------- ### Get Customer Name (JavaScript) Source: https://book-refactoring2.ifmicro.com/docs/ch10 Retrieves the name of a customer. It handles the case where the customer is unknown, assigning a default name 'occupant'. ```javascript let customerName; if (isUnknown(aCustomer)) customerName = "occupant"; else customerName = aCustomer.name; ``` ```javascript const customerName = aCustomer.name; ``` -------------------------------- ### Example Site Data Structure (JSON) Source: https://book-refactoring2.ifmicro.com/docs/ch10 An example of a site data structure, which can be represented as a JavaScript object or JSON. It includes customer information that can be 'unknown'. ```json { name: "Acme Boston", location: "Malden MA", // more site details customer: { name: "Acme Industries", billingPlan: "plan-451", paymentHistory: { weeksDelinquentInLastYear: 7 //more }, // more } } ``` ```json { name: "Warehouse Unit 15", location: "Malden MA", // more site details customer: "unknown", } ``` -------------------------------- ### Build Static Site (Shell) Source: https://book-refactoring2.ifmicro.com/index This command builds the static website for the Book Refactoring 2 project. The output will be placed in the `_book/` directory, ready for static deployment. Ensure Node.js and gitbook are installed and dependencies are managed by `npm install`. ```shell $ npm run build ``` -------------------------------- ### JavaScript: Split Phase - Initial Order Processing Source: https://book-refactoring2.ifmicro.com/docs/ch6 Initial code for processing order string. It splits the string and calculates the order price directly. ```javascript const orderData = orderString.split(/\s+/); const productPrice = priceList[orderData[0].split("-")[1]]; const orderPrice = parseInt(orderData[1]) * productPrice; ``` -------------------------------- ### Initial Function Call Before Refactoring (JavaScript) Source: https://book-refactoring2.ifmicro.com/docs/ch11 An example of an initial function call that will be refactored, involving passing separate low and high temperature values. ```javascript const low = aRoom.daysTempRange.low; const high = aRoom.daysTempRange.high; if (!aPlan.withinRange(low, high)) alerts.push("room temperature went outside range"); ``` -------------------------------- ### Replace Parameter with Query Example (JavaScript) Source: https://book-refactoring2.ifmicro.com/docs/ch11 Demonstrates the 'Replace Parameter with Query' refactoring. It shows the transition from passing a grade as a parameter to deriving it within the function. ```javascript availableVacation(anEmployee, anEmployee.grade); function availableVacation(anEmployee, grade) { // calculate vacation... } ``` ```javascript availableVacation(anEmployee) function availableVacation(anEmployee) { const grade = anEmployee.grade; // calculate vacation... ``` -------------------------------- ### Acquire and Enrich Site Data (JavaScript) Source: https://book-refactoring2.ifmicro.com/docs/ch10 Demonstrates acquiring raw site data and then enriching it with additional properties or transformations. This is part of refactoring to handle unknown customer cases. ```javascript const rawSite = acquireSiteData(); const site = enrichSite(rawSite); const aCustomer = site.customer; // ... lots of intervening code ... let customerName; if (isUnknown(aCustomer)) customerName = "occupant"; else customerName = aCustomer.name; ``` ```javascript function enrichSite(inputSite) { return _.cloneDeep(inputSite); } ``` ```javascript const rawSite = acquireSiteData(); const site = enrichSite(rawSite); const aCustomer = site.customer; // ... lots of intervening code ... let customerName; if (isUnknown(aCustomer)) customerName = "occupant"; else customerName = aCustomer.name; ``` ```javascript function enrichSite(aSite) { const result = _.cloneDeep(aSite); const unknownCustomer = { isUnknown: true, }; if (isUnknown(result.customer)) result.customer = unknownCustomer; else result.customer.isUnknown = false; return result; } ``` ```javascript function enrichSite(aSite) { const result = _.cloneDeep(aSite); const unknownCustomer = { isUnknown: true, name: "occupant", }; if (isUnknown(result.customer)) result.customer = unknownCustomer; else result.customer.isUnknown = false; return result; } ``` -------------------------------- ### JavaScript: Calculate Taxable Charge Source: https://book-refactoring2.ifmicro.com/docs/ch6 Initial calculation of base and taxable charges from reading data. This snippet demonstrates direct calculation before refactoring. ```javascript const aReading = acquireReading(); const base = baseRate(aReading.month, aReading.year) * aReading.quantity; const taxableCharge = Math.max(0, base - taxThreshold(aReading.year)); ``` -------------------------------- ### Site Class with Unknown Customer Handling (JavaScript) Source: https://book-refactoring2.ifmicro.com/docs/ch10 Modifies the Site class to return a special unknown customer object when the internal customer is 'unknown'. This simplifies client code. ```javascript get customer() { return (this._customer === "unknown") ? createUnknownCustomer() : this._customer; } ``` -------------------------------- ### JavaScript: Using Enriched Reading Data Source: https://book-refactoring2.ifmicro.com/docs/ch6 Client code updated to use the `baseCharge` from the enriched reading object. This simplifies the client code by accessing pre-calculated values. ```javascript const rawReading = acquireReading(); const aReading = enrichReading(rawReading); const basicChargeAmount = aReading.baseCharge; ``` -------------------------------- ### Extract Function: Simple Case (JavaScript) Source: https://book-refactoring2.ifmicro.com/docs/ch6 Refactoring a function by extracting a part of its logic into a new, separate function. This example demonstrates extracting the banner printing logic. ```javascript function printOwing(invoice) { let outstanding = 0; console.log("***********************"); console.log("**** Customer Owes ****"); console.log("***********************"); // calculate outstanding for (const o of invoice.orders) { outstanding += o.amount; } // record due date const today = Clock.today; invoice.dueDate = new Date( today.getFullYear(), today.getMonth(), today.getDate() + 30 ); //print details console.log(`name: ${invoice.customer}`); console.log(`amount: ${outstanding}`); console.log(`due: ${invoice.dueDate.toLocaleDateString()}`); } ``` ```javascript function printOwing(invoice) { let outstanding = 0; printBanner(); // calculate outstanding for (const o of invoice.orders) { outstanding += o.amount; } // record due date const today = Clock.today; invoice.dueDate = new Date( today.getFullYear(), today.getMonth(), today.getDate() + 30 ); //print details console.log(`name: ${invoice.customer}`); console.log(`amount: ${outstanding}`); console.log(`due: ${invoice.dueDate.toLocaleDateString()}`); } function printBanner() { console.log("***********************"); console.log("**** Customer Owes ****"); console.log("***********************"); } ``` -------------------------------- ### Create Unknown Customer Function (JavaScript) Source: https://book-refactoring2.ifmicro.com/docs/ch10 A function to create a special case object representing an unknown customer. This object has an 'isUnknown' property set to true. ```javascript function createUnknownCustomer() { return { isUnknown: true, }; } ``` ```javascript function createUnknownCustomer() { return { isUnknown: true, name: "occupant", }; } ``` ```javascript function createUnknownCustomer() { return { isUnknown: true, name: "occupant", billingPlan: registry.billingPlans.basic, }; } ``` ```javascript function createUnknownCustomer() { return { isUnknown: true, name: "occupant", billingPlan: registry.billingPlans.basic, paymentHistory: { weeksDelinquentInLastYear: 0, }, }; } ``` -------------------------------- ### Inline Function: Simple Case Source: https://book-refactoring2.ifmicro.com/docs/ch6 Demonstrates inlining a simple function by copying its return statement directly into the calling function. This reduces the number of functions and simplifies the call site. ```javascript function rating(aDriver) { return moreThanFiveLateDeliveries(aDriver) ? 2 : 1; } function moreThanFiveLateDeliveries(aDriver) { return aDriver.numberOfLateDeliveries > 5; } ``` ```javascript function rating(aDriver) { return aDriver.numberOfLateDeliveries > 5 ? 2 : 1; } ``` -------------------------------- ### JavaScript: Refactor Base Charge Calculation Source: https://book-refactoring2.ifmicro.com/docs/ch6 Refactoring the base charge calculation into a separate function `calculateBaseCharge`. This improves modularity and readability. ```javascript const aReading = acquireReading(); const basicChargeAmount = calculateBaseCharge(aReading); function calculateBaseCharge(aReading) { return baseRate(aReading.month, aReading.year) * aReading.quantity; } ``` -------------------------------- ### Build Ebook Formats (Shell) Source: https://book-refactoring2.ifmicro.com/index This command generates ebook files in mobi, epub, and pdf formats for the Book Refactoring 2 project. Note that Calibre must be installed as it's a required software for gitbook ebook generation. ```shell $ npm run ebook ``` -------------------------------- ### Extract Function: With Local Variables as Parameters (JavaScript) Source: https://book-refactoring2.ifmicro.com/docs/ch6 Refactoring by extracting logic into a separate function, passing local variables as parameters. This example extracts the detail printing logic and passes 'invoice' and 'outstanding'. ```javascript function printOwing(invoice) { let outstanding = 0; printBanner(); // calculate outstanding for (const o of invoice.orders) { outstanding += o.amount; } // record due date const today = Clock.today; invoice.dueDate = new Date( today.getFullYear(), today.getMonth(), today.getDate() + 30 ); printDetails(invoice, outstanding); } function printDetails(invoice, outstanding) { console.log(`name: ${invoice.customer}`); console.log(`amount: ${outstanding}`); console.log(`due: ${invoice.dueDate.toLocaleDateString()}`); } ``` -------------------------------- ### Calling Temperature Range Check (JavaScript) Source: https://book-refactoring2.ifmicro.com/docs/ch11 Example of how to call the refactored `withinRange` function from a caller context, passing the room's temperature data range. ```javascript const low = aRoom.daysTempRange.low; const high = aRoom.daysTempRange.high; if (!aPlan.xxNEWwithinRange(aRoom.daysTempRange)) alerts.push("room temperature went outside range"); ``` ```javascript const tempRange = aRoom.daysTempRange; const isWithinRange = aPlan.xxNEWwithinRange(tempRange); if (!isWithinRange) alerts.push("room temperature went outside range"); ``` ```javascript if (!aPlan.withinRange(aRoom.daysTempRange)) alerts.push("room temperature went outside range"); ``` -------------------------------- ### JavaScript: Customer Data Structure Example Source: https://book-refactoring2.ifmicro.com/docs/ch7 An example of a deeply nested customer data structure, represented as a JavaScript object. This is the data being refactored. ```javascript "1920": { name: "martin", id: "1920", usages: { "2016": { "1": 50, "2": 55, // remaining months of the year }, "2015": { "1": 70, "2": 63, // remaining months of the year } } }, "38673": { name: "neal", id: "38673", // more customers in a similar form } ``` -------------------------------- ### JavaScript: Encapsulating Raw Data Access Source: https://book-refactoring2.ifmicro.com/docs/ch7 Functions to get and set the raw customer data. These act as initial wrappers around the data, preparing for further encapsulation. ```javascript function getRawDataOfCustomers() { return customerData; } function setRawDataOfCustomers(arg) { customerData = arg; } ``` -------------------------------- ### JavaScript: Incorporate Taxable Charge into Enrich Function Source: https://book-refactoring2.ifmicro.com/docs/ch6 Adding the calculation for `taxableCharge` into the `enrichReading` function. This consolidates related calculations into a single place. ```javascript function enrichReading(original) { const result = _.cloneDeep(original); result.baseCharge = calculateBaseCharge(result); result.taxableCharge = Math.max( 0, result.baseCharge - taxThreshold(result.year) ); return result; } ``` -------------------------------- ### Initial JavaScript Functions for Rendering Data Source: https://book-refactoring2.ifmicro.com/docs/ch8 This snippet shows the initial state of JavaScript functions: renderPerson, listRecentPhotos, and emitPhotoData. These functions are responsible for writing HTML content to an output stream, including person details and photo information. ```javascript function renderPerson(outStream, person) { outStream.write(`
${person.name}
\n`); renderPhoto(outStream, person.photo); emitPhotoData(outStream, person.photo); } function listRecentPhotos(outStream, photos) { photos .filter(p => p.date > recentDateCutoff()) .forEach(p => { outStream.write("title: ${photo.title}
\n`); outStream.write(`date: ${photo.date.toDateString()}
\n`); outStream.write(`location: ${photo.location}
\n`); } ``` -------------------------------- ### Generate Statement - JavaScript Source: https://book-refactoring2.ifmicro.com/docs/ch1 Generates a formatted statement for customer invoices, detailing performance costs and earned credits. It iterates through performances, calculates amounts using the 'amountFor' function, and formats the output. Dependencies include 'playFor' and 'amountFor' functions. ```javascript 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 = playFor(perf); // This line was removed in a later refactoring step 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)\n`; totalAmount += thisAmount; } result += `Amount owed is ${format(totalAmount/100)}\n`; result += `You earned ${volumeCredits} credits\n`; return result; } ``` ```javascript 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) { 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)\n`; totalAmount += thisAmount; } result += `Amount owed is ${format(totalAmount/100)}\n`; result += `You earned ${volumeCredits} credits\n`; return result; } ``` ```javascript 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) { // 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(amountFor(perf)/100)} (${perf.audience} seats)\n`; totalAmount += amountFor(perf); } result += `Amount owed is ${format(totalAmount/100)}\n`; result += `You earned ${volumeCredits} credits\n`; return result; } ``` -------------------------------- ### JavaScript After Inlining into renderPerson Source: https://book-refactoring2.ifmicro.com/docs/ch8 In this step, the Inline Function refactoring has been applied to the renderPerson function. The logic previously in emitPhotoData (now zztmp) has been moved directly into renderPerson, along with the location output. ```javascript function renderPerson(outStream, person) { outStream.write(`${person.name}
\n`); renderPhoto(outStream, person.photo); zztmp(outStream, person.photo); outStream.write(`location: ${person.photo.location}
\n`); } function listRecentPhotos(outStream, photos) { photos .filter(p => p.date > recentDateCutoff()) .forEach(p => { outStream.write("location: ${photo.location}
\n`); } function zztmp(outStream, photo) { outStream.write(`title: ${photo.title}
\n`); outStream.write(`date: ${photo.date.toDateString()}
\n`); } ``` -------------------------------- ### JavaScript: Split Phase - Refactored Order Processing Source: https://book-refactoring2.ifmicro.com/docs/ch6 Refactored order processing using separate functions `parseOrder` and `price`. This improves clarity and separates parsing logic from calculation logic. ```javascript const orderRecord = parseOrder(order); const orderPrice = price(orderRecord, priceList); function parseOrder(aString) { const values = aString.split(/\s+/); return { productID: values[0].split("-")[1], quantity: parseInt(values[1]), }; } function price(order, priceList) { return order.quantity * priceList[order.productID]; } ``` -------------------------------- ### Customer Class with isUnknown Getter (JavaScript) Source: https://book-refactoring2.ifmicro.com/docs/ch10 Defines a getter 'isUnknown' for the Customer class. This is part of refactoring to introduce a special case object for unknown customers. ```javascript get isUnknown() {return false;} ``` -------------------------------- ### Extracting Formatting Logic to renderPlainText Source: https://book-refactoring2.ifmicro.com/docs/ch1 Initial step of refactoring by extracting the printing logic into a new top-level function `renderPlainText`. This function takes invoice and plays data and formats it into a plain text statement. It includes nested helper functions for calculation and formatting. ```javascript function statement (invoice, plays) { return renderPlainText(invoice, plays); } function renderPlainText(invoice, plays) { let result = `Statement for ${invoice.customer}\n`; for (let perf of invoice.performances) { result += ` ${playFor(perf).name}: ${usd(amountFor(perf))} (${perf.audience} seats)\n`; } result += `Amount owed is ${usd(totalAmount())}\n`; result += `You earned ${totalVolumeCredits()} credits\n`; return result; function totalAmount() {...} function totalVolumeCredits() {...} function usd(aNumber) {...} function volumeCreditsFor(aPerformance) {...} function playFor(aPerformance) {...} function amountFor(aPerformance) {...}__ ``` -------------------------------- ### JavaScript After Inlining into listRecentPhotos Source: https://book-refactoring2.ifmicro.com/docs/ch8 The Inline Function refactoring has now been applied to the listRecentPhotos function. The location output is directly included within the forEach loop, incorporating the logic from the previous emitPhotoData call. ```javascript function renderPerson(outStream, person) { outStream.write(`${person.name}
\n`); renderPhoto(outStream, person.photo); zztmp(outStream, person.photo); outStream.write(`location: ${person.photo.location}
\n`); } function listRecentPhotos(outStream, photos) { photos .filter(p => p.date > recentDateCutoff()) .forEach(p => { outStream.write("location: ${p.location}
\n`); outStream.write("location: ${photo.location}
\n`); } function zztmp(outStream, photo) { outStream.write(`title: ${photo.title}
\n`); outStream.write(`date: ${photo.date.toDateString()}
\n`); } ``` -------------------------------- ### Moving Customer Data to Intermediate Structure Source: https://book-refactoring2.ifmicro.com/docs/ch1 Enhances the intermediate `statementData` object by adding the `customer` field from the invoice. This moves customer information into the data structure passed to `renderPlainText`, further decoupling formatting from the original invoice object. ```javascript function statement (invoice, plays) { const statementData = {}; statementData.customer = invoice.customer; return renderPlainText(statementData, invoice, plays); } function renderPlainText(data, invoice, plays) { let result = `Statement for ${data.customer}\n`; for (let perf of invoice.performances) { result += ` ${playFor(perf).name}: ${usd(amountFor(perf))} (${perf.audience} seats)\n`; } result += `Amount owed is ${usd(totalAmount())}\n`; result += `You earned ${totalVolumeCredits()} credits\n`; return result;__ ``` -------------------------------- ### Check if Customer is Unknown (JavaScript) Source: https://book-refactoring2.ifmicro.com/docs/ch10 A utility function to determine if a given customer argument represents an unknown customer. It checks for the string 'unknown' or an 'isUnknown' property. ```javascript function isUnknown(arg) { return arg === "unknown"; } ``` ```javascript function isUnknown(arg) { return arg.isUnknown; } ``` ```javascript function isUnknown(aCustomer) { return aCustomer === "unknown"; } ``` ```javascript function isUnknown(aCustomer) { if (aCustomer === "unknown") return true; else return aCustomer.isUnknown; } ``` -------------------------------- ### Extract voyageProfitFactor Logic into Helper (JavaScript) Source: https://book-refactoring2.ifmicro.com/docs/ch10 Refactors the 'voyageProfitFactor' getter in the 'Rating' class by extracting conditional logic related to voyage and history length into a new helper method, 'voyageAndHistoryLengthFactor'. This simplifies the main getter. ```javascript get voyageProfitFactor() { let result = 2; if (this.voyage.zone === "china") result += 1; if (this.voyage.zone === "east-indies") result += 1; if (this.voyage.zone === "china" && this.hasChinaHistory) { result += 3; if (this.history.length > 10) result += 1; if (this.voyage.length > 12) result += 1; if (this.voyage.length > 18) result -= 1; } else { if (this.history.length > 8) result += 1; if (this.voyage.length > 14) result -= 1; } return result; } get voyageAndHistoryLengthFactor() { let result = 0; if (this.voyage.zone === "china" && this.hasChinaHistory) { result += 3; if (this.history.length > 10) result += 1; if (this.voyage.length > 12) result += 1; if (this.voyage.length > 18) result -= 1; } else { if (this.history.length > 8) result += 1; if (this.voyage.length > 14) result -= 1; } return result; } ``` -------------------------------- ### Accessing Calculation Logic within renderPlainText Scope Source: https://book-refactoring2.ifmicro.com/docs/ch1 Shows how `totalAmount` and `totalVolumeCredits` functions now implicitly access the `data.performances` within their scope, as they are nested within or rely on the context where `data` is available. ```javascript function totalAmount() { let result = 0; for (let perf of data.performances) { result += amountFor(perf); } return result; } function totalVolumeCredits() { let result = 0; for (let perf of data.performances) { result += volumeCreditsFor(perf); } return result; }__ ``` -------------------------------- ### Inline Function: Parameter Name Mismatch Source: https://book-refactoring2.ifmicro.com/docs/ch6 Shows how to handle parameter name mismatches when inlining a function. The code requires minor adjustments to align variable names between the caller and the inlined function body. ```javascript function rating(aDriver) { return moreThanFiveLateDeliveries(aDriver) ? 2 : 1; } function moreThanFiveLateDeliveries(dvr) { return dvr.numberOfLateDeliveries > 5; } ``` ```javascript function rating(aDriver) { return aDriver.numberOfLateDeliveries > 5 ? 2 : 1; } ``` -------------------------------- ### Determine Weeks Delinquent for Customer (JavaScript) Source: https://book-refactoring2.ifmicro.com/docs/ch10 Calculates the number of weeks a customer has been delinquent in the last year. It handles cases where the customer information might be unknown. ```javascript const weeksDelinquent = aCustomer === "unknown" ? 0 : aCustomer.paymentHistory.weeksDelinquentInLastYear; ``` ```javascript const weeksDelinquent = isUnknown(aCustomer) ? 0 : aCustomer.paymentHistory.weeksDelinquentInLastYear; ``` ```javascript const weeksDelinquent = isUnknown(aCustomer) ? 0 : aCustomer.paymentHistory.weeksDelinquentInLastYear; ``` ```javascript const weeksDelinquent = aCustomer.paymentHistory.weeksDelinquentInLastYear; ``` ```javascript const weeksDelinquent = aCustomer === "unknown" ? 0 : aCustomer.paymentHistory.weeksDelinquentInLastYear; ``` ```javascript const weeksDelinquent = isUnknown(aCustomer) ? 0 : aCustomer.paymentHistory.weeksDelinquentInLastYear; ``` ```javascript const weeksDelinquent = isUnknown(aCustomer) ? 0 : aCustomer.paymentHistory.weeksDelinquentInLastYear; ``` ```javascript const weeksDelinquent = aCustomer.paymentHistory.weeksDelinquentInLastYear; ``` -------------------------------- ### JavaScript After Removing Obsolete Function and Renaming Source: https://book-refactoring2.ifmicro.com/docs/ch8 This final snippet shows the result of the refactoring. The obsolete emitPhotoData function has been removed, and the temporary 'zztmp' function has been renamed to emitPhotoData. The location-specific writing logic is now directly within the call sites. ```javascript function renderPerson(outStream, person) { outStream.write(`${person.name}
\n`); renderPhoto(outStream, person.photo); emitPhotoData(outStream, person.photo); outStream.write(`location: ${person.photo.location}
\n`); } function listRecentPhotos(outStream, photos) { photos .filter(p => p.date > recentDateCutoff()) .forEach(p => { outStream.write("location: ${p.location}
\n`); outStream.write("title: ${photo.title}
\n`); outStream.write(`date: ${photo.date.toDateString()}
\n`); } ``` -------------------------------- ### Introducing Intermediate Data Structure for Formatting Source: https://book-refactoring2.ifmicro.com/docs/ch1 Modifies the `statement` function to create an intermediate data structure `statementData` and pass it to `renderPlainText`. This prepares for separating calculation logic from formatting, allowing `renderPlainText` to operate solely on prepared data. ```javascript function statement (invoice, plays) { const statementData = {}; return renderPlainText(statementData, invoice, plays); } function renderPlainText(data, invoice, plays) { let result = `Statement for ${invoice.customer}\n`; for (let perf of invoice.performances) { result += ` ${playFor(perf).name}: ${usd(amountFor(perf))} (${perf.audience} seats)\n`; } result += `Amount owed is ${usd(totalAmount())}\n`; result += `You earned ${totalVolumeCredits()} credits\n`; return result; function totalAmount() {...} function totalVolumeCredits() {...} function usd(aNumber) {...} function volumeCreditsFor(aPerformance) {...} function playFor(aPerformance) {...} function amountFor(aPerformance) {...}__ ``` -------------------------------- ### JavaScript: Using Enriched Taxable Charge Source: https://book-refactoring2.ifmicro.com/docs/ch6 Client code modified to directly access the `taxableCharge` from the enriched reading object. This removes the need for separate calculation steps in the client. ```javascript const rawReading = acquireReading(); const aReading = enrichReading(rawReading); const taxableCharge = aReading.taxableCharge; ``` -------------------------------- ### Extracting Methods in JavaScript Class Source: https://book-refactoring2.ifmicro.com/docs/ch6 Refactoring a JavaScript class by extracting intermediate calculations into getter methods. This improves the class's cohesion and makes the price calculation more readable and maintainable. ```javascript class Order { constructor(aRecord) { this._data = aRecord; } get quantity() { return this._data.quantity; } get itemPrice() { return this._data.itemPrice; } get price() { return ( this.quantity * this.itemPrice - Math.max(0, this.quantity - 500) * this.itemPrice * 0.05 + Math.min(this.quantity * this.itemPrice * 0.1, 100) ); } } ``` ```javascript class Order { constructor(aRecord) { this._data = aRecord; } get quantity() { return this._data.quantity; } get itemPrice() { return this._data.itemPrice; } get price() { return this.basePrice - this.quantityDiscount + this.shipping; } get basePrice() { return this.quantity * this.itemPrice; } get quantityDiscount() { return Math.max(0, this.quantity - 500) * this.itemPrice * 0.05; } get shipping() { return Math.min(this.basePrice * 0.1, 100); } } ``` -------------------------------- ### Inline Function: Multiple Statements Source: https://book-refactoring2.ifmicro.com/docs/ch6 Illustrates inlining a function that contains multiple statements, requiring more significant adjustments than a simple return. This involves copying multiple lines of code and potentially modifying them to fit the new context. ```javascript function reportLines(aCustomer) { const lines = []; gatherCustomerData(lines, aCustomer); return lines; } function gatherCustomerData(out, aCustomer) { out.push(["name", aCustomer.name]); out.push(["location", aCustomer.location]); } ``` ```javascript function reportLines(aCustomer) { const lines = []; lines.push(["name", aCustomer.name]); gatherCustomerData(lines, aCustomer); return lines; } function gatherCustomerData(out, aCustomer) { out.push(["name", aCustomer.name]); out.push(["location", aCustomer.location]); } ``` ```javascript function reportLines(aCustomer) { const lines = []; lines.push(["name", aCustomer.name]); lines.push(["location", aCustomer.location]); return lines; } ``` -------------------------------- ### JavaScript: Enrich Reading with Base Charge Source: https://book-refactoring2.ifmicro.com/docs/ch6 Moving the `calculateBaseCharge` logic into an `enrichReading` function. This function takes original reading data and returns an enriched object with the base charge included. It uses Lodash's `cloneDeep` for copying. ```javascript function enrichReading(original) { const result = _.cloneDeep(original); result.baseCharge = calculateBaseCharge(result); return result; } ``` -------------------------------- ### Create Factory Function for Rating Variants (JavaScript) Source: https://book-refactoring2.ifmicro.com/docs/ch10 Establishes a factory function to dynamically return the appropriate Rating subclass instance based on voyage and history conditions. This centralizes instantiation logic and decouples callers from concrete subclass implementations. ```javascript function createRating(voyage, history) { if (voyage.zone === "china" && history.some(v => "china" === v.zone)) return new ExperiencedChinaRating(voyage, history); else return new Rating(voyage, history); } ``` -------------------------------- ### Override captainHistoryRisk in ExperiencedChinaRating (JavaScript) Source: https://book-refactoring2.ifmicro.com/docs/ch10 Overrides the 'captainHistoryRisk' getter in the 'ExperiencedChinaRating' subclass. It calls the superclass implementation and applies a specific reduction for China-related history, demonstrating polymorphic behavior. ```javascript get captainHistoryRisk() { const result = super.captainHistoryRisk - 2; return Math.max(result, 0); } ``` -------------------------------- ### Extract Variable: Simplify Complex Expression Source: https://book-refactoring2.ifmicro.com/docs/ch6 Demonstrates the 'Extract Variable' refactoring technique to simplify a complex expression. By assigning parts of the expression to named variables, the code becomes more readable and maintainable. ```javascript return ( order.quantity * order.itemPrice - Math.max(0, order.quantity - 500) * order.itemPrice * 0.05 + Math.min(order.quantity * order.itemPrice * 0.1, 100) ); ``` ```javascript const basePrice = order.quantity * order.itemPrice; const quantityDiscount = Math.max(0, order.quantity - 500) * order.itemPrice * 0.05; const shipping = Math.min(basePrice * 0.1, 100); return basePrice - quantityDiscount + shipping; ``` -------------------------------- ### Enriching Performance Data with Play Information Source: https://book-refactoring2.ifmicro.com/docs/ch1 Modifies the `statement` function to enrich each performance object with play details using `map` and an `enrichPerformance` helper function. This prepares the data for accessing play names directly from the performance data structure. ```javascript function statement (invoice, plays) { const statementData = {}; statementData.customer = invoice.customer; statementData.performances = invoice.performances.map(enrichPerformance); return renderPlainText(statementData, plays); function enrichPerformance(aPerformance) { const result = Object.assign({}, aPerformance); return result; } }__ ``` -------------------------------- ### Moving Performances Data to Intermediate Structure Source: https://book-refactoring2.ifmicro.com/docs/ch1 Moves the `performances` array from the invoice object to the `statementData` structure. This change allows the removal of the `invoice` parameter from `renderPlainText`, simplifying its signature and focusing it on the prepared data. ```javascript function statement (invoice, plays) { const statementData = {}; statementData.customer = invoice.customer; statementData.performances = invoice.performances; return renderPlainText(statementData, plays); } function renderPlainText(data, plays) { let result = `Statement for ${data.customer}\n`; for (let perf of data.performances) { result += ` ${playFor(perf).name}: ${usd(amountFor(perf))} (${perf.audience} seats)\n`; } result += `Amount owed is ${usd(totalAmount())}\n`; result += `You earned ${totalVolumeCredits()} credits\n`; return result;__ ``` -------------------------------- ### Refactoring JavaScript: Using map() to Split Lines into Arrays Source: https://book-refactoring2.ifmicro.com/docs/ch8 This refactoring step uses the `map()` array method to transform each non-empty line into an array of its fields by splitting it at the comma. This prepares the data for further filtering and transformation. ```javascript function acquireData(input) { const lines = input.split("\n"); const result = []; const loopItems = lines .slice(1) .filter(line => line.trim() !== "") .map(line => line.split(",")) ; for (const line of loopItems) { const record = line;.split(","); if (record[1].trim() === "India") { result.push({city: record[0].trim(), phone: record[2].trim()}); } } return result; } ``` -------------------------------- ### Refactor voyageAndHistoryLengthFactor in Rating (JavaScript) Source: https://book-refactoring2.ifmicro.com/docs/ch10 Further refactors the 'voyageAndHistoryLengthFactor' in the 'Rating' class by extracting the history length calculation into 'historyLengthFactor'. This isolates specific pieces of logic for better readability and potential reuse. ```javascript get voyageAndHistoryLengthFactor() { let result = 0; result += this.historyLengthFactor; if (this.voyage.length > 14) result -= 1; return result; } get historyLengthFactor() { return (this.history.length > 8) ? 1 : 0; } ``` -------------------------------- ### Refactoring JavaScript: Using map() for Output Formatting Source: https://book-refactoring2.ifmicro.com/docs/ch8 The final `map()` operation transforms the filtered records into the desired output format: an object containing the city and phone number. This completes the data transformation pipeline. ```javascript function acquireData(input) { const lines = input.split("\n"); const result = []; const loopItems = lines .slice(1) .filter(line => line.trim() !== "") .map(line => line.split(",")) .filter(record => record[1].trim() === "India") .map(record => ({city: record[0].trim(), phone: record[2].trim()})) ; for (const line of loopItems) { const record = line; result.push(line); } return result; } ``` -------------------------------- ### Refactor historyLengthFactor in ExperiencedChinaRating (JavaScript) Source: https://book-refactoring2.ifmicro.com/docs/ch10 Extracts and refactors the history length calculation into 'historyLengthFactor' within the 'ExperiencedChinaRating' subclass. This mirrors the refactoring in the base class and adapts the logic for the specific subclass conditions. ```javascript get historyLengthFactor() { return (this.history.length > 10) ? 1 : 0; } ``` -------------------------------- ### Replace Nested Conditional with Guard Clauses (JavaScript) Source: https://book-refactoring2.ifmicro.com/docs/ch10 Demonstrates replacing nested if-else statements with a series of guard clauses (early returns). This improves readability by handling exceptional cases first and reducing indentation. Assumes boolean flags like 'isDead', 'isSeparated', 'isRetired' and corresponding functions to calculate amounts. ```javascript function getPayAmount() { if (isDead) return deadAmount(); if (isSeparated) return separatedAmount(); if (isRetired) return retiredAmount(); return normalPayAmount(); } ``` -------------------------------- ### JavaScript After Extract Method Refactoring Source: https://book-refactoring2.ifmicro.com/docs/ch8 Following the Extract Method refactoring, the emitPhotoData function has been modified. A new temporary function 'zztmp' has been created to hold parts of the original emitPhotoData logic, preparing for subsequent inlining. ```javascript function renderPerson(outStream, person) { outStream.write(`${person.name}
\n`); renderPhoto(outStream, person.photo); emitPhotoData(outStream, person.photo); } function listRecentPhotos(outStream, photos) { photos .filter(p => p.date > recentDateCutoff()) .forEach(p => { outStream.write("location: ${photo.location}
\n`); } function zztmp(outStream, photo) { outStream.write(`title: ${photo.title}
\n`); outStream.write(`date: ${photo.date.toDateString()}
\n`); } ``` -------------------------------- ### Refactoring JavaScript: Using filter() for Country Selection Source: https://book-refactoring2.ifmicro.com/docs/ch8 Another `filter()` operation is added to the pipeline to select only those records where the country field (index 1) is 'India'. This progressively narrows down the dataset to the desired entries. ```javascript function acquireData(input) { const lines = input.split("\n"); const result = []; const loopItems = lines .slice(1) .filter(line => line.trim() !== "") .map(line => line.split(",")) .filter(record => record[1].trim() === "India") ; for (const line of loopItems) { const record = line; if (record[1].trim() === "India") { result.push({city: record[0].trim(), phone: record[2].trim()}); } } return result; } ``` -------------------------------- ### Integrating Play Data into Performance Objects Source: https://book-refactoring2.ifmicro.com/docs/ch1 Updates the `enrichPerformance` function to include the `play` object by calling `playFor`. It also refactors the `playFor` function to directly use the `plays` data available in the scope, streamlining play data retrieval. ```javascript function enrichPerformance(aPerformance) { const result = Object.assign({}, aPerformance); result.play = playFor(result); return result; } function playFor(aPerformance) { return plays[aPerformance.playID]; }__ ``` -------------------------------- ### Refactor Employee Payment Calculation with Guard Clauses (JavaScript) Source: https://book-refactoring2.ifmicro.com/docs/ch10 Shows step-by-step refactoring of an employee payment calculation function. It replaces nested conditionals for 'isSeparated' and 'isRetired' status with guard clauses, simplifying the main logic flow and removing an unnecessary 'result' variable. ```javascript function payAmount(employee) { let result; if(employee.isSeparated) { result = {amount: 0, reasonCode:"SEP"}; } else { if (employee.isRetired) { result = {amount: 0, reasonCode: "RET"}; } else { // logic to compute amount lorem.ipsum(dolor.sitAmet);1 consectetur(adipiscing).elit(); sed.do.eiusmod = tempor.incididunt.ut(labore) && dolore(magna.aliqua); ut.enim.ad(minim.veniam); result = someFinalComputation(); } } return result; } ``` ```javascript function payAmount(employee) { let result; if (employee.isSeparated) return {amount: 0, reasonCode: "SEP"}; if (employee.isRetired) { result = {amount: 0, reasonCode: "RET"}; } else { // logic to compute amount lorem.ipsum(dolor.sitAmet); consectetur(adipiscing).elit(); sed.do.eiusmod = tempor.incididunt.ut(labore) && dolore(magna.aliqua); ut.enim.ad(minim.veniam); result = someFinalComputation(); } return result; } ``` ```javascript function payAmount(employee) { let result; if (employee.isSeparated) return {amount: 0, reasonCode: "SEP"}; if (employee.isRetired) return {amount: 0, reasonCode: "RET"}; // logic to compute amount lorem.ipsum(dolor.sitAmet); consectetur(adipiscing).elit(); sed.do.eiusmod = tempor.incididunt.ut(labore) && dolore(magna.aliqua); ut.enim.ad(minim.veniam); result = someFinalComputation(); return result; } ``` ```javascript function payAmount(employee) { let result; if (employee.isSeparated) return {amount: 0, reasonCode: "SEP"}; if (employee.isRetired) return {amount: 0, reasonCode: "RET"}; // logic to compute amount lorem.ipsum(dolor.sitAmet); consectetur(adipiscing).elit(); sed.do.eiusmod = tempor.incididunt.ut(labore) && dolore(magna.aliqua); ut.enim.ad(minim.veniam); return someFinalComputation(); } ``` -------------------------------- ### JavaScript Original Function for Insurance Application Scoring Source: https://book-refactoring2.ifmicro.com/docs/ch11 This JavaScript function calculates a score for an insurance application based on candidate, medical examination, and scoring guide information. It's an example of a function that might be refactored into a command object if it becomes too complex. ```javascript function score(candidate, medicalExam, scoringGuide) { let result = 0; let healthLevel = 0; let highMedicalRiskFlag = false; if (medicalExam.isSmoker) { healthLevel += 10; highMedicalRiskFlag = true; } let certificationGrade = "regular"; if (scoringGuide.stateWithLowCertification(candidate.originState)) { certificationGrade = "low"; result -= 5; } // lots more code like this result -= Math.max(healthLevel - 5, 0); return result; } ```