Refactoring to Patterns CSE301 University of Sunderland Harry R Erwin, PhD.

35
Refactoring to Patterns CSE301 University of Sunderland Harry R Erwin, PhD

Transcript of Refactoring to Patterns CSE301 University of Sunderland Harry R Erwin, PhD.

Page 1: Refactoring to Patterns CSE301 University of Sunderland Harry R Erwin, PhD.

Refactoring to Patterns

CSE301

University of Sunderland

Harry R Erwin, PhD

Page 2: Refactoring to Patterns CSE301 University of Sunderland Harry R Erwin, PhD.

Introduction

• Want to use a pattern in an existing programme, but don’t know how to do it?

• Don’t panic—this lecture will discuss how to refactor a programme to introduce a pattern.

• Resource:– Kerievsky, J., 2005, Refactoring to Patterns,

Addison-Wesley.

Page 3: Refactoring to Patterns CSE301 University of Sunderland Harry R Erwin, PhD.

Topics

• Standard patterns used in refactoring• Creation—creation methods, factory, builder, and

singleton• Simplification—composition, strategy, decorator,

state, composite, command• Generalization—template, composite, observer,

adapter, interpreter• Protection—singleton, null object, generalization• Accumulation (visitor) patterns—at the end if time is

available.• Miscellany—utilities

Page 4: Refactoring to Patterns CSE301 University of Sunderland Harry R Erwin, PhD.

Standard Patterns

• These two patterns are used in refactoring, but are never clearly defined:– Method Object or Functor– Reference and Value Objects

Page 5: Refactoring to Patterns CSE301 University of Sunderland Harry R Erwin, PhD.

Method Object or Functor

• It is possible to make a method into a class instance. This allows you to – pass it to other methods as an object, – store it in a jump table, – save it in a log file, – save it in an undo queue, and – prioritise it for execution in a priority queue.

Page 6: Refactoring to Patterns CSE301 University of Sunderland Harry R Erwin, PhD.

Functor Implementation

• The class has constructors or factory methods for each possible set of arguments.

• You can define default values for arguments if you’re careful.

• It also has an execute()/do()/perform()/etc. method for performing the function. This method can accept additional arguments.

• You can define other methods for converting the results to a string, etc.

Page 7: Refactoring to Patterns CSE301 University of Sunderland Harry R Erwin, PhD.

Reference and Value Objects

• A reference object is a business object like customer or account.

• Each reference object stands for something in the real world. You use object identity (==) to test for equality. (equals() defaults to this test.) You may have to use a factory method to ensure non-duplication of reference objects. Consider this in designing your system.

• A value object stands for something like money, defined entirely through its data values (usually immutable and declared final). You override equals() and hashCode() and use equals() to test for equality.

Page 8: Refactoring to Patterns CSE301 University of Sunderland Harry R Erwin, PhD.

Overriding equals() for Value Objects

• The signature of equals() is– public boolean equals(Object obj)

• You should do the following:– Test obj for instanceof(YourClass). This will

return true if obj belongs to your class or a subclass. You may need to watch out for subclassing here.

– Typecast obj to YourClass– Compare the field values– Return true if everything matches, else return false.

Page 9: Refactoring to Patterns CSE301 University of Sunderland Harry R Erwin, PhD.

Overriding hashCode() for Value Objects

• The signature of hashCode() is– public int hashCode();

• If two objects are equal(), they must have the same hashCode() values.

• There are at least three approaches:– Compute the hashCode() of the component fields (you may

need to autobox primitive types) and xor them together. – Compute the hashCode() of toString() applied to the

component fields (you may need to autobox primitive types) and xor them together.

– Compute a hashCode() arithmetically. See Bloch, Effective Java Programming Guide (Addison-Wesley) for help.

Page 10: Refactoring to Patterns CSE301 University of Sunderland Harry R Erwin, PhD.

Creation Patterns

• These refactorings address some of the common problems in this area.

• The Creation Method pattern is new. It describes the replacement of a collection of constructors with static methods that do the same thing more clearly.

• Factory classes usually implement one or more Creation Methods.

Page 11: Refactoring to Patterns CSE301 University of Sunderland Harry R Erwin, PhD.

“Replace Constructors with Creation Methods”

• Multiple constructors for a class make it hard to choose the right one. Perhaps the class instance is supposed to be a reference object.

• Solution: replace the constructors with static (or non-static) creation methods that are easier to understand. Make the constructors private.

• These are often called factory methods.• It does make object creation for that class non-

standardized, since the new operator is not available.

Page 12: Refactoring to Patterns CSE301 University of Sunderland Harry R Erwin, PhD.

“Move Creation Knowledge to Factory”

• Data and code to instantiate a class is sprawled across many classes.

• Move the creation knowledge into a single Factory class to encapsulate the creation logic and client preferences. Start by writing a static creation method. Then create a Factory class and move the creation method into it. Chase down all the compilation problems that result. You’re done.

• Avoid overuse of the Factory pattern. That will overcomplicate your design.

Page 13: Refactoring to Patterns CSE301 University of Sunderland Harry R Erwin, PhD.

“Encapsulate Classes With Factory”

• Clients directly instantiate classes that reside in one package and implement a common interface.

• Solution: Make the class constructors non-public and introduce a Factory class to create them. (See java.util.Collections.) Improves encapsulation.

• First use Extract Method on constructor calls and then Move Method to move them to the Factory class. Make the constructors non-public.

• Negatives: new creation methods are needed for new kinds of instances. Also limits client customization as source code is unavailable to clients.

Page 14: Refactoring to Patterns CSE301 University of Sunderland Harry R Erwin, PhD.

“Inline Singleton”

• Too many globals are bad, so you’ve become addicted to Singleton classes. Your code needs access to a unique object, but doesn’t need a global point of access. This is a normal situation.

• Solution: Move the Singleton’s features to a class that stores and provides access to the object. Delete the Singleton.

• Use Move Method and Move Field to do this.• This does complicate a design when objects need

to be passed down chains.

Page 15: Refactoring to Patterns CSE301 University of Sunderland Harry R Erwin, PhD.

Simplification

• Most of your code starts out complicated. The goal of these refactorings is to redesign it for simplicity.

Page 16: Refactoring to Patterns CSE301 University of Sunderland Harry R Erwin, PhD.

“Compose Method”

• You can’t understand a method’s logic.• Solution: Transform the logic into a small number

of intention-revealing steps at the same level of detail. Will communicate and simplify what the method does.

• I use Extract Method heavily to do this, particularly for GUIs.

• Leads to many small methods of 5-10 lines. Can make debugging a chore.

Page 17: Refactoring to Patterns CSE301 University of Sunderland Harry R Erwin, PhD.

“Replace Conditional Logic with Strategy”

• Sign: lots of conditional logic in a method.• Solution: Create a Strategy for each variant

and delegate the computing to the Strategy instance. The Strategies encapsulate the various possible conditions. Think about using the Prototype pattern to avoid creation overhead.

• It can complicate a design or algorithm.

Page 18: Refactoring to Patterns CSE301 University of Sunderland Harry R Erwin, PhD.

“Move Embellishment to Decorator”

• Code embellishes a class’s core responsibility.• Solution: Move it to a Decorator. Note that the

Decorator class has to implement the public interface of the decorated class, so you may want to refactor towards this solution, rather than all the way.

• First create the Decorator class (mucho work). Then find where the choice of embellishment is made and instead construct a Decorator around the base class. Note Decorators may be constructed around Decorators.

Page 19: Refactoring to Patterns CSE301 University of Sunderland Harry R Erwin, PhD.

“Replace State-Altering Conditionals with State”

• The conditional expressions controlling an object’s state are complex.

• Solution: Replace the conditionals with State classes that represent specific states.

• Find where the state fields are maintained and encapsulate them in an abstract State class. Use Extract Subclass to get concrete States. Use Move Method to delegate responsibilities to the subclasses. Identify state transitions and switch the State instance there. Consider using Prototype.

• NB: State and Strategy patterns are different!

Page 20: Refactoring to Patterns CSE301 University of Sunderland Harry R Erwin, PhD.

“Replace Conditional Dispatcher with Command”

• Conditional logic (if then else or switch) is used to dispatch requests and execute actions.

• Solution: Create a command for each action. Store in a collection and replace the conditional logic with code to fetch and execute commands.

• This is done using Extract Method over and over again. Then you Extract Class on each method. Create a Command interface and an execute() method for that interface. Build a CommandMap. Finally replace the conditional dispatcher with logic that selects the Command from the CommandMap and executes it.

Page 21: Refactoring to Patterns CSE301 University of Sunderland Harry R Erwin, PhD.

Generalization

• Converts specific code to general-purpose code.

• Removes duplicated code.

• Simplifies and clarifies code.

Page 22: Refactoring to Patterns CSE301 University of Sunderland Harry R Erwin, PhD.

“Form Template Method”

• Two methods in subclasses perform similar but not identical steps in the same order.

• Solution: Generalize the two methods by extracting their steps into methods with identical signatures. Use Extract Method to do this.

• Then pull up the generalized methods to form a template method in their base class.

Page 23: Refactoring to Patterns CSE301 University of Sunderland Harry R Erwin, PhD.

“Replace Hard-Coded Notifications with Observer”

• Subclasses are defined and hard-coded to notify an instance of another class of their changes. The notified class varies with the subclass selected.

• Solution: Remove the subclasses by making their superclass capable of notifying any class that implements an Observer pattern. The receiver classes implement that pattern and register for updates.

• You may need to move logic from the subclass to its receiver to allow this to work.

Page 24: Refactoring to Patterns CSE301 University of Sunderland Harry R Erwin, PhD.

“Unify Interfaces with Adapter”

• Clients interact with two alternative classes, one of which has a preferred or mandatory interface.

• Solution: Unify the interfaces with an adapter.• Apply Extract Interface to the class with the preferred

interface.• Apply Extract Class to the class that uses the

alternatives. That creates a primitive Adapter.• Use the Adapter instead of the hidden classes. Use

Move Method to make this happen.• Have the Adapter ‘implement’ the common interface.

Page 25: Refactoring to Patterns CSE301 University of Sunderland Harry R Erwin, PhD.

Protection

• These refactorings improve the protection of existing code.

Page 26: Refactoring to Patterns CSE301 University of Sunderland Harry R Erwin, PhD.

“Replace Type Code with Class”

• A field’s type (primitive type or String) does not protect it from unsafe assignments and invalid equality comparisons. This can be a particular problem for ‘enumerations’.

• Solution: Make the type of the field a class (or enum) so you can constrain those unsafe operations.

• Apply Self Encapsulate Field so you use getters and setters to access the field—which becomes private.

• Create a concrete class to hold the field. • Instantiate in the using class and switch over.

Page 27: Refactoring to Patterns CSE301 University of Sunderland Harry R Erwin, PhD.

“Limit Instantiation with Singleton”

• Your code creates multiple instances of an object and then uses too much memory or slows down.

• Solution: Replace the multiple instances with a Singleton

• Make sure the state of the object can be shared.• Replace the constructor with a creation method. Then

insert the standard Singleton logic.• You also might do this with a Map if you still need

more than one object. The Singleton contains the Map. Note you can decorate the Map with ‘immutable’ when you return it so that it’s read-only.

Page 28: Refactoring to Patterns CSE301 University of Sunderland Harry R Erwin, PhD.

“Introduce Null Object”• Your code has lots of logic for dealing with a null field or

variable.• Solution: Replace the null value with a Null Object, which has

all the necessary methods defined and simply does nothing. There are a lot of examples of this in javax.swing.

• Create a NullObject class by applying Extract Subclass and/or Extract Interface.

• Find out what the null checks do in your code and have the NullObject class do them.

• Whenever the field is set to null, set it to an instance of the NullObject.

• You might save a copy of the NullObject in the superclass and either share it or clone it to cut execution time.

• Eventually, you will remove the null checks.

Page 29: Refactoring to Patterns CSE301 University of Sunderland Harry R Erwin, PhD.

Miscellany

• Low-level refactorings– Chain Constructor– Unify Interface– Extract Parameter

Page 30: Refactoring to Patterns CSE301 University of Sunderland Harry R Erwin, PhD.

“Chain Constructor”

• You have multiple constructors with duplicate code.

• Solution: Chain them together• Look for duplication and figure out ways

that the constructors can call each other to eliminate it.

• Make any constructors that are internal to this process non-public.

• See the BankingDataReader for an example.

Page 31: Refactoring to Patterns CSE301 University of Sunderland Harry R Erwin, PhD.

“Unify Interface”

• You need a superclass or superinterface to have the same interface as a subclass.

• Solution: Find all public methods in the subclass and put null versions in the superclass.

• Often a station on a road to someplace else.

Page 32: Refactoring to Patterns CSE301 University of Sunderland Harry R Erwin, PhD.

“Extract Parameter”• A method or constructor assigns a field to a locally

instantiated value.• Solution: Assign the field to a parameter supplied by a

client by extracting one-half of the assignment statement to a parameter.

• The client supplies a null variable to fill in, and the constructor or method sets it to the value.

• It’s like a website cookie. If the variable is final, this can even ensure that it is preserved unchanged until it is needed later.

• Often used in ‘swap’ algorithms.

Page 33: Refactoring to Patterns CSE301 University of Sunderland Harry R Erwin, PhD.

Accumulation

• A lot of code accumulates information for a summary report– Move Accumulation to Collecting Parameter– Move Accumulation to Visitor

Page 34: Refactoring to Patterns CSE301 University of Sunderland Harry R Erwin, PhD.

“Move Accumulation to Collecting Parameter”

• A bulky method exists that accumulates information in a local variable. What to do?

• A Collecting Parameter is an object that is passed to methods to accumulate information from them.

• Often used with Compose Method.• Often used for composites.• Used in JUnit to accumulate test result

information.

Page 35: Refactoring to Patterns CSE301 University of Sunderland Harry R Erwin, PhD.

“Move Accumulation to Visitor”

• Like the previous refactoring, but the classes are heterogeneous.

• Move the accumulation task to a Visitor. • Operates on an object structure, each object providing a

‘double dispatch’. The visitor is passed to the object using an accept(Visitor v) method defined by the object, and the object calls v.visitType(this), passing itself to the appropriate visit method provided by the Visitor.

• Visitor is rarely needed, but when needed it’s the only good solution. It’s needed when you have to run several algorithms on the object structure. Think XML, generating a test report, or logging.