Or Web version (although it is the complete version), A complementary Refactoring Catalog is available.
Sample code for books and catalogs is JavaScript, Here is a sample code for refactoring in Python based on the catalog
I will leave the detailed explanation to the book and show it with a glue like "Let's do this here"
https://refactoring.com/catalog/changeReferenceToValue.html
Initial code, JavaScript version
class Product {
applyDiscount(arg) {this._price.amount -= arg;}
After refactoring
class Product {
applyDiscount(arg) {
this._price = new Money(this._price.amount - arg, this._price.currency);
}
Initial code
class Product():
def applyDiscount(self, arg):
self._price.amount -= arg
However, when I looked at the book to find out how to define _price, I explained it concretely with class Person and class TelephoneNumber, so I changed it to Gununu.
Initial code, JavaScript version, modified
class Person{
constructor() {
this._telephoneNumber = new TelephoneNumber();
}
get officeAreaCode() {return this._telephoneNumber.areaCode;}
set officeAreaCode(arg) {this._telephoneNumber.areaCode = arg;}
get officeNumber() {return this._telephoneNumber.number;}
set officeNumber(arg) {this._telephoneNumber.number = arg;}
}
class TelephoneNumber{
get areaCode() {return this._areaCode;}
set areaCode(arg) {this._areaCode = arg;}
get number() {return this._number;}
set number(arg) {this._number = arg;}
}
After refactoring
class Person{
constructor() {
this._telephoneNumber = new TelephoneNumber();
}
get officeAreaCode() {return this._telephoneNumber.areaCode;}
set officeAreaCode(arg) {
this._telephoneNumber = new TelephoneNumber(arg, this.officeNumber);
}
get officeNumber() {return this._telephoneNumber.number;}
set officeNumber(arg) {
this._telephoneNumber = new TelephoneNumber(this.officeAreaCode, arg);
}
}
class TelephoneNumber{
constructor(areaCode, number) {
this._areaCode = areaCode;
this._number = number;
}
get areaCode() {return this._areaCode;}
set areaCode(arg) {this._areaCode = arg;}
get number() {return this._number;}
set number(arg) {this._number = arg;}
}
Initial code
class Person():
def __init__(self):
self.__telephoneNumber = TelephoneNumber()
@property
def officeAreaCode(self):
return self.__telephoneNumber.areaCode
@officeAreaCode.setter
def officeAreaCode(self, arg):
self.__telephoneNumber.areaCode = arg
@property
def officeNumber(self):
return self.__telephoneNumber.number
@officeNumber.setter
def officeNumber(self, arg):
self.__telephoneNumber.number = arg
class TelephoneNumber():
def __init__(self):
self.__areaCode = ''
self.__number = ''
@property
def areaCode(self):
return self.__areaCode
@areaCode.setter
def areaCode(self, arg):
self.__areaCode = arg
@property
def number(self):
return self.__number
@number.setter
def number(self, arg):
self.__number = arg
Test code
from unittest import TestCase
class TestPerson(TestCase):
target = Person()
def test_officeAreaCode(self):
self.target.officeAreaCode = '312'
self.assertEqual(self.target.officeAreaCode, '312')
def test_officeNumber(self):
self.target.officeNumber = '555-0142'
self.assertEqual(self.target.officeNumber, '555-0142')
class TestTelephoneNumber(TestCase):
target = TelephoneNumber()
def test_areaCode(self):
self.target.areaCode = '312'
self.assertEqual(self.target.areaCode, '312')
def test_number(self):
self.target.number = '555-0142'
self.assertEqual(self.target.number, '555-0142')
Preparing to remove setters, adding parameters to constructor, adding tests
class TestTelephoneNumber(TestCase):
target = TelephoneNumber()
def test_areaCode(self):
self.target.areaCode = '312'
self.assertEqual(self.target.areaCode, '312')
def test_number(self):
self.target.number = '555-0142'
self.assertEqual(self.target.number, '555-0142')
def test_telephoneNumber(self): # add
target = TelephoneNumber('312', '555-0142') # add
self.assertEqual(target.areaCode, '312') # add
self.assertEqual(target.number, '555-0142') # add
Prepare for setter removal, extract variables
class TelephoneNumber():
def __init__(self, areaCode='', number=''): # edit
self.__areaCode = areaCode # edit
self.__number = number # edit
@property
def areaCode(self):
return self.__areaCode
@areaCode.setter
def areaCode(self, arg):
self.__areaCode = arg
@property
def number(self):
return self.__number
@number.setter
def number(self, arg):
self.__number = arg
Change from reference to value
class Person():
def __init__(self):
self.__telephoneNumber = TelephoneNumber()
@property
def officeAreaCode(self):
return self.__telephoneNumber.areaCode
@officeAreaCode.setter
def officeAreaCode(self, arg):
self.__telephoneNumber = TelephoneNumber(arg, self.officeNumber) # edit
@property
def officeNumber(self):
return self.__telephoneNumber.number
@officeNumber.setter
def officeNumber(self, arg):
self.__telephoneNumber = TelephoneNumber(self.officeAreaCode, arg) # edit
Remove setter, remove test
class TestTelephoneNumber(TestCase):
target = TelephoneNumber('312', '555-0142') # edit
def test_areaCode(self):
# self.target.areaCode = '312' # del
self.assertEqual(self.target.areaCode, '312')
def test_number(self):
# self.target.number = '555-0142' # del
self.assertEqual(self.target.number, '555-0142')
# def test_telephoneNumber(self): # del
# target = TelephoneNumber('312', '555-0142') # del
# self.assertEqual(target.areaCode, '312') # del
# self.assertEqual(target.number, '555-0142') # del
Removal of setter
class TelephoneNumber():
def __init__(self, areaCode='', number=''): # edit
self.__areaCode = areaCode # edit
self.__number = number # edit
@property
def areaCode(self):
return self.__areaCode
# @areaCode.setter # del
# def areaCode(self, arg): # del
# self.__areaCode = arg # del
@property
def number(self):
return self.__number
# @number.setter # del
# def number(self, arg): # del
# self.__number = arg # del
TestTelephoneNumber is now immutable, As a value object, I want to be able to judge value-based equivalence, The book is over here, we will also implement it in this article
Equivalence judgment, added test
class TestTelephoneNumber(TestCase):
target = TelephoneNumber('312', '555-0142')
def test_areaCode(self):
self.assertEqual(self.target.areaCode, '312')
def test_number(self):
self.assertEqual(self.target.number, '555-0142')
def test_equals(self): # add
self.assertEqual(self.target == TelephoneNumber('312', '555-0142'), True) # add
Equivalence judgment, first test green
class TelephoneNumber():
def __init__(self, areaCode='', number=''):
self.__areaCode = areaCode
self.__number = number
def __eq__(self, o): # add
return True # add
@property
def areaCode(self):
return self.__areaCode
@areaCode.setter
def areaCode(self, arg):
self.__areaCode = arg
@property
def number(self):
return self.__number
@number.setter
def number(self, arg):
self.__number = arg
Equivalence judgment, added test, triangulation
class TestTelephoneNumber(TestCase):
target = TelephoneNumber('312', '555-0142')
def test_areaCode(self):
self.assertEqual(self.target.areaCode, '312')
def test_number(self):
self.assertEqual(self.target.number, '555-0142')
def test_equals(self):
self.assertEqual(self.target == TelephoneNumber('312', '555-0142'), True)
def test_equals_areaCode_false(self): # add
self.assertEqual(self.target == TelephoneNumber('312x', '555-0142'), False) # add
Equivalence judgment,__eq__Implemented properly, areaCode
class TelephoneNumber():
def __init__(self, areaCode='', number=''):
self.__areaCode = areaCode
self.__number = number
def __eq__(self, o):
return self.areaCode == o.areaCode # edit
@property
def areaCode(self):
return self.__areaCode
@areaCode.setter
def areaCode(self, arg):
self.__areaCode = arg
@property
def number(self):
return self.__number
@number.setter
def number(self, arg):
self.__number = arg
Equivalence judgment, added test, triangulation
class TestTelephoneNumber(TestCase):
target = TelephoneNumber('312', '555-0142')
def test_areaCode(self):
self.assertEqual(self.target.areaCode, '312')
def test_number(self):
self.assertEqual(self.target.number, '555-0142')
def test_equals(self):
self.assertEqual(self.target == TelephoneNumber('312', '555-0142'), True)
def test_equals_areaCode_false(self):
self.assertEqual(self.target == TelephoneNumber('312x', '555-0142'), False)
def test_equals_number_false(self): # add
self.assertEqual(self.target == TelephoneNumber('312', '555-0142x'), False) # add
Equivalence judgment,__eq__Implemented properly, number
class TelephoneNumber():
def __init__(self, areaCode='', number=''):
self.__areaCode = areaCode
self.__number = number
def __eq__(self, o):
return self.areaCode == o.areaCode and self.number == o.number # edit
@property
def areaCode(self):
return self.__areaCode
@property
def number(self):
return self.__number
Classes like this are easier to implement in Python 3.7 and later
dataclass
from dataclasses import dataclass
@dataclass(frozen=True)
class TelephoneNumber():
areaCode: str = ''
number: str = ''
You can rest assured that you can adopt dataclass only by preparing for test green.
that's all
[(Youtube) Say Martin Fowler-Refactoring Catalog-Change from Reference to Value @ Tommy109](http://www.youtube. com/watch? v = xUB5e5h6yxw)
Recommended Posts