and evolutionary architecture. Identifies design-maintenance issues (“code smells”) that typically represent violations of known principles of good design. Applies incrementally and iteratively a set of design improvement techniques (“refactorings”). Minimize complexity & duplication in order to maximize simplicity & ease-of-change is the goal. Encourages the “right” design details to emerge “just-in-time” with minimal guesswork/rework. REFACTORING
activities: adding function and refactoring. When you add function, you shouldn’t be changing existing code; you are just adding new capabilities. You can measure your progress by adding tests and ge#ing the tests to work. When you refactor, you make a point of not adding function; you only restructure the code. You don’t add any tests; you only change tests when you absolutely need to in order to cope with a change in an interface. Martin Fowler, Refactoring while talking about "two hats metaphor" of Kent Beck
a field from one class to another, pull some code out of a method to make into its own method, and push some code up or down a hierarchy. To refactor safely, you must run tests to ensure your changes didn't break anything. You will have more confidence to refactor and make other design changes if you can quickly run automated tests. Refactoring in small steps helps prevent introducing defects. Most refactorings take only seconds or minutes to perform. Even large restructurings are implemented in small steps. MAKE IT SMALL LET IT GROW WITH TESTS
it is not an entity or a value object Add intention to the name if not exists Extract new structures into new classes or methods Go to line 1 1 2 3 4 5 That turns our big fat X_Manager into A_Remover, B_Provider, C_Calculator, D_Creator, E_Validator. Now we can start to talk about Single Responsibility Rule. Developers hate creating classes with few lines. Even though the majority know big fat classes need refactoring, they o!en prefer few number of big fat classes over many small classes. Here is a way of breaking this habit.
aside time to do separately from implementation activities. Refactoring is something you do all the time in li#le bursts. Martin Fowler, Refactoring: Improving the Design of Existing Code
While reviewing code A!er coding the same/similar thing for the third time A!er the third time you deferred refactoring a change, for any reason Before the end of the iteration if you haven’t been following The Rule of Three 1 2 3 4 5 6 The Rule of Three The Rule of Three The Rule of Three Martin Fowler, Refactoring: Improving the Design of Existing Code ?
that “emerges” from the synergy of combining Refactoring together with TDD, Continuous Integration and Automated Testing. Design, rather than occurring all up front, occurs continuously during development. The cumulative effect of these small changes can radically improve the design. EMERGENT DESIGN
on that project have a shared understanding of the system design. This shared understanding is called "architecture." This understanding includes how the system is divided into components and how the components interact through interfaces. Ralph Johnson
can begin execution. Architecture is about things that are hard to change later, it is difficult to allow an architecture to emerge. However, just because we can't allow architecture to emerge doesn't mean that it can't evolve. If we create an initial, flexible architecture and take special care to not create an irreversible decision, then we can allow it to evolve over time as new concerns appear. — Neal Ford, Evolutionary Architecture and Emergent Design Key techniques of Evolutionary Architecture include: Deferring Irreversible Decisions to the “Last Responsible Moment” (LRM Principle) Architectural “Spike” (a.k.a. Architectural “Deep Dive”) Architecture Iteration and/or Spike Iteration EVOLUTIONARY ARCHITECTURE
change something in one place in your code, you have to make many changes in other places too. Divergent Change Shotgun Surgery Parallel Inheritance Hierarchies
would make the code cleaner, more efficient and easier to understand. Comments Duplicate Code Loser Class Pale Data Class Dead Code Speculative Generality
coupling between classes or show what happens if coupling is replaced by excessive delegation. Feature Envy Inappropriate Intimacy Message Chains Middle Man Incomplete Library Class
+ name); System.out.println("amount: " + getOutstanding()); } void printOwing() { printBanner(); printDetails(getOutstanding()); } void printDetails(double outstanding) { System.out.println("name: " + name); System.out.println("amount: " + outstanding); } You have a code fragment that can be grouped together. Move this code to a separate new method (or function) and replace the old code with a call to the method. 1
1; } boolean moreThanFiveLateDeliveries() { return _numberOfLateDeliveries > 5; } int getRating() { return (_numberOfLateDeliveries > 5) ? 2 : 1; } Someone is using too much indirection and it seems that every method does simple delegation to another method, and I get lost in all the delegation You inline the various calls made by the method that have behavior you want to have in the method object. It’s easier to move one method than to move the method and its called methods. 2
and then pass them as parameters to a method. Instead, try passing the whole object. int low = daysTempRange().getLow(); int high = daysTempRange().getHigh(); boolean withinPlan = plan.withinRange(low, high); boolean withinPlan = plan.withinRange(daysTempRange()); 4
double price() { double primaryBasePrice; double secondaryBasePrice; double tertiaryBasePrice; // long computation. //... } } class Order { //... public double price() { return new PriceCalculator(this).compute(); } } class PriceCalculator { private double primaryBasePrice; private double secondaryBasePrice; private double tertiaryBasePrice; public PriceCalculator(Order order) { // copy relevant information from order object. //... } public double compute() { // long computation. //... } } You have a long method in which the local variables are so intertwined that you cannot apply Extract Method. Transform the method into a separate class so that the local variables become fields of the class. Then you can split the method into several methods within the same class. 5
Decompose the complicated parts of the conditional into separate methods: the condition, then and else. if (date.before(SUMMER_START) || date.after(SUMMER_END)) { charge = quantity * winterRate + winterServiceCharge; } else { charge = quantity * summerRate; } if (notSummer(date)) { charge = winterCharge(quantity); } else { charge = summerCharge(quantity); } 6
classes) contains a data field. The field has its own behavior and associated data. Create a new class, place the old field and its behavior in the class, and store the object of the class in the original class. 9
that contains type code. The values of this type are not used in operator conditions and do not affect the behavior of the program. Create a new class and use its objects instead of the type code values. 10
that directly affects program behavior (values of this field trigger various code in conditionals). Create subclasses for each value of the coded type. Then extract the relevant behaviors from the original class to these subclasses. Replace the control flow code with polymorphism. 11
int status; // 0=PRODUCED, 1=SOLD private String model; // XSI-Q2TU-17-1 public String getSeries() { return this.model.split("-")[0]; } public String getModelYear() { return this.model.split("-")[2]; } } public class NotebookModel { final String series; final String subseries; public NotebookModel(String series, String subseries) { this.series = series; this.subseries = subseries; } } public enum NotebookStatus { PRODUCED, SOLD; } public class Notebook { long id; NotebookStatus status; NotebookModel model; } You keep data and assign a logic behind the scenes. The best way to remove the logic on data is not to keep it at all. 12
itemPrice; double seasonDiscount = store.getSeasonalDiscount(); double fees = store.getFees(); double finalPrice = discountedPrice(basePrice, seasonDiscount, fees); int basePrice = quantity * itemPrice; double finalPrice = discountedPrice(basePrice, store); Before a method call, a second method is run and its result is sent back to the first method as an argument. But the parameter value could have been obtained inside the method being called. Instead of passing the value through a parameter, place the value-ge#ing code inside the method. 13
than in its own class. Create a new method in the class that uses the method the most, then move code from the old method to there. Turn the code of the original method into a reference to the new method in the other class or else remove it entirely. 14
double getSpeed(); } class European extends Bird { double getSpeed() { return getBaseSpeed(); } } class African extends Bird { double getSpeed() { return getBaseSpeed() - getLoadFactor() * numberOfCoconuts; } } class NorwegianBlue extends Bird { double getSpeed() { return (isNailed) ? 0 : getBaseSpeed(voltage); } } // Somewhere in client code speed = bird.getSpeed(); Create subclasses matching the branches of the conditional. In them, create a shared method and move code from the corresponding branch of the conditional to it. Then replace the conditional with the relevant method call. The result is that the proper implementation will be a#ained via polymorphism depending on the object class. 15
{ if (name.equals("height")) { height = value; return; } if (name.equals("width")) { width = value; return; } Assert.shouldNeverReachHere(); } void setHeight(int arg) { height = arg; } void setWidth(int arg) { width = arg; } A method is split into parts, each of which is run depending on the value of a parameter. Extract the individual parts of the method into their own methods and call them instead of the original method. 16
BillingPlan.basic(); } else { plan = customer.getPlan(); } class NullCustomer extends Customer { boolean isNull() { return true; } Plan getPlan() { return new NullPlan(); } // Some other NULL functionality. } // Replace null values with Null-object. customer = (order.customer != null) ? order.customer : new NullCustomer(); // Use Null-object as if it's normal subclass. plan = customer.getPlan(); Since some methods return null instead of real objects, you have many checks for null in your code. Instead of null, return a null object that exhibits the default behavior. 17
only a portion of the methods of its superclass (or it's not possible to inherit superclass data). Create a field and put a superclass object in it, delegate methods to the superclass object, and get rid of inheritance. 18
understand. Place the result of the expression or its parts in separate variables that are self-explanatory. void renderBanner() { if ((platform.toUpperCase().indexOf("MAC") > -1) && (browser.toUpperCase().indexOf("IE") > -1) && wasInitialized() && resize > 0 ) { // do something } } void renderBanner() { final boolean isMacOs = platform.toUpperCase().indexOf("MAC") > -1; final boolean isIE = browser.toUpperCase().indexOf("IE") > -1; final boolean wasResized = resize > 0; if (isMacOs && isIE && wasInitialized() && wasResized) { // do something } } 20
or method of object А. Then the client calls a method of object B. Create a new method in class A that delegates the call to object B. Now the client does not know about, or depend on, class B. 21
need and you cannot add the method to the class. class Report { //... void sendReport() { Date nextDay = new Date(previousEnd.getYear(), previousEnd.getMonth(), previousEnd.getDate() + 1); //... } } 23
need and you cannot add the method to the class. Introduce Foreign Method class Report { //... void sendReport() { Date nextDay = new Date(previousEnd.getYear(), previousEnd.getMonth(), previousEnd.getDate() + 1); //... } } class Report { //... void sendReport() { Date newStart = nextDay(previousEnd); //... } private static Date nextDay(Date arg) { return new Date(arg.getYear(), arg.getMonth(), arg.getDate() + 1); } } Add the method to a client class and pass an object of the utility class to it as an argument. 23
methods that you need. But you cannot add these methods to the class. Create a new class containing the methods and make it either the child or wrapper of the utility class. 24