Download presentation
Presentation is loading. Please wait.
Published byAshlyn Hardy Modified over 8 years ago
1
Catalog of Refactoring (3) Organizing Data
2
Catalog Self Encapsulate Field Replace Data Value with Object Change Value to Reference Change Reference to Value Replace Array with Object Duplicate Observed Data Move Method Replace Magic Number with Symbolic Constant Change Unidirectional Association to Bidirectional Change Bidirectional Association to Unidirectional Encapsulate Field Encapsulate Collection Replace Record with Data Class Replace Type Code with Class Replace Type Code with Subclasses Replace Type Code with State/Strategy Replace Subclass with Fields
3
Self Encapsulate Field You are accessing a field directly, but the coupling to the field is becoming awkward. Create getting and setting methods for the field and use only those to access the field.
4
Motivation Allows a subclass to override how to get that information with a method Supports more flexibility in managing the data, such as lazy initialization
5
Mechanics 1.Create a getting and setting method for the field. 2.Find all references to the field and replace them with a getting or setting method. 3.Make the field private 4.Compile and test
6
Example class IntRange { private int _low, _high; boolean includes (int arg) { return arg >= _low && arg <= _high; } void grow(int factor) { _high = _high * factor; } IntRange (int low, int high) { _low = low; _high = high; }
7
class IntRange { boolean includes (int arg) { return arg >= getLow() && arg <= getHigh(); } void grow(int factor) { setHigh (getHigh() * factor); } private int _low, _high; int getLow() { return _low; } int getHigh() { return _high; } void setLow(int arg) { _low = arg; } void setHigh(int arg) { _high = arg; } Add getter and setter
8
class CappedRange extends IntRange { CappedRange (int low, int high, int cap) { super (low, high); _cap = cap; } private int _cap; int getCap() { return _cap; } int getHigh() { return Math.min(super.getHigh(), getCap()); } Benefit: can change behavior
9
Replace Data Value with Object You have a data item that needs additional data or behavior. Turn the data item into an object.
10
Mechanism 1.Create the class for the value. Give it a final field of the same type as the value in the source class. Add a getter and a constructor that takes the field as an argument. 2.Change the type of the field in the source class to the new class. 3.Change the getter in the source class to call the getter in the new class. 4.If the field is mentioned in the source class constructor, assign the field using the constructor of the new class. 5.Change the getting method to create a new instance of the new class. 6.Compile and test.
11
class Order... public Order (String customer) { _customer = customer; } public String getCustomer() { return _customer; } public void setCustomer(String arg) { _customer = arg; } private String _customer; …… private static int numberOfOrdersFor(Collection orders, Stringcustomer) { int result = 0; Iterator iter = orders.iterator(); while (iter.hasNext()) { Order each = (Order) iter.next(); if (each.getCustomerName().equals(customer)) result++; } return result; } Example
12
class Customer { public Customer (String name) { _name = name; } public String getName() { return _name; } private final String _name; } …… class Order... public Order (String customerName) { _customer = new Customer(customerName); } public String getCustomer() { return _customer.getName(); } private Customer _customer; public void setCustomer(String customerName) { _customer = new Customer(customerName); } Add Customer Class
13
Change Value to Reference You have a class with many equal instances that you want to replace with a single object. Turn the object into a reference object.
14
Motivation Reference objects are things like customer or account. Each object stands for one object in the real world –You use the object identity to test whether they are equal. Value objects are things like date or money. They are defined entirely through their data values. –You don't mind that copies exist; –Need to override the equals method (and the hashCode method too). Need Reference Objects: when you want to give it some changeable data and ensure that the changes ripple to everyone referring to the object.
15
Mechanism 1.Use Replace Constructor with Factory Method 2.Decide what object is responsible for providing access to the objects. 3.Decide whether the objects are precreated or created on the fly. 4.Alter the factory method to return the reference object. 5.Compile and test.
16
class Customer { public Customer (String name) { _name = name; } public String getName() { return _name; } private final String _name; } …… class Order... public Order (String customerName) { _customer = new Customer(customerName); } public String getCustomer() { return _customer.getName(); } private Customer _customer; public void setCustomer(String customerName) { _customer = new Customer(customerName); } Example Customer is a value object, each order has its own customer object
17
class Customer { private static Dictionary _instances = new Hashtable(); private string _name; private Customer (String name) { _name = name; } static void loadCustomers() { new Customer ("Lemon Car Hire").store(); new Customer ("Associated Coffee Machines").store(); new Customer ("Bilston Gasworks").store(); } private void store() { _instances.put(this.getName(), this); } public static Customer getNamed (String name) { return (Customer) _instances.get(name); } …… class Order { public Order (String customer) { _customer = Customer.getNamed(customer); } Turn into Reference Object
18
Change Reference to Value You have a reference object that is small, immutable, and awkward to manage. Turn it into a value object.
19
Motivation Reference objects have to be controlled in some way. You always need to ask the controller for the appropriate object. The memory links also can be awkward. Value objects are particularly useful for distributed and concurrent systems. –Can create many equivalent copies
20
Mechanism 1.Check that the candidate object is immutable or can become immutable. –If the candidate cannot become immutable, you should abandon this refactoring. 2.Create an equals method and a hash method. 3.Compile and test. 4.Consider removing any factory method and making a constructor public.
21
Example class Currency... private String _code; public String getCode() { return _code; } private Currency (String code) { _code = code; } … Currency usd = Currency.get("USD"); new Currency("USD").equals(new Currency("USD")) // returns false class Currency... public boolean equals(Object arg) { if (! (arg instanceof Currency)) return false; Currency other = (Currency) arg; return (_code.equals(other._code)); } public int hashCode() { return _code.hashCode(); } … new Currency("USD").equals(new Currency("USD")) // now returns true
22
Replace Array with Object You have an array in which certain elements mean different things. Replace the array with an object that has a field for each element.
23
Motivation Array should be used only to contain a collection of similar objects in some order
24
Example String[] row = new String[3]; row [0] = "Liverpool"; row [1] = "15"; Performance row = new Performance(); row.setName("Liverpool"); row.setWins("15");
25
Duplicate Observed Data You have domain data available only in a GUI control, and domain methods need access. Copy the data to a domain object. Set up an observer to synchronize the two pieces of data.
26
Motivation Separates code that handles the user interface from code that handles the business logic. Behavior can be separated easily, the data often cannot. –Data needs to be embedded in GUI control that has the same meaning as data that lives in the domain model Cannot just move the data, you have to duplicate it and provide the synchronization mechanism
27
Mechanism 1.Make the presentation class an observer of the domain class. 2.Use Self Encapsulate Field on the domain data within the GUI class. 3.Add a call to the setting method in the event handler to update the component with its current value using direct access. 4.Define the data and accessor methods in the domain class. 5.Redirect the accessors to write to the domain field. 6.Modify the observer's update method to copy the data from the domain field to the GUI control. 7.Compile and test every step.
28
Example public class IntervalWindow extends Frame... java.awt.TextField _startField; java.awt.TextField _endField; java.awt.TextField _lengthField; class SymFocus extends java.awt.event.FocusAdapter { public void focusLost(java.awt.event.FocusEvent event{ Object object = event.getSource(); if (object == _startField) StartField_FocusLost(event); else if (object == _endField) EndField_FocusLost(event); else if (object == _lengthField) LengthField_FocusLost(event); } void StartField_FocusLost(java.awt.event.FocusEvent event) { if (isNotInteger(_startField.getText())) _startField.setText("0"); calculateLength(); } void EndField_FocusLost(java.awt.event.FocusEvent event) { if (isNotInteger(_endField.getText())) _endField.setText("0"); calculateLength(); } void LengthField_FocusLost(java.awt.event.FocusEvent event) { if (isNotInteger(_lengthField.getText())) _lengthField.setText("0"); calculateEnd(); }
29
void calculateLength(){ try { int start = Integer.parseInt(_startField.getText()); int end = Integer.parseInt(_endField.getText()); int length = end - start; _lengthField.setText(String.valueOf(length)); } catch (NumberFormatException e) { throw new RuntimeException ("Unexpected Number Format Error"); } void calculateEnd() { try { int start = Integer.parseInt(_startField.getText()); int length = Integer.parseInt(_lengthField.getText()); int end = start + length; _endField.setText(String.valueOf(end)); } catch (NumberFormatException e) { throw new RuntimeException ("Unexpected Number Format Error"); } Need to move calculation from Window object into domain objects
30
class Interval extends Observable { private String _end = "0"; String getEnd() { return _end; } void setEnd (String arg) { _end = arg; setChanged(); notifyObservers(); } String calculateLength() { … } public class IntervalWindow extends Frame implements Observer { private Interval _subject; String getEnd() { return _subject.getEnd(); } void setEnd (String arg) { _subject.setEnd(arg); } public void update(Observable observed, Object arg) { _endField.setText(_subject.getEnd()); } Observer Pattern
31
Change Unidirectional Association to Bidirectional You have two classes that need to use each other's features, but there is only a one-way link. Add back pointers, and change modifiers to update both sets.
32
Mechanism 1.Add a field for the back pointer.. 2.Decide which class will control the association. 3.Create a helper method on the noncontrolling side of the association. Name this method to clearly indicate its restricted use. 4.If the existing modifier is on the controlling side, modify it to update the back pointers. 5.If the existing modifier is on the controlled side, create a controlling method on the controlling side and call it from the existing modifier. 6.Compile and test every step.
33
Example class Order... Customer getCustomer() { return _customer; } void setCustomer (Customer arg) { _customer = arg; } Customer _customer;
34
Decide which class take charge If both objects are reference objects and the association is one to many, then the object that has the one reference is the controller. –E.g. If one customer has many orders, the order controls the association. If one object is a component of the other, the composite should control the association If both objects are reference objects and the association is many to many, it doesn't matter which controls the association
35
Add Back Pointer to Order in Customer class Customer { private Set _orders = new HashSet(); Set friendOrders() { /** should only be used by Order when modifying the association */ return _orders; } void addOrder(Order arg) { arg.setCustomer(this); } … class Order... Customer _customer; void setCustomer (Customer arg)... if (_customer != null) _customer.friendOrders().remove(this); _customer = arg; if (_customer != null) _customer.friendOrders().add(this); }
36
Change Bidirectional Association to Unidirectional You have a two-way association but one class no longer needs features from the other Drop the unneeded end of the association.
37
Motivation Bidirectional associations have complexity –Error prone to maintain –Easy to lead to zombies: objects that should be dead but still hang around because of a reference that was not cleared –Force an interdependency between the two classes
38
Mechanism 1.Examine all the readers of the field that holds the pointer that you wish to remove to see whether the removal is feasible 2.If clients need to use the getter, use Self Encapsulate Field 3.If clients don't need the getter, change each user of the field so that it gets the object in the field another way. 4.When no reader is left in the field, remove all updates to the field, and remove the field. 5.Compile and test every step.
39
Replace Magic Number with Symbolic Constant You have a literal number with a particular meaning Create a constant, name it after the meaning, and replace the number with it.
40
Example double potentialEnergy(double mass, double height) { return mass * 9.81 * height; } double potentialEnergy(double mass, double height) { return mass * GRAVITATIONAL_CONSTANT * height; } static final double GRAVITATIONAL_CONSTANT = 9.81;
41
Encapsulate Field There is a public field Make it private and provide accessors.
42
Example public String _name private String _name; public String getName() {return _name;} public void setName(String arg) {_name = arg;}
43
Encapsulate Collection A method returns a collection Make it return a read-only view and provide add/remove methods.
44
Motivation Getter should not return the collection object itself –Prevent clients from manipulating the contents of the collection without the owning class's knowing what is going on –Avoid revealing too much to clients about the object's internal data structures –Reduce interdependency There should be operations to add and remove elements in owing class
45
Mechanism 1.Add an add and remove method for the collection. 2.Initialize the field to an empty collection 3.Find callers of the setting method. Either modify the setting method to use the add and remove operations or have the clients call those operations instead 4.Find all users of the getter that modify the collection. Change them to use the add and remove methods. 5.When all uses of the getter that modify have been changed, modify the getter to return a read-only view of the collection. 6.Compile and test every step.
46
Example class Person... public Set getCourses() { return _courses; } public void setCourses(Set arg) { _courses = arg; } private Set _courses; … Person kent = new Person(); Set s = new HashSet(); s.add(new Course ("Smalltalk Programming", false)); s.add(new Course ("Appreciating Single Malts", true)); kent.setCourses(s); Assert.equals (2, kent.getCourses().size()); Course refact = new Course ("Refactoring", true); kent.getCourses().add(refact); kent.getCourses().add(new Course ("Brutal Sarcasm", false)); Assert.equals (4, kent.getCourses().size()); kent.getCourses().remove(refact); Assert.equals (3, kent.getCourses().size());
47
Use Add and Remove Method class Person public void addCourse (Course arg) { _courses.add(arg); } public void removeCourse (Course arg) { _courses.remove(arg); } public Set getCourses() { return Collections.unmodifiableSet(_courses); } private Set _courses = new HashSet(); … Person kent = new Person(); kent.addCourse(new Course ("Smalltalk Programming", false)); kent.addCourse(new Course ("Appreciating Single Malts", true));
48
Replace Type Code with Class A class has a numeric type code that does not affect its behavior. Replace the number with a new class.
49
Motivation Better compiler type checks –By providing factory methods for the class, you can statically check that only valid instances are created and that those instances are passed on to the correct objects. Replace the type code with a class only if the type code is pure data –E.g. does not cause different behavior inside a switch statement
50
Mechanism 1.Create a new class for the type code. 2.Modify the implementation of the source class to use the new class 3.For each method on the source class that uses the code, create a new method that uses the new class instead 4.One by one, change the clients of the source class so that they use the new interface. 5.Remove the old interface that uses the codes, and remove the static declarations of the codes. 6.Compile and test every step.
51
Example class Person { public static final int O = 0; public static final int A = 1; public static final int B = 2; public static final int AB = 3; private int _bloodGroup; public Person (int bloodGroup) { _bloodGroup = bloodGroup; } public void setBloodGroup(int arg) { _bloodGroup = arg; } public int getBloodGroup() { return _bloodGroup; }
52
Use Classes Instead class Person... public static final int O = BloodGroup.O.getCode(); public static final int A = BloodGroup.A.getCode(); public static final int B = BloodGroup.B.getCode(); public static final int AB = BloodGroup.AB.getCode(); public Person (int bloodGroup) { _bloodGroup = BloodGroup.code(bloodGroup); } public int getBloodGroup() { return _bloodGroup.getCode(); } public void setBloodGroup(int arg) { _bloodGroup = BloodGroup.code (arg); } … class BloodGroup { public static final BloodGroup O = new BloodGroup(0); public static final BloodGroup A = new BloodGroup(1); public static final BloodGroup B = new BloodGroup(2); public static final BloodGroup AB = new BloodGroup(3); private static final BloodGroup[] _values = {O, A, B, AB}; private final int _code; private BloodGroup (int code ) { _code = code; } public int getCode() { return _code; } public static BloodGroup code(int arg) { return _values[arg]; }
53
Replace Type Code with Subclasses You have an immutable type code that affects the behavior of a class. Replace the type code with subclasses.
54
Motivation Presence of case-like conditional statements –switches or if-then-else constructs. –execute different code depending on the value of the type code Moves knowledge of the variant behavior from clients of the class to the class itself Valuable when variants keep changing
55
Mechanism 1.Self-encapsulate the type code 2.For each value of the type code, create a subclass. Override the getting method of the type code in the subclass to return the relevant value 3.Remove the type code field from the superclass. Declare the accessors for the type code as abstract. 4.Compile and test every step.
56
Example class Employee... private int _type; static final int ENGINEER = 0; static final int SALESMAN = 1; static final int MANAGER = 2; Employee (int type) { _type = type; } abstract int getType(); static Employee create(int type) { switch (type) { case ENGINEER: return new Engineer(); case SALESMAN: return new Salesman(); case MANAGER: return new Manager(); default: throw new IllegalArgumentException("Incorrect type code value"); }
57
Replace Type Code with State/Strategy You have a type code that affects the behavior of a class, but you cannot use subclassing. Replace the type code with a state object.
58
Mechanism 1.Self-encapsulate the type code 2.Create a new class, and name it after the purpose of the type code. This is the state object. 3.Create an abstract query in the state object to return the type code. Create overriding queries of each state object subclass to return the correct type code. 4.Create a field in the old class for the new state object 5.Adjust the type code query on the original class to delegate to the state object 6.Adjust the type code setting methods on the original class to assign an instance of the appropriate state object subclass. 7.Compile and test every step.
59
Example class Employee... private int _type; static final int ENGINEER = 0; static final int SALESMAN = 1; static final int MANAGER = 2; Employee (int type) { _type = type; } class Employee... void setType(int arg) { _type = EmployeeType.newType(arg); } class EmployeeType... static EmployeeType newType(int code) { switch (code) { case ENGINEER: return new Engineer(); case SALESMAN: return new Salesman(); case MANAGER: return new Manager(); default: throw new IllegalArgumentException("Incorrect Employee Code"); } static final int ENGINEER = 0; static final int SALESMAN = 1; static final int MANAGER = 2;
60
Replace Subclass with Fields You have subclasses that vary only in methods that return constant data. Change the methods to superclass fields and eliminate the subclasses.
61
Example abstract class Person { abstract boolean isMale(); abstract char getCode();... class Male extends Person { boolean isMale() {return true;} char getCode() {return 'M';} } class Female extends Person { boolean isMale() {return false;} char getCode() {return 'F';} } class Person static Person createMale(){ return new Person(true, 'M'); } private final boolean _isMale; private final char _code;
Similar presentations
© 2025 SlidePlayer.com. Inc.
All rights reserved.