25
Refactoring Ch7 Moving Features Btw Objects Chen Jing Fung 2011/5/18 http://sourcemaking.com/refactoring

Refactoring-ch7 moving feature btw objects

Embed Size (px)

DESCRIPTION

decide how to put responsibilities? There are 8 methods including move field or move method, Extract Class vs. Inline Class, Hide Delegate vs. Remove Middle Man, Introduce Foreign Method & Introduce Local Extension (subclass or wrapper).

Citation preview

Page 1: Refactoring-ch7 moving feature btw objects

Refactoring – Ch7

Moving Features Btw Objects

Chen Jing Fung

2011/5/18

http://sourcemaking.com/refactoring

Page 2: Refactoring-ch7 moving feature btw objects

Outline

• Moving Features Between Objects

Decide where to put responsibilities (refactoring?)

– Move Method vs. Move Field

– Extract Class vs. Inline Class

– Hide Delegate vs. Remove Middle Man

– Introduce Foreign Method & Introduce Local Extension

• Summary

Page 3: Refactoring-ch7 moving feature btw objects

Move Method

• Examine all features used by the method on Class S. – Consider what situation should be move? (one or more methods ?)

• Check the sub- & superclasses of Class S – unless Class S and Class T are both the polymorphism

• Naming the method’ in Class T (more sense)

• Copy the method code from Class S to Class T. Adjust the method’ to fit Class T

– Reference back Class S? just send a parameter from Class S

– Exception handler at Class S or Class T?

• Compile Class T

• Determine how to reference back Class S – Class T has existing field or method

– If no existing, create a method in Class T

– or Create a new field (store the target object) in Class S [temporariness]

• Turn the source method into a delegating method

• Compile and test

• Decide whether to remove the source method or retain it as a delegating method

– retain as a delegating method => many references

– If remove the source method, replace all the references and make the reference to link Class T

• Compile and test

Class S

Class T

Method()

Class S

Class T

Method’()

Mechanics:

Motivation:

In Class S, method() used more features from Class T

Page 4: Refactoring-ch7 moving feature btw objects

Example for Move Method (1)

class Account...

double overdraftCharge() {

if (_type.isPremium()) {

double result = 10;

if (_daysOverdrawn > 7)

result += (_daysOverdrawn - 7) * 0.85;

return result;

}

else return _daysOverdrawn * 1.75;

}

double bankCharge() {

double result = 4.5;

if (_daysOverdrawn > 0)

result += overdraftCharge();

return result;

}

private AccountType _type;

private int _daysOverdrawn;

Original code class AccountType...

double overdraftCharge(int daysOverdrawn) {

if (isPremium()) {

double result = 10;

if (daysOverdrawn > 7)

result += (daysOverdrawn - 7) * 0.85;

return result;

}

else return daysOverdrawn * 1.75;

}

class Account...

double overdraftCharge() {

return _type.overdraftCharge(_daysOverdrawn);

}

class Account...

double bankCharge() {

double result = 4.5;

if (_daysOverdrawn > 0)

result += _type.overdraftCharge(_daysOverdrawn);

return result;

} a parameter

delegation

Many accounts join

& Every account has

self-overdraftCharge

=> refactoring

the original code

Remain

individual

accounts

Class T Class S

Page 5: Refactoring-ch7 moving feature btw objects

Example for Move Method (2)

class AccountType...

double overdraftCharge(Account account) {

if (isPremium()) {

double result = 10;

if (account.getDaysOverdrawn() > 7)

result += (account.getDaysOverdrawn() - 7) * 0.85;

return result;

}

else return account.getDaysOverdrawn() * 1.75;

}

class AccountType...

double overdraftCharge(int daysOverdrawn) {

if (isPremium()) {

double result = 10;

if (daysOverdrawn > 7)

result += (daysOverdrawn - 7) * 0.85;

return result;

}

else return daysOverdrawn * 1.75;

}

Reference

a parameter

Reference

a field

(need several

features)

If there are too

many features

=> future

refactoring

Page 6: Refactoring-ch7 moving feature btw objects

Move Field

• Motivation – Class S’s field(be used by more methods) is used frequently in

Class T • If those methods seem sensible where they are => Move Field

– When doing Extract Class, must do Move Field

• Mechanics – Public field => use Encapsulate Field

• If the field be accessed frequently by many methods => use Self Encapsulate Field

– Compile and test

– Create a field in Class T with getting & setting methods

– Compile Class T

– Determine how to reference back Class S • Class T has existing field or method

• If no existing, create a method in Class T

• or Create a new field (store the target object) in Class S [temporariness]

– Remove Class S’s field

– Replace all field reference in Class S and chose the appropriate link for Class T

• Access field is by variable => replace the reference with a call to the target object’s getting method

• Access field by assignments => replace the reference with a call to the setting method

• If field is not private => look in all subclass S for reference

– Compile and test

Class S

Class T

Field

Class S

Class T

Field’

Page 7: Refactoring-ch7 moving feature btw objects

Example for Move field

class Account...

private AccountType _type;

private double _interestRate;

double interestForAmount_days (double amount, int days) {

return _interestRate * amount * days / 365;

}

class AccountType...

private double _interestRate;

void setInterestRate (double arg) {

_interestRate = arg;

}

double getInterestRate () {

return _interestRate;

}

a field

accessor

pair

private double _interestRate;

double interestForAmount_days (double amount, int days) {

return _type.getInterestRate() * amount * days / 365;

}

class Account...

Page 8: Refactoring-ch7 moving feature btw objects

Example for Move field – Self Encapsulate Field

class Account...

private AccountType _type;

private double _interestRate;

double interestForAmount_days (double amount, int days) {

return getInterestRate() * amount * days / 365;

}

private void setInterestRate (double arg) {

_interestRate = arg;

}

private double getInterestRate () {

return _interestRate;

}

double interestForAmountAndDays (double amount, int days) {

return getInterestRate() * amount * days / 365;

}

private void setInterestRate (double arg) {

_type.setInterestRate(arg);

}

private double getInterestRate () {

return _type.getInterestRate();

}

A lot of methods use

the interest rate field

=> refactoring for

easy expanding class

Asscessor =>

Self Encapsulate

Field (redirection)

accessor

pair

a field

Page 9: Refactoring-ch7 moving feature btw objects

Extract Class

• Motivation – A class is too big to understand easily

• Many methods & a lot of data

• Mechanics – Decide how to split the responsibilities of the class

– Create a new class to express the split-off responsibility • Maybe rename the old class

– Make a link from the old to the new class • May need a 2-way link

– Use Move Field on each field you wish to move

– Compile and test after each move

– Use Move Method to move methods over from old to new • Start with low-level methods(few call) & build to the higher level

– Compile and test after each move

– Review & reduce the interfaces of each class • 2-way link => one way

– Decide whether to expose the new class • as a reference object or as an immutable value object (ch.8)

Person

name

officeAreaCode

officeNumber

getTelephoneNumber

Person

name

getTelephoneNumber

Telephone Number

areaCode

number

getTelephoneNumber

officeTelephone

Page 10: Refactoring-ch7 moving feature btw objects

class Person...

public String getName() {

return _name;

}

public String getTelephoneNumber() {

return ("(" + _officeAreaCode + ") " +

_officeNumber);

}

String getOfficeAreaCode() {

return _officeAreaCode;

}

void setOfficeAreaCode(String arg) {

_officeAreaCode = arg;

}

String getOfficeNumber() {

return _officeNumber;

}

void setOfficeNumber(String arg) {

_officeNumber = arg;

}

private String _name;

private String _officeAreaCode;

private String _officeNumber;

class TelephoneNumber {

}

class Person ...

private TelephoneNumber _officeTelephone = new TelephoneNumber();

accessor

pair

New

class

class TelephoneNumber {

String getAreaCode() {

return _areaCode;

}

void setAreaCode(String arg) {

_areaCode = arg;

}

private String _areaCode;

}

accessor

pair

class Person...

public String getTelephoneNumber() {

return ("(" + getOfficeAreaCode() + ")

" + _officeNumber);

}

String getOfficeAreaCode() {

return _officeTelephone.getAreaCode();

}

void setOfficeAreaCode(String arg) {

_officeTelephone.setAreaCode(arg);

}

accessor

pair

Example for Extract Class (1)

Move Field

Intermediate

process to

fool complier

Page 11: Refactoring-ch7 moving feature btw objects

Example for Extract Class (2)

class Person...

public String getName() {

return _name;

}

public String getTelephoneNumber(){

return

_officeTelephone.getTelephoneNumber();

}

TelephoneNumber getOfficeTelephone() {

return _officeTelephone;

}

private String _name;

private TelephoneNumber _officeTelephone =

new TelephoneNumber();

class TelephoneNumber...

public String getTelephoneNumber() {

return ("(" + _areaCode + ") " + _number);

}

String getAreaCode() {

return _areaCode;

}

void setAreaCode(String arg) {

_areaCode = arg;

}

String getNumber() {

return _number;

}

void setNumber(String arg) {

_number = arg;

}

private String _number;

private String _areaCode;

Move Method

Page 12: Refactoring-ch7 moving feature btw objects

Inline Class

• Motivation – A class isn’t doing very much

• Maybe the result of refactoring to move other responsibilities out of class

• Mechanics (Move all its features into another class and delete it) – Declare the public protocol of the source class

onto the absorbing class. Delegate all these methods to the source class

• Source class methods have a separate interface => use Extract Interface(ch.11) before inlining

– Change all references (source class -> absorbing class)

• Source class: Declare private to out-of-package reference & change name to fool compiler

– Compile and test

– Use Move Method and Move Field to move features form the source class to the absorbing class (until nothing is left)

– Delete non-necessary class

Person

name

officeAreaCode

officeNumber

getTelephoneNumber

Person

name

getTelephoneNumber

Telephone Number

areaCode

number

getTelephoneNumber

officeTelephone

absorbing class

source class

Page 13: Refactoring-ch7 moving feature btw objects

class Person...

public String getName() {

return _name;

}

public String getTelephoneNumber(){

return _officeTelephone.getTelephoneNumber();

}

TelephoneNumber getOfficeTelephone() {

return _officeTelephone;

}

private String _name;

private TelephoneNumber _officeTelephone = new

TelephoneNumber();

class TelephoneNumber...

public String getTelephoneNumber() {

return ("(" + _areaCode + ") " + _number);

}

String getAreaCode() {

return _areaCode;

}

void setAreaCode(String arg) {

_areaCode = arg;

}

String getNumber() {

return _number;

}

void setNumber(String arg) {

_number = arg;

}

private String _number;

private String _areaCode;

String getAreaCode() {

return _officeTelephone.getAreaCode();

}

void setAreaCode(String arg) {

_officeTelephone.setAreaCode(arg);

}

String getNumber() {

return _officeTelephone.getNumber();

}

void setNumber(String arg) {

_officeTelephone.setNumber(arg);

}

Person martin = new Person();

martin.getOfficeTelephone().setAreaCode ("781");

Person martin = new Person();

martin.setAreaCode ("781");

interface

interface

Declare all the visible

methods about

TelephoneNumber class Person...

Example for Inline class

Page 14: Refactoring-ch7 moving feature btw objects

Hide Delegate Person

getDepartment

Department

getMange

r

Client

Class

Client

Class

Person

getManager Department

Client Server

Method()

Delegate

Method()

Delegate.method()

Client calls delegate class by server

object

Server builds all methods (delegate method) to be used by

client

Changes are limited to

Server-side & don’t

propagate to Client-side

The advantage of

encapsulating

Page 15: Refactoring-ch7 moving feature btw objects

Hide Delegate - mechanics

• Mechanics

– Create a simple delegating method (for each

method) on the server

• Client is not the same package as server => make

the delegate method to visibility

– Compile and test after adjustung each method

– If no client needs to access the delegate

anymore, remove the server’s accessor for

the delegate

– Compile and test

Page 16: Refactoring-ch7 moving feature btw objects

Example for Hide Delegate

class Person…

Department _department;

public Department getDepartment() {

return _department;

}

public void setDepartment(Department

arg) {

_department = arg;

}

manager = john.getDepartment().getManager();

client accesses a person's manager =>

get the department first

manager = john.getManager();

client accesses a person's manager

class Person...

Department _department;

public Person getManager() {

return _department.getManager();

}

Original code …

Refactoring…

class Department…

private String _chargeCode;

private Person _manager;

public Department (Person manager) {

_manager = manager;

}

public Person getManager() {

return _manager;

}

class Department...

private Person _manager;

public Department (Person manager) {

_manager = manager;

}

Page 17: Refactoring-ch7 moving feature btw objects

Remove Middle Man

• Motivation – Add too much delegating on the server, so accessing

becomes painful => server class is a middle man

• Mechanics – Create an accessor for the delegate

– For each client use of a delegate, remove the method from server & make the client call the delegate method directly

– Compile and test after each method

Foo

getbar

Bar

getImpValue

Client

Class

Client

Class

Foo

getImpValue

Bar

A class has too

much simple

delegation

Get the caller to

call the delegate

directly

Page 18: Refactoring-ch7 moving feature btw objects

Example for Remove Middle Man(1)

public class Foo {

Bar bar;

public Foo getImpValue(){

return bar.getImpValue();

}

}

public class Foo {

Bar bar;

public Bar getbar() {

return bar;

}

}

Original code … Refactoring…

http://www.jetbrains.com/idea/webhelp/remove-middleman.html

public class Bar {

private Foo impValue1;

public Bar(Foo impValue){

impValue1 = impValue;

}

public Foo getImpValue(){

return impValue1;

}

}

public class Client {

Foo a;

Foo impValue = a.getImpValue();

}

public class Bar {

private Foo impValue1;

public Bar(Foo impValue){

impValue1 = impValue;

}

public Foo getImpValue(){

return impValue1;

}

}

public class Client {

Foo a;

Foo impValue = a.getbar().getImpValue();

}

Page 19: Refactoring-ch7 moving feature btw objects

Example for Remove Middle Man(2)

http://lostechies.com/seanchambers/2009/08/28/refactoring-day-29-remove-middle-man/

public class Consumer {

public AccountManager AccountManager { get; set; }

public Consumer(AccountManager accountManager) {

AccountManager = accountManager;

}

public void Get(int id) {

Account account = AccountManager.GetAccount(id);

}

}

public class Consumer {

public AccountDataProvider AccountDataProvider { get; set; }

public Consumer(AccountDataProvider dataProvider) {

AccountDataProvider = dataProvider;

}

public void Get(int id) {

Account account = AccountDataProvider.GetAccount(id);

}

}

may have a set of “Ghost” classes in code

public class AccountManager {

public AccountDataProvider DataProvider { get; set; }

public AccountManager(AccountDataProvider

dataProvider) {

DataProvider = dataProvider;

}

public Account GetAccount(int id) {

return DataProvider.GetAccount(id);

}

} public class AccountDataProvider {

public Account GetAccount(int id) {

// get account

}

} Original code …

public class AccountDataProvider {

public Account GetAccount(int id) {

// get account

}

}

Refactoring…

“Ghost” classes

Page 20: Refactoring-ch7 moving feature btw objects

Introduce Foreign Method • Motivation

– Want add foreign method in, but can’t change the source

– Create many (> 1~2) foreign methods on a server class or many other classes need the same foreign method => use Introduce Local Extension

• Mechanics – Create a method in the client

class (you need) • The method should access no

feature on client class.

– Make an instance of the server class the first parameter

– Comment the method as “foreign method; should be in server”

• Mark the comment as text to easy refactoring again

Date newStart = new Date (previousEnd.getYear(),

previousEnd.getMonth(),

previousEnd.getDate() + 1);

Date newStart = nextDay(previousEnd);

private static Date nextDay(Date arg) {

return new Date (arg.getYear(),arg.getMonth(),

arg.getDate() +1);

}

Need to add methods

in server class, but

can’t modify it

Create a method in

client class

server

client

Page 21: Refactoring-ch7 moving feature btw objects

Introduce Local Extension

• Motivation – Can’t modify => group the methods

together & using object-oriented techniques(subclass/ warp) to do (local extension)

– Local extension • A separate class & a subtype of extended

class

• [methods & data should be packaged into well-formed units]

• Mechanics – Create an extension class either as a

subclass or a wrapper of the original

– Add converting constructors to the extension

• Constructor takes the original as an argument

• Subclass calls an superclass constructor

• Wrapper sets the delegate field to the argument

– Add new features to the extension

– Replace the original with the extension where needed

– Move any foreign methods defined for this class onto the extension

Client Class

nextDay(Date):Date

Date

MfDate

nextDay():Date

Q: Need to add

methods(>1~2) in server

class, but can’t modify it

Create a new class

that contains these

extra method

Make this extension

class a subclass or

wrapper of the

original

Class mfDate extends Date

{

public nextDay()...

public dayOfYear()...

Subclass

class mfDate {

private Date _original;

Wrapper

(delegation)

Page 22: Refactoring-ch7 moving feature btw objects

Example for Introduce Local Extension -

subclass

class MfDateSub extends Date…

public MfDateSub (String dateString) {

super (dateString);

};

public MfDateSub (Date arg) {

super (arg.getTime());

}

client class...

private static Date nextDay(Date arg) {

// foreign method, should be on date

return new Date (arg.getYear(),arg.getMonth(), arg.getDate() + 1);

}

class MfDateSub extends Date {

public MfDateSub (String dateString) {

super (dateString);

};

public MfDateSub (Date arg) {

super (arg.getTime());

}

Date nextDay() {

return new Date (getYear(),getMonth(), getDate() + 1);

}

}

Move Method

Add a converting constructor

Original code … Refactoring…

Page 23: Refactoring-ch7 moving feature btw objects

Example for Introduce Local Extension

- wrapper

http://hi.baidu.com/hanframe_ip/blog/item/76c3b154d21aac50d0090659.html

class mfDate {

private Date _original;

}

1. Declare a class

public mfDateWarp (String dateString) {

_original = new Date (dateString);

} ;

2. Set the constructors by a delegation

public mfDateWarp (String arg) {

_original = arg;

}

2.1 Set the instance variable

3. Delegate all methods

public int getYear() {

return _original.getYear();

}

public boolean equals (MfDateWrap arg) {

return (toDate().equals(arg.toDate()));

}

client class...

private static Date nextDay(Date arg) {

// foreign method, should be on date

return new Date (arg.getYear(),arg.getMonth(),

arg.getDate() + 1);

}

class MfDate...

Date nextDay() {

return new Date (getYear(),getMonth(),

getDate() + 1);

}

4. Move Method

Put back to class

Page 24: Refactoring-ch7 moving feature btw objects

Problem: Introduce Local

Extension – wrapper

wrapper problem: Can’t alter

Public boolean after (Date arg)

aWrapper.after(aDate)

aDate.after(aWrapper)

Override => hide

wrapper info.

Public boolean equals (Date arg)

cause problem:

equals is symmetric !! public boolean equalsDate

(MfDateWrap arg)

public boolean equalsDate (Date arg)

Page 25: Refactoring-ch7 moving feature btw objects

Summary –

How to put responsibilities ? • Refactoring – basic idea

– Move Field > Move Method

• Class over responsibilities or less – Extract Class V.S Inline Class

• Class uses another Class – Hide Delegate (hide their relationship)

• As Hide delegate causes owner’s interface change in frequency – Remove Middle Man

• As can’t modify a class, but want add… – Introduce Foreign Method (only for 1~2 methods)

– Introduce Local Extension • Subclass vs. wrapper (delegate field)