Catalog of Refactoring (3) Organizing Data. Catalog Self Encapsulate Field Replace Data Value with Object Change Value to Reference Change Reference to.

Slides:



Advertisements
Similar presentations
Identity and Equality Based on material by Michael Ernst, University of Washington.
Advertisements

L3:CSC © Dr. Basheer M. Nasef Lecture #3 By Dr. Basheer M. Nasef.
Composition CMSC 202. Code Reuse Effective software development relies on reusing existing code. Code reuse must be more than just copying code and changing.
Written by: Dr. JJ Shepherd
Intro to OOP with Java, C. Thomas Wu Inheritance and Polymorphism
Software Engineering Key Refactorings
George Blank University Lecturer. CS 602 Java and the Web Object Oriented Software Development Using Java Chapter 4.
1 Chapter 4 Language Fundamentals. 2 Identifiers Program parts such as packages, classes, and class members have names, which are formally known as identifiers.
CS2200 Software Development Lecture: Object class A. O’Riordan, 2008.
© 2006 Pearson Addison-Wesley. All rights reserved4-1 Chapter 4 Data Abstraction: The Walls.
Introduction to Refactoring Excerpted from ‘What is Refactoring?’ by William C. Wake and Refactoring: Improving the Design of Existing Code by Martin Fowler.
REFACTORING Improving the Design of Existing Code Atakan Şimşek e
© 2006 Pearson Addison-Wesley. All rights reserved4-1 Chapter 4 Data Abstraction: The Walls.
Refactoring Encapsulation and Unit Testing Lesson Two: Encapsulation and Unit Testing.
(c) University of Washingtonhashing-1 CSC 143 Java Hashing Set Implementation via Hashing.
REFACTORING Lecture 4. Definition Refactoring is a process of changing the internal structure of the program, not affecting its external behavior and.
Introduction to Object Oriented Programming. Object Oriented Programming Technique used to develop programs revolving around the real world entities In.
Refactoring Cristescu Marilena. Definitions Loose Usage: Reorganize a program(or something) As a noun: a change made to the internal structure of some.
Programming in Java Unit 2. Class and variable declaration A class is best thought of as a template from which objects are created. You can create many.
By Nicholas Policelli An Introduction to Java. Basic Program Structure public class ClassName { public static void main(String[] args) { program statements.
The Java Programming Language
Hello.java Program Output 1 public class Hello { 2 public static void main( String [] args ) 3 { 4 System.out.println( “Hello!" ); 5 } // end method main.
Checking Equality of Reference Variables. Arrays and objects are both “reference” types n They are allocated a chunk of memory in the address space n.
Best Practices. Contents Bad Practices Good Practices.
Refactoring1 Improving the structure of existing code.
Page: 1 การโปรแกรมเชิงวัตถุด้วยภาษา JAVA บุรินทร์ รุจจนพันธุ์.. ปรับปรุง 15 มิถุนายน 2552 Keyword & Data Type มหาวิทยาลัยเนชั่น.
Refactoring Deciding what to make a superclass or interface is difficult. Some of these refactorings are helpful. Some research items include Inheritance.
Design Patterns Gang Qian Department of Computer Science University of Central Oklahoma.
CSSE501 Object-Oriented Development. Chapter 4: Classes and Methods  Chapters 4 and 5 present two sides of OOP: Chapter 4 discusses the static, compile.
1.  At the end of this slide, student able to:  Object-Oriented Programming  Research on OOP features.  Do a code walkthrough to examine the implementation.
Topic 1 Object Oriented Programming. 1-2 Objectives To review the concepts and terminology of object-oriented programming To discuss some features of.
Chapter 10 Defining Classes. The Internal Structure of Classes and Objects Object – collection of data and operations, in which the data can be accessed.
Today’s Agenda  More refactoring patterns Software Testing and Maintenance 1.
Refactoring 2. Admin Blackboard Quiz Acknowledgements Material in this presentation was drawn from Martin Fowler, Refactoring: Improving the Design of.
Chapter 6 Introduction to Defining Classes. Objectives: Design and implement a simple class from user requirements. Organize a program in terms of a view.
Programming with Java © 2002 The McGraw-Hill Companies, Inc. All rights reserved. 1 McGraw-Hill/Irwin Chapter 5 Creating Classes.
COP3502 Programming Fundamentals for CIS Majors 1 Instructor: Parisa Rashidi.
Chapter 5 Objects and Classes Inheritance. Solution Assignments 3 & 4 Review in class…..
08 Encapsulation and Abstraction. 2 Contents Defining Abstraction Levels of Abstraction Class as Abstraction Defining a Java Class Instantiating a Class.
Chapter 5 Introduction to Defining Classes
Software Construction and Evolution - CSSE 375 Making Method Calls Simpler Shawn and Steve Below – “Be the character!” The late acting teacher Lee Strasberg.
COP3502 Programming Fundamentals for CIS Majors 1 Instructor: Parisa Rashidi.
1 Software Maintenance and Evolution CSSE 575: Session 3, Part 3 Dealing with Generalization Steve Chenoweth Office Phone: (812) Cell: (937)
Liang, Introduction to Java Programming, Eighth Edition, (c) 2011 Pearson Education, Inc. All rights reserved Objects and Classes.
 In the java programming language, a keyword is one of 50 reserved words which have a predefined meaning in the language; because of this,
CSSE 375 Organizing Data – Part 2 Shawn and Steve Continue the same quiz!
Refactoring1 Improving the structure of existing code.
Chapter 11: Advanced Inheritance Concepts. Objectives Create and use abstract classes Use dynamic method binding Create arrays of subclass objects Use.
Quick Review of OOP Constructs Classes:  Data types for structured data and behavior  fields and methods Objects:  Variables whose data type is a class.
Written by: Dr. JJ Shepherd
Refactoring Constants and Variables Lesson Three: Constants and Variables.
CSSE 375 Organizing Data – Part 1 Shawn and Steve Q1.
REFACTORING CHANGE VALUE TO REFERENCE SUBSTITUTE ALGORITHM REPLACE CONDITIONAL WITH POLYMORHPISM.
© 2006 Pearson Addison-Wesley. All rights reserved 1-1 Chapter 1 Review of Java Fundamentals.
Software Construction and Evolution - CSSE 375 Dealing with Generalization Steve and Shawn Left – In the 1990 movie “The Freshman,” Matthew Broderick,
Refactoring. DCS – SWC 2 Refactoring ”A change made to the internal structure of software to make it easier to understand and cheaper to modify without.
Module 9. Dealing with Generalization Course: Refactoring.
Reference Types CSE301 University of Sunderland Harry R Erwin, PhD.
Chapter 9: Continuing Classes By Matt Hirsch. Table Of Contents 1.Static Fields and Methods 2.Inheritance I. Recycle Code with Inheritance II. Overriding.
Classes CS 162 (Summer 2009). Parts of a Class Instance Fields Methods.
Chapter 5 Introduction to Defining Classes Fundamentals of Java.
Catalog of Refactoring (6) Making Method Calls Simpler.
Catalog of Refactoring
Catalog of Refactoring
Refactoring Methods: Kevin Murphy.
Java Programming Language
Improving the structure of existing code
OO Design with Inheritance
CS 112 Programming 2 Lecture 02 Abstract Classes & Interfaces (2)
CS 240 – Advanced Programming Concepts
Presentation transcript:

Catalog of Refactoring (3) Organizing Data

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

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.

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

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

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; }

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

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

Replace Data Value with Object You have a data item that needs additional data or behavior. Turn the data item into an object.

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.

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

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

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.

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.

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.

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

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

Change Reference to Value You have a reference object that is small, immutable, and awkward to manage. Turn it into a value object.

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

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.

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

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.

Motivation Array should be used only to contain a collection of similar objects in some order

Example String[] row = new String[3]; row [0] = "Liverpool"; row [1] = "15"; Performance row = new Performance(); row.setName("Liverpool"); row.setWins("15");

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.

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

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.

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(); }

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

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

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.

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.

Example class Order... Customer getCustomer() { return _customer; } void setCustomer (Customer arg) { _customer = arg; } Customer _customer;

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

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); }

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.

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

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.

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.

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;

Encapsulate Field There is a public field Make it private and provide accessors.

Example public String _name private String _name; public String getName() {return _name;} public void setName(String arg) {_name = arg;}

Encapsulate Collection A method returns a collection Make it return a read-only view and provide add/remove methods.

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

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.

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());

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));

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.

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

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.

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; }

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]; }

Replace Type Code with Subclasses You have an immutable type code that affects the behavior of a class. Replace the type code with subclasses.

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

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.

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"); }

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.

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.

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;

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.

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;