書籍 リファクタリング―既存のコードを安全に改善する 第2版
またはWeb版(の方が完全版なんですが)には、
これを補完する リファクタリングカタログ が公開されています
これは何
書籍およびカタログはサンプルコードが JavaScript です、
カタログをもとに Python でリファクタリングのサンプルコードを示します
詳細解説は本にお任せして、「ここをこうこうこう」くらいのノリで示します
カタログ:Change Function Declaration
当初コード、JavaScript版
function circum(radius) {
return 2 * Math.PI * radius;
}
リファクタリング後
function circumference(radius) {
return 2 * Math.PI * radius;
}
Python版
当初コード
import numpy as np
def circum(radius):
return 2 * np.pi * radius
テストコード
テストコード、三角測量
from unittest import TestCase
import numpy as np
class TestCatalog(TestCase):
def test_circum_10(self):
self.assertEqual(circum(10), 20*np.pi)
def test_circum_20(self):
self.assertEqual(circum(20), 40*np.pi)
ここをこうこうこう
内部関数を追加
import numpy as np
def circum(radius):
def circumference(radius): # add
return 2 * np.pi * radius # add
return 2 * np.pi * radius
内部関数を利用する
import numpy as np
def circum(radius):
def circumference(radius):
return 2 * np.pi * radius
return circumference(radius) # edit
関数を外部化、変数を抽出
import numpy as np
def circum(radius):
return circumference(radius)
def circumference(radius): # move
return 2 * np.pi * radius # move
テストの対象を変更
from unittest import TestCase
import numpy as np
class TestCatalog(TestCase):
def test_circum_10(self):
self.assertEqual(circumference(10), 20*np.pi) # edit
def test_circum_20(self):
self.assertEqual(circumference(20), 40*np.pi) # edit
未使用関数を除去
import numpy as np
def circumference(radius):
return 2 * np.pi * radius
出来上がり
この例のような変更はエディタのリファクタリング機能が助けてくれることもあると思います、
引数の追加、除去も同様の手順で安全にリファクタリングできます
引数の変更の例
オブジェクトの一部だけ使うのに、引数ではオブジェクト丸ごと渡しているのを是正したいケース
当初コード、JavaScript版
function inNewEngland(aCustomer) {
return ["MA", "CT", "ME", "VT", "NH", "RI"].includes(aCustomer.address.state);
}
リファクタリング後
function inNewEngland(stateCode) {
return ["MA", "CT", "ME", "VT", "NH", "RI"].includes(stateCode);
}
Python版
当初コード
def inNewEngland(aCustomer):
return aCustomer.address.state in ["MA", "CT", "ME", "VT", "NH", "RI"]
テストコード
テストコード
from unittest import TestCase
from collections import namedtuple
class TestCatalog(TestCase):
def test_inNewEngland(self):
address = namedtuple("address", "state")
aCustomer = namedtuple("aCustomer", "address")
customer = aCustomer(address("MA"))
self.assertTrue(inNewEngland(customer))
ここをこうこうこう
変数の抽出
def inNewEngland(aCustomer):
stateCode = aCustomer.address.state # add
return stateCode in ["MA", "CT", "ME", "VT", "NH", "RI"] # edit
関数の抽出
def inNewEngland(aCustomer):
stateCode = aCustomer.address.state
return xxinNewEngland(stateCode) # edit
def xxinNewEngland(stateCode): # add
return stateCode in ["MA", "CT", "ME", "VT", "NH", "RI"] # add
変数のインライン化
def inNewEngland(aCustomer):
return xxinNewEngland(aCustomer.address.state) # edit
def xxinNewEngland(stateCode):
return stateCode in ["MA", "CT", "ME", "VT", "NH", "RI"]
テストの対象を変更
from unittest import TestCase
from collections import namedtuple
class TestCatalog(TestCase):
def test_inNewEngland(self):
address = namedtuple("address", "state")
aCustomer = namedtuple("aCustomer", "address")
customer = aCustomer(address("MA"))
self.assertTrue(xxinNewEngland(customer.address.state)) # edit
未使用関数を除去、そして、関数名の変更
def inNewEngland(stateCode):
return stateCode in ["MA", "CT", "ME", "VT", "NH", "RI"]
出来上がり
最後ちょっとジャンプしましたが、関数名の変更はエディタの機能が助けてくれると期待します
以上
参考
(Youtube)マーチンファウラーによろしく - リファクタリングカタログ - 関数宣言の変更 @ Tommy109