\documentclass[10pt]{article} \title{Refactoring\\*2020} \author{Wouter} \usepackage{amsmath} \usepackage{graphicx} \usepackage[top=25mm, bottom=25mm, left=25mm, right=25mm]{geometry} \usepackage{color} \usepackage{multicol} \usepackage{tikz} \usepackage{listings} \lstset{ escapeinside={<@}{@>}, frame = single, framexleftmargin=-15pt,} \usepackage{hyperref} \usepackage{pdfpages} \definecolor{gray}{RGB}{145,145,145} \definecolor{brown}{RGB}{139,69,19} \definecolor{blue}{RGB}{0,0,255} \definecolor{green}{RGB}{0,100,0} \definecolor{red}{RGB}{255,0,0} \definecolor{purple}{RGB}{128,0,128} \hypersetup{colorlinks=true,linkcolor=black,filecolor=magenta,urlcolor=blue,} \newcommand{\wordt}{$$\tikz[baseline] \draw [<-, line width=2.0pt, black] (0,0) -- ++(0,3.0ex);$$} \newcommand{\link}[1]{\hyperref[sec:#1]{#1}} \begin{document} \maketitle \tableofcontents \newpage Gebaseerd op het gelijknamige boek van Martin Fowler \& Kent Beck. Het boek is best een aanrader om te lezen, vooral om na de studies te gebruiken. De verandering zijn meestal vrij simpel en worden dan ook in combinatie gebruikt. \section{Notes} \subsection{Why refactor?} To save time in the long run. Nobody wants to bugfix spaghetti. Mostly to make the code more readable, or encourage reusability. \subsection{Tests} Test everything, all the time. Catch bugs early, fix them while they are small. \begin{enumerate} \item Refactor something \item Run Tests \item Repeat. \end{enumerate} \section{Code smells} Basically: when should you refactor. Best to do it while coding, after adding a new feature, this keeps the refactors small and bug free. No need to refactor passive code, only refactor actively developing code. \begin{itemize} \item Mysterious name: Naming should be mundane and clear. \\ Fixes: \begin{itemize} \item \link{Change Function Declaration} \item \link{Rename Variable} \item \link{Rename Field} \end{itemize} \item Duplicated Code: Keeping multiple identical code up to date is a pain. \\ Fixes: \begin{itemize} \item \link{Extract Function} \item \link{Slide Statements} \item \link{Pull Up Method} \end{itemize} \item Long Function: Shorter is better. \\ Fixes: \begin{itemize} \item \link{Extract Function} \item \link{Replace Temp with Query} \item \link{Introduce Parameter Object} \item \link{Preserve Whole Object} \item \link{Replace Function with Command} \item \link{Decompose Conditional} \item \link{Replace Conditional with Polymorphism} \item \link{Split Loop} \end{itemize} \item Long Parameter List: Long parameter lists are often confusing. \\ Fixes: \begin{itemize} \item \link{Replace Parameter with Query} \item \link{Preserve Whole Object} \item \link{Introduce Parameter Object} \item \link{Remove Flag Argument} \item \link{Combine Functions into Class} \end{itemize} \item Global Data: Hellspawn. \\ Fixes: \begin{itemize} \item \link{Encapsulate Variable} \end{itemize} \item Mutable Data: Changes to data can often lead to unexpected consequences and tricky bugs. Control who changes the data and who uses it. \\ Fixes: \begin{itemize} \item \link{Encapsulate Variable} \item \link{Split Variable} \item \link{Slide Statements} \item \link{Extract Function} \item \link{Separate Query from Modifier} \item \link{Remove Setting Method} \item \link{Replace Derived Variable with Query} \item \link{Combine Functions into Class} \item \link{Combine Functions into Transform} \item \link{Change Reference to Value} \end{itemize} \item Divergent Change: When we make a change, we want to be able to jump to a single clear point in the system and make the change. \\ Fixes: \begin{itemize} \item \link{Split Phase} \item \link{Move Function} \item \link{Extract Function} \item \link{Extract Class} \end{itemize} \item Shotgun Surgery: Opposite of Divergent Change. When making a change and you have to make lots of tiny edits all over the place. \\ Fixes: \begin{itemize} \item \link{Move Function} \item \link{Move Field} \item \link{Combine Functions into Class} \item \link{Combine Functions into Transform} \item \link{Split Phase} \item \link{Inline Function} \item \link{Inline Class} \end{itemize} \item Feature Envy: When a function in one module spends more time communicating with functions or data inside another module than it does within its own module. \\ Fixes: \begin{itemize} \item \link{Move Function} \item \link{Extract Function} \end{itemize} \item Data Clumps: Groups of data items that pop up together all the time. \\ Fixes: \begin{itemize} \item \link{Extract Class} \item \link{Introduce Parameter Object} \item \link{Preserve Whole Object} \end{itemize} \item Primitive Obsession: Avoiding making your own types by butchering primitives. \\ Fixes: \begin{itemize} \item \link{Replace Primitive with Object} \item \link{Replace Type Code with Subclasses} \item \link{Replace Conditional with Polymorphism} \item \link{Extract Class} \item \link{Introduce Parameter Object} \end{itemize} \item Repeated Switches: The same conditional switching logic pops up in different places. \\ Fixes: \begin{itemize} \item \link{Replace Conditional with Polymorphism} \end{itemize} \item Loops: Pipeline operations help see the elements that are included in the processing and what is done with them. \\ Fixes: \begin{itemize} \item \link{Replace Loop with Pipeline} \end{itemize} \item Lazy Element: Removing unneeded structures. \\ Fixes: \begin{itemize} \item \link{Inline Function} \item \link{Inline Class} \item \link{Collapse Hierarchy} \end{itemize} \item Speculative Generality: Supporting dreams. "We'll add this ability one day." \\ Fixes: \begin{itemize} \item \link{Collapse Hierarchy} \item \link{Inline Function} \item \link{Inline Class} \item \link{Change Function Declaration} \item \link{Remove Dead Code} \end{itemize} \item Temporary Field: A field used only in certain circumstances. \\ Fixes: \begin{itemize} \item \link{Extract Class} \item \link{Move Function} \item \link{Introduce Special Case} \end{itemize} \item Message Chains: An object asks an object asks an object \dots for another object. \\ Fixes: \begin{itemize} \item \link{Hide Delegate} \item \link{Extract Function} \item \link{Move Function} \end{itemize} \item Middle Man: Too many methods in an interface delegate to another class. \\ Fixes: \begin{itemize} \item \link{Remove Middle Man} \item \link{Inline Function} \item \link{Replace Superclass with Delegate} \item \link{Replace Subclass with Delegate} \end{itemize} \item Insider Trading: Too much trading of data. \\ Fixes: \begin{itemize} \item \link{Move Function} \item \link{Move Field} \item \link{Hide Delegate} \item \link{Replace Subclass with Delegate} \item \link{Replace Superclass with Delegate} \end{itemize} \item Large Class: A class that wants to do too much. \\ Fixes: \begin{itemize} \item \link{Extract Class} \item \link{Extract Superclass} \item \link{Replace Type Code with Subclasses} \end{itemize} \item Alternative Classes with Different Interfaces: Substitution only works if the interfaces are the same. \\ Fixes: \begin{itemize} \item \link{Change Function Declaration} \item \link{Move Function} \item \link{Extract Superclass} \end{itemize} \item Data Class: Classes with fields, getters and setters and nothing else. \\ Fixes: \begin{itemize} \item \link{Encapsulate Record} \item \link{Remove Setting Method} \item \link{Move Function} \item \link{Extract Function} \item \link{Split Phase} \end{itemize} \item Refused Bequest: Unneeded methods or data from a parent in a child. \\ Fixes: \begin{itemize} \item \link{Push Down Method} \item \link{Push Down Field} \item \link{Replace Subclass with Delegate} \item \link{Replace Superclass with Delegate} \end{itemize} \item Comments: Comments shouldn't mask bad code. \\ Fixes: \begin{itemize} \item \link{Extract Function} \item \link{Change Function Declaration} \item \link{Introduce Assertion} \end{itemize} \end{itemize} \section{A First Set of Refactorings} \subsection{Extract Function} \label{sec:Extract Function} Inverse of Inline Function\\ When we have a code fragment that can be grouped together. \begin{lstlisting} Function printOwing (invoice){ printBanner(); let outstanding = calculateOutstanding(); <@\textcolor{red}{//print details}@> Console.log('name:invoice.customer'); Console.log('amount:outstanding'); } \end{lstlisting} \wordt \begin{lstlisting} Function printOwing (invoice){ printBanner(); let outstanding = calculateOutstanding(); <@\textcolor{red}{printDetails(outstanding)}@>{ Console.log('name:invoice.customer'); Console.log('amount:outstanding'); } } \end{lstlisting} \subsection{Inline Function} \label{sec:Inline Function} Inverse of Extract Function \begin{lstlisting} Function getRating(driver){ return moreThanFiveLateDeliveries(driver) ? 2 :1; } Function moreThanFiveLateDelivers(driver){ <@\textcolor{red}{return driver.numberOfLateDelivers $>$ 5;}@> } \end{lstlisting} \wordt \begin{lstlisting} Function getRating(driver){ <@\textcolor{red}{return (driver.numberOfLateDelivers $>$ 5) ? 2 : 1;}@> } \end{lstlisting} \subsection{Extract Variable} \label{sec:Extract Variable} Inverse of Inline Variable \begin{lstlisting} return <@\textcolor{red}{order.quantity*order.itemPrice}@> - <@\textcolor{green}{Math.max(0,order.quantity-500)*order.itemPrice*0.05}@> + <@\textcolor{purple}{Math.min(order.quantity*order.itemPrice*0.1,100)}@>; \end{lstlisting} \wordt \begin{lstlisting} <@\textcolor{red}{const basePrice = order.quantity * order.itemPrice;}@> <@\textcolor{green}{const quantityDiscount = Math.max(0,order.quantity - 500) * order.itemPrice * 0.05;}@> <@\textcolor{purple}{const shipping = Math.min(order.quantity * order.itemPrice * 0.1,100);}@> return basePrice - quantityDiscount + shipping; \end{lstlisting} \subsection{Inline Variable} \label{sec:Inline Variable} Inverse of Extract Variable \begin{lstlisting} let basePrice = <@\textcolor{red}{anOrder.basePrice}@>; return (basePrice <@\textcolor{red}{$>$ 100}@>); \end{lstlisting} \wordt \begin{lstlisting} return (<@\textcolor{red}{anOrder.basePrice $>$ 100}@>); \end{lstlisting} \subsection{Change Function Declaration} \label{sec:Change Function Declaration} Rename Method, Add Parameter, Remove Parameter \begin{lstlisting} Function <@\textcolor{red}{circum}@>(radius){} \end{lstlisting} \wordt \begin{lstlisting} Function <@\textcolor{red}{circumference}@>(radius){} \end{lstlisting} \subsection{Encapsulate Variable} \label{sec:Encapsulate Variable} \begin{lstlisting} let <@\textcolor{red}{defaultOwner}@> = {firstname:"Martin",lastname:"Fowler"}; \end{lstlisting} \wordt \begin{lstlisting} let defaultOwnerData = {firstname:"Martin",lastname:"Fowler"}; export <@\textcolor{red}{function defaultOwner}@>(){return defaultOwnerData;} export <@\textcolor{red}{setDefaultOwner}@>(arg){defaultOwnerData = arg}; \end{lstlisting} \subsection{Rename Variable} \label{sec:Rename Variable} \begin{lstlisting} let <@\textcolor{red}{a}@> = height * width; \end{lstlisting} \wordt \begin{lstlisting} let <@\textcolor{red}{area}@> = height * width; \end{lstlisting} \subsection{Introduce Parameter Object} \label{sec:Introduce Parameter Object} When you have a method that requires a long parameter list and you can group the parameters together naturally. \begin{lstlisting} Function amountInvoiced(<@\textcolor{red}{startDate;endDate}@>; Function amountReceived(<@\textcolor{red}{startDate;endDate}@>; Function amountOverdue(<@\textcolor{red}{startDate;endDate}@>; \end{lstlisting} \wordt \begin{lstlisting} Function amountInvoiced(<@\textcolor{red}{aDateRange}@>; Function amountReceived(<@\textcolor{red}{aDateRange}@>; Function amountOverdue(<@\textcolor{red}{aDateRange}@>; \end{lstlisting} \subsection{Combine Functions into Class} \label{sec:Combine Functions into Class} \begin{lstlisting} <@\textcolor{red}{Function}@> base(aReading){}; <@\textcolor{red}{Function}@> taxableCharge(aReading){}; <@\textcolor{red}{Function}@> calculateBaseCharge(aReading){}; \end{lstlisting} \wordt \begin{lstlisting} <@\textcolor{red}{Class Reading\{}@> Base(){} TaxableCharge(){} CalculateBaseCharge(){} <@\textcolor{red}{\}}@> \end{lstlisting} \subsection{Combine Functions into Transform} \label{sec:Combine Functions into Transform} \begin{lstlisting} Function base(aReading){} Function taxableCharge(aReading){} \end{lstlisting} \wordt \begin{lstlisting} <@\textcolor{red}{Function enrichReading(argReading)}@>{ Const aReading = _.cloneDeep(argReading); aReading.baseCharge = base(aReading); aReading.taxableCharge = taxableCharge(aReading); <@\textcolor{red}{return aReading;}@> } \end{lstlisting} \subsection{Split Phase} \label{sec:Split Phase} \begin{lstlisting} Const orderData = <@\textcolor{red}{orderString.split(/\textbackslash s+/);}@> Const productPrice = <@\textcolor{red}{pricelist[orderData[0].split("-")[1]];}@> Const orderPrice = <@\textcolor{red}{parseInt(orderData[1])*productPrice;}@> \end{lstlisting} \wordt \begin{lstlisting} Const orderRecord = parseOrder(order); Const orderPrice = price(orderRecord,priceList); Function parseOrder(aString){ Const values = <@\textcolor{red}{aString.split(/\textbackslash s+/);}@> return({ productID:values[0].split("-")[1], quantity:parseInt(values[1]), }); } Function price (order,pricelist){ return order.quantity*pricelist[order.productID]; } \end{lstlisting} \section{Encapsulation} \subsection{Encapsulate Record} \label{sec:Encapsulate Record} Replace record with data class. \begin{lstlisting} Organization = {<@\textcolor{red}{name}@>:"Acme Gooseberries",<@\textcolor{red}{country}@>:"GB"} \end{lstlisting} \wordt \begin{lstlisting} Class Organization{ Constructor(data){ this.name = data.<@\textcolor{red}{name}@>; this.country = data.<@\textcolor{red}{country}@>; } get name(){return this.name;} set name(arg){this.name = arg;} get country(){return this.country;} set country(arg){this.country = arg;} } \end{lstlisting} \subsection{Encapsulate Collection} \label{sec:Encapsulate Collection} When a method returns a collection. \begin{lstlisting} Class Person{ get courses(){return this.courses;} set courses(aList){this.courses = aList;} } \end{lstlisting} \wordt \begin{lstlisting} Class Person{ get courses(){return this.courses.slice();} addCourse(aCourse){} removeCourse(aCourse){} } \end{lstlisting} \subsection{Replace Primitive with Object} \label{sec:Replace Primitive with Object} Replace data value with object, replace type code with class. \begin{lstlisting} Orders.filter(o => "high" == o.priority || "rush" == o.priority); \end{lstlisting} \wordt \begin{lstlisting} Orders.filter(o => o.priority.higherThan(new Priority("normal"))) \end{lstlisting} \subsection{Replace Temp with Query} \label{sec:Replace Temp with Query} Create a method to compute or access the temporary variable. \begin{lstlisting} Const basePrice = <@\textcolor{red}{this.quantity * this.itemPrice}@>; if(basePrice>1000) return basePrice * 0.95; else return basePrice * 0.98; \end{lstlisting} \wordt \begin{lstlisting} <@\textcolor{red}{get basePrice(){this.quantity*this.itemParice;}}@>; ... if(this.basePrice>1000) return this.basePrice*0.95; else return this.basePrice*0.98; \end{lstlisting} \subsection{Extract Class} \label{sec:Extract Class} Inverse of Inline Class\\ When we have one class doing the work that should be done by two. \begin{lstlisting} Class Person{ get officeAreaCode(){return this.officeAreaCode;} get officeNumber(){return this.officeNumber;} } \end{lstlisting} \wordt \begin{lstlisting} Class Person{ get officeAreaCode(){return this.telephoneNumber.areaCode;} get officeNumber(){return this.telephoneNumber.number;} } Class TelephoneNumber{ get areaCode(){return this.areaCode;} get number(){return this.number;} } \end{lstlisting} \subsection{Inline Class} \label{sec:Inline Class} Inverse of Extract Class\\ When a class isn't doing enough. \begin{lstlisting} Class Person{ get officeAreaCode(){return this.telephoneNumber.areaCode;} get officeNumber(){return this.telephoneNumber.number;} } Class TelephoneNumber{ get areaCode(){return this.areaCode;} get number(){return this.number;} } \end{lstlisting} \wordt \begin{lstlisting} Class Person{ get officeAreaCode(){return this.officeAreaCode;} get officeNumber(){return this.officeNumber;} } \end{lstlisting} \subsection{Hide Delegate} \label{sec:Hide Delegate} Inverse of Remove Middle Man \begin{lstlisting} manager = aPerson.department.manager; \end{lstlisting} \wordt \begin{lstlisting} manager = aPerson.manager; Class Person{ get manager(){return this.department.manager;} } \end{lstlisting} \subsection{Remove Middle Man} \label{sec:Remove Middle Man} Inverse of Hide Delegate. \begin{lstlisting} manager = aPerson.manager; Class Person{ get manager(){return this.department.manager;} } \end{lstlisting} \wordt \begin{lstlisting} manager = aPerson.department.manager; \end{lstlisting} \subsection{Substitute Algorithm} \label{sec:Substitute Algorithm} \begin{lstlisting} function foundPerson(people){ for(let i = 0;i < people.length;i++){ if(people[i] == "Don") return "Don"; if(people[i] == "John") return"Johan"; if(people[i] == "Kent") return "Kent"; } return ""; } \end{lstlisting} \wordt \begin{lstlisting} function foundPerson(people){ const candidates = ["Don","John","Kent"]; return people.find(p => candidates.includes(p)) || ' '; } \end{lstlisting} \section{Moving Features} \subsection{Move Function} \label{sec:Move Function} \begin{lstlisting} Class Account{ get overdraftCharge(){} } \end{lstlisting} \wordt \begin{lstlisting} Class AccountType{ get overdraftCharge(){} } \end{lstlisting} \subsection{Move Field} \label{sec:Move Field} \begin{lstlisting} Class Customer{ get plan(){return this.plan;} get discountRate(){return this.discountRate;} } \end{lstlisting} \wordt \begin{lstlisting} Class Customer{ get plan(){return this.plan;} get discountRate(){return this.plan.discountRate;} } \end{lstlisting} \subsection{Move Statements into Function} \label{sec:Move Statements into Function} Inverse of Move Statements to Callers \begin{lstlisting} Result.push(
title:${person.photo.title}
); Result.concat(photoData(person.photo)); Function photoData(aPhoto){ return[location:${aPhoto.location}
,date:${aPhoto.date.toDateString()}
, ]; } \end{lstlisting} \wordt \begin{lstlisting} Result.concat(photoData(person.photo)); Function photoData(aPhoto){ return[title: ${person.photo.title}
,location:${aPhoto.location}
,date:${aPhoto.date.toDateString()}
, ]; } \end{lstlisting} \subsection{Move Statements to Callers} \label{sec:Move Statements to Callers} Inverse of Move Statements into Function \begin{lstlisting} emitPhotoData(outStream,person.photo); function emitPhotoData(outStream,photo){ outStream.write(title:{photo.title}
\n); outStream.write(location:{photo.location}
\n); } \end{lstlisting} \wordt \begin{lstlisting} emitPhotoData(outStream,person.photo); outStream.write(location:{photo.location}
\n); function emitPhotoData(outStream,photo){ outStream.write(title:{photo.title}
\n); } \end{lstlisting} \subsection{Replace Inline Code with Function Call} \label{sec:Replace Inline Code with Function Call} \begin{lstlisting} let appliesToMass = false; for(const s of states){ if(s == "MA") appliesToMass = true; } \end{lstlisting} \wordt \begin{lstlisting} appliesToMass = states.includes("MA"); \end{lstlisting} \subsection{Slide Statements} \label{sec:Slide Statements} \begin{lstlisting} Const pricingPlan = retrievePricingPlan(); Const order = retreiveOrder(); Let charge; Const chargePerUnit = pricingPlan.unit; \end{lstlisting} \wordt \begin{lstlisting} Const pricingPlan = retrievePricingPlan(); Const chargePerUnit = pricingPlan.unit; Const order = retreiveOrder(); Let charge; \end{lstlisting} \subsection{Split Loop} \label{sec:Split Loop} \begin{lstlisting} let averageAge = 0; let totalSalary = 0; for(const p of people){ averageage += p.age; totalSalary += p.salary; } averageAge = averageAge / people.length; \end{lstlisting} \wordt \begin{lstlisting} let totalSalary = 0; for(const p of people){ totalSalary += p.salary; } let averageAge = 0; for(const p of people){ averageage += p.age; } averageAge = averageAge / people.length; \end{lstlisting} \subsection{Replace Loop with Pipeline} \label{sec:Replace Loop with Pipeline} \begin{lstlisting} const names = []; for(const i of input){ if(i.job === "programmer") names.push(i.name); } \end{lstlisting} \wordt \begin{lstlisting} const names = input .filter(i => i.job === "programmer") .map(i => i.name) ; \end{lstlisting} \subsection{Remove Dead Code} \label{sec:Remove Dead Code} Vrij duidelijk denk ik \section{Organizing Data} \subsection{Split Variable} \label{sec:Split Variable} Remove Assignments to Parameters, Split Temp \begin{lstlisting} let <@\textcolor{red}{temp}@> = 2 * (height + width); console.log(<@\textcolor{red}{temp}@>); <@\textcolor{green}{temp}@> = height * width; console.log(<@\textcolor{green}{temp}@>); \end{lstlisting} \wordt \begin{lstlisting} const <@\textcolor{red}{perimeter}@> = 2 * (height + width); console.log(<@\textcolor{red}{perimeter}@>); const <@\textcolor{green}{area}@> = height * width; console.log(<@\textcolor{green}{area}@>); \end{lstlisting} \subsection{Rename Field} \label{sec:Rename Field} \begin{lstlisting} Class Orgnization{ get name(){} } \end{lstlisting} \wordt \begin{lstlisting} Class Orgnization{ get title(){} } \end{lstlisting} \subsection{Replace Derived Variable with Query} \label{sec:Replace Derived Variable with Query} \begin{lstlisting} get discountedTotal(){return this.discountedTotal;} set discount(aNumber){ const old = this.discount; this.discount = aNumber; this.discountedTotal += old - aNumber; } \end{lstlisting} \wordt \begin{lstlisting} get discountedTotal(){return this.baseTotal - this.discount;} set discount(aNumber){this.discount = aNumber;} \end{lstlisting} \subsection{Change Reference to Value} \label{sec:Change Reference to Value} Inverse of Change Value to Reference \begin{lstlisting} Class Product{ applyDiscount(arg){this.price.amount -= arg;} } \end{lstlisting} \wordt \begin{lstlisting} Class Product{ applyDiscount(arg){ this.price.amount -= new Money(this.price.amount - arg,this.price.currency); } } \end{lstlisting} \subsection{Change Value to Reference} \label{sec:Change Value to Reference} Inverse of Change Reference to Value. \begin{lstlisting} let customer = new Customer(customerData); \end{lstlisting} \wordt \begin{lstlisting} let customer = customerRepository.get(customerData.id); \end{lstlisting} \section{Simplifying Conditional Logic} \subsection{Decompose Conditional} \label{sec:Decompose Conditional} When we have a complicated conditional (if-then-else) statement \begin{lstlisting} if (!aDate.isBefore(plan.summerStart) && !aDate.isAfter(plan.summerEnd)) charge = quantity * plan.summerRate; else charge = quantity + plan.regularRate + plan.regularServiceCharge; \end{lstlisting} \wordt \begin{lstlisting} if(summer) charge = summerCharge(); else charge = regularCharge(); \end{lstlisting} \subsection{Consolidate Conditional Expression} \label{sec:Consolidate Conditional Expression} \begin{lstlisting} if(anEmployee.seniority < 2) return 0; if(anEmployee.monthsDisabled > 12) return 0; if(anEmployee.isPartTime) return 0; \end{lstlisting} \wordt \begin{lstlisting} If(isNotEligableForDisability()) return 0; function isNotEligableForDisability(){ return ((anEmployee.seniority < 2) || anEmployee.monthsDisabled > 12) || anEmployee.isPartTime)); } \end{lstlisting} \subsection{Replace Nested Conditional with Guard Clauses} \label{sec:Replace Nested Conditional with Guard Clauses} \begin{lstlisting} function getPayAmount(){ let result; if(isDead) result = deadAmount(); else{ if(isSeparated) result = separatedAmount(); else{ if(isRetired) result = retiredAmount(); else result = normalPayAmount(); } } return result; } \end{lstlisting} \wordt \begin{lstlisting} function getPayAmount(){ if(isDead) return deadAmount(); if(isSeparated) return separatedAmount(); if(isRetired) return retiredAmount(); return normalPayAmount(); } \end{lstlisting} \subsection{Replace Conditional with Polymorphism} \label{sec:Replace Conditional with Polymorphism} \begin{lstlisting} switch (bird.type){ case 'EuropeanSwallow': return "average"; case 'AfricanSwallow': return(bird.numberOfCocounts > 2) ? "tired" : "average"; case 'NorwegianBlueParrot': return(bird.voltage > 100) ? "scorched" : "beautiful"; default: return "unknown"; } \end{lstlisting} \wordt \begin{lstlisting} class EuropeanSwallow{ get plumage(){ return "average"; } } class AfricanSwallow{ get plumage(){ return(bird.numberOfCocounts > 2) ? "tired" : "average"; } } class NorwegianBlueParrot{ get plumage(){ return(bird.voltage > 100) ? "scorched" : "beautiful"; } } \end{lstlisting} \subsection{Introduce Special Case} \label{sec:Introduce Special Case} Introduce Null Object \begin{lstlisting} if(aCustomer === "unknown") customerName = "occupant"; \end{lstlisting} \wordt \begin{lstlisting} class UnknownCustomer{ get name(){return "occupant";} } \end{lstlisting} \subsection{Introduce Assertion} \label{sec:Introduce Assertion} \begin{lstlisting} if(this.discountRate) base = base - (this.discountRate * base); \end{lstlisting} \wordt \begin{lstlisting} assert (this.discountRate >= 0); if(this.discountRate) base = base - (this.discountRate * base); \end{lstlisting} \section{Refactoring APIs} \subsection{Separate Query from Modifier} \label{sec:Separate Query from Modifier} \begin{lstlisting} function getTotalOutstandingAndSendBill(){ const result = customer.invoices.reduce((total,each) => each.amount + total,0); sendBill(); return result; } \end{lstlisting} \wordt \begin{lstlisting} function totalOutstanding(){ return customer.invoices.reduce((total,each) => each.amount + total,0); } function sendBill(){ emailGateWay.send(formatBill(customer)); } \end{lstlisting} \subsection{Parameterize Function} \label{sec:Parameterize Function} Parameterize Method. \begin{lstlisting} function tenPercentRaise(aPerson){ aPerson.salary = aPerson.salary.multiply(1.1); } function fivePercentRaise(aPerson){ aPerson.salary = aPerson.salary.multiply(1.05); } \end{lstlisting} \wordt \begin{lstlisting} function raise(aPerson,factor){ aPerson.salary = aPerson.salary.multiply(1 + factor); } \end{lstlisting} \subsection{Remove Flag Argument} \label{sec:Remove Flag Argument} Replace Parameter with Explicit Methods \begin{lstlisting} function setDimension(name,value){ if(name == "height"){ this.height = value; return; } if(name == "width"){ this.width = value; return; } } \end{lstlisting} \wordt \begin{lstlisting} function setHeight(value){this.height = value;} function setWidth(value){this.width = value;} \end{lstlisting} \subsection{Preserve Whole Object} \label{sec:Preserve Whole Object} When you have a method that requires a long parameter list: passing multiple values from same object, pass whole object instead. \begin{lstlisting} const low = aRoom.daysTempRange.low; const high = aRoom.daysTempRange.high; if(aPlan.withinRange(low,high)) \end{lstlisting} \wordt \begin{lstlisting} if(aPlan.withinRange(aRoom.daysTempRange)) \end{lstlisting} \subsection{Replace Parameter with Query} \label{sec:Replace Parameter with Query} Inverse of Replace Query with Parameter\\ Replace Parameter with Method \begin{lstlisting} availableVacation(anEmployee,anEmployee.grade); function availableVacation(anEmployee,grade){ //calculate vacation } \end{lstlisting} \wordt \begin{lstlisting} availableVacation(anEmployee) function availableVacation(anEmployee){ const grade = anEmployee.grade; //calculate vacation } \end{lstlisting} \subsection{Replace Query with Parameter} \label{sec:Replace Query with Parameter} Inverse of Replace Parameter with Query. \begin{lstlisting} targetTemperature(aPlan) function targetTemperature(aPlan){ currentTemperature = thermostat.currentTemperature; //rest of function } \end{lstlisting} \wordt \begin{lstlisting} targetTemperature(aPlan, thermostat.currentTemperature) function targetTemperature(aPlan, currentTemperature){ //rest of function } \end{lstlisting} \subsection{Remove Setting Method} \label{sec:Remove Setting Method} \begin{lstlisting} class Person{ get name(){} set name(aString){} } \end{lstlisting} \wordt \begin{lstlisting} class Person{ get name(){} } \end{lstlisting} \subsection{Replace Constructor with Factory Function} \label{sec:Replace Constructor with Factory Function} Replace Constructor with Factory Method \begin{lstlisting} leadEngineer = new Employee(document.leadEngineer,'E'); \end{lstlisting} \wordt \begin{lstlisting} leadEngineer = createEngineer(document.leadEngineer); \end{lstlisting} \subsection{Replace Function with Command} \label{sec:Replace Function with Command} Inverse of Replace Command with Function \begin{lstlisting} function score(candidate,medicalExam,scoringGuide){ let result = 0; let healthLevel = 0; //long body code } \end{lstlisting} \wordt \begin{lstlisting} class Score{ constructor(candidate,medicalExam,scoringGuid){ this.candidate = candidate; this.medicalExam = medicalExam; this.scoringGuide = scoringGuide; } execute(){ this.result = 0; this.healthLevel = 0; //long body code } } \end{lstlisting} \subsection{Replace Command with Function} \label{sec:Replace Command with Function} Inverse of Replace Function with Command \begin{lstlisting} class ChargeCalculator{ constructor(customer,usage){ this.customer = customer; this.usage = usage; } execute(){ return this.customer.rate * this.usage; } } \end{lstlisting} \wordt \begin{lstlisting} function charge(customer,usage){ return customer.rate * usage; } \end{lstlisting} \section{Dealing with Inheritance} \subsection{Pull Up Method} \label{sec:Pull Up Method} Inverse of Push Down Method \begin{lstlisting} class Employee{} class Salesman extends Employee{ get name(){} } class Engineer extends Employee{ get name(){} } \end{lstlisting} \wordt \begin{lstlisting} class Employee{ get name(){} } class Salesman extends Employee{} class Engineer extends Employee{} \end{lstlisting} \subsection{Pull Up Field} \label{sec:Pull Up Field} Inverse of Push Down Field \begin{lstlisting} class Employee{} class Salesman extends Employee{ private String name; } class Engineer extends Employee{ private String name; } \end{lstlisting} \wordt \begin{lstlisting} class Employee{ protected String name; } class Salesman extends Employee{} class Engineer extends Employee{} \end{lstlisting} \subsection{Pull Up Constructor Body} \label{sec:Pull Up Constructor Body} \begin{lstlisting} class Party{} class Employee extends Party{ constructor(name,id,monthlyCost){ super(); this.id = id; this.name = name; this.monthlyCost = monthlyCost; } } \end{lstlisting} \wordt \begin{lstlisting} class Party{ constructor(name){ this.name=name; } } class Employee extends Party{ constructor(name,id,monthlyCost){ super(name); this.id = id; this.monthlyCost = monthlyCost; } } \end{lstlisting} \subsection{Push Down Method} \label{sec:Push Down Method} Inverse of Pull Up Method \begin{lstlisting} class Employee{ get quota{} } class Engineer extends Employee{} class Salesman extends Employee{} \end{lstlisting} \wordt \begin{lstlisting} class Employee{} class Engineer extends Employee{} class Salesman extends Employee{ get quota{} } \end{lstlisting} \subsection{Push Down Field} \label{sec:Push Down Field} Inverse of Pull Up Field \begin{lstlisting} class Employee{ private String quota; } class Salesman extends Employee{} class Engineer extends Employee{} \end{lstlisting} \wordt \begin{lstlisting} class Employee{} class Engineer extends Employee{} class Salesman extends Employee{ protected String quota; } \end{lstlisting} \subsection{Replace Type Code with Subclasses} \label{sec:Replace Type Code with Subclasses} Inverse of Remove Subclass \begin{lstlisting} function createEmployee(name,type){ return new Employee(name,type); } \end{lstlisting} \wordt \begin{lstlisting} function createEmployee(name,type){ switch(type){ case "engineer" : return new Engineer(name); case "salesman" : return new Salesman(name); case "manager" : return new Manager(name); } } \end{lstlisting} \subsection{Remove Subclass} \label{sec:Remove Subclass} Replace Subclass with Fields\\ Inverse of Replace Type Code with Subclasses \begin{lstlisting} class Person{ get genderCode(){ return"X";} } class Male extends Person{ get genderCode(){ return "M";} } class Female extends Person{ get genderCode(){ return "F";} } \end{lstlisting} \wordt \begin{lstlisting} class Person{ get genderCode() { return this.genderCode; } } \end{lstlisting} \subsection{Extract Superclass} \label{sec:Extract Superclass} \begin{lstlisting} class Department{ get totalAnnualCost(){} get name(){} get headcount(){} } class Employee{ get annualCost(){} get name(){} get id(){} } \end{lstlisting} \wordt \begin{lstlisting} class Party{ get annualCost(){} get name(){} } class Department extends Party{ get annualCost(){} get headcount(){} } class Employee extends Party{ get annualCost(){} get id(){} } \end{lstlisting} \subsection{Collapse Hierarchy} \label{sec:Collapse Hierarchy} If it serves no purpose. \begin{lstlisting} class Employee{} class Salesman extends Employee{} \end{lstlisting} \wordt \begin{lstlisting} class Employee{...} \end{lstlisting} \subsection{Replace Subclass with Delegate} \label{sec:Replace Subclass with Delegate} \begin{lstlisting} class Order{ get daysToShip(){ return this.warehouse.daysToShip; } } class PriorityOrder extends Order{ get daysToShip(){ return this.priorityPlan.daysToShip; } } \end{lstlisting} \wordt \begin{lstlisting} class Order{ get daysToShip(){ return (this.priorityDelegate) ? this.priorityDelegate.daysToShip : this.warehouse.daysToShip; } } class PriorityOrderDelegate{ get daysToShip(){ return this.priorityPlan.daysToShip; } } \end{lstlisting} \subsection{Replace Superclass with Delegate} \label{sec:Replace Superclass with Delegate} Replace Inheritance with Delegation\\ Sometimes a subclass inherits more from its superclass than you want it to. \begin{lstlisting} class List{} class Stack extends List{} \end{lstlisting} \wordt \begin{lstlisting} class Stack { constructor(){ this.storage = new List(); } } class List{} \end{lstlisting} \end{document}