【リファクタリングカタログ】スーパークラスの抽出
書籍 リファクタリング―既存のコードを安全に改善する 第2版
またはWeb版(の方が完全版なんですが)には、
これを補完する リファクタリングカタログ が公開されています
これは何
書籍およびカタログはサンプルコードが JavaScript です、
カタログをもとに Python でリファクタリングのサンプルコードを示します
詳細解説は本にお任せして、「ここをこうこうこう」くらいのノリで示します
カタログ:Extract Superclass
class Department {
get totalAnnualCost() {...}
get name() {...}
get headCount() {...}
}
class Employee {
get annualCost() {...}
get name() {...}
get id() {...}
}
class Party {
get name() {...}
get annualCost() {...}
}
class Department extends Party {
get annualCost() {...}
get headCount() {...}
}
class Employee extends Party {
get annualCost() {...}
get id() {...}
}
Python版
class Department:
def totalAnnualCost(self):
...
と行きたいところですが、書籍を見ると、Employee, Department で具体説明をしていて、ぐぬぬ、という訳で改めまして
class Employee {
constructor(name, id, monthlyCost) {
this._id = id;
this._name = name;
this._monthlyCost = monthlyCost;
}
get monthlyCost() {return this._monthlyCost;}
get name() {return this._name;}
get id() {return this._id;}
get annualCost() {
return this.monthlyCost * 12;
}
}
class Department {
constructor(name, staff){
this._name = name;
this._staff = staff;
}
get staff() {return this._staff.slice();}
get name() {return this._name;}
get totalMonthlyCost() {
return this.staff
.map(e => e.monthlyCost)
.reduce((sum, cost) => sum + cost);
}
get headCount() {
return this.staff.length;
}
get totalAnnualCost() {
return this.totalMonthlyCost * 12;
}
}
という訳で初期コードはつぎのようになります
Python版、改
from dataclasses import dataclass
from functools import reduce
from operator import add
@dataclass
class Employee:
__name: str
__id: str
__monthlyCost: int
@property
def name(self):
return self.__name
@property
def id(self):
return self.__id
@property
def monthlyCost(self):
return self.__monthlyCost
@property
def annualCost(self):
return self.monthlyCost * 12
@dataclass
class Department:
__name: str
__staff: list
@property
def name(self):
return self.__name
@property
def staff(self):
return self.__staff[:]
@property
def totalMonthlyCost(self):
return reduce(add, (e.monthlyCost for e in self.staff))
@property
def headCount(self):
return len(self.staff)
@property
def totalAnnualCost(self):
return self.totalMonthlyCost * 12
テストコード
from unittest import TestCase
class TestEmployee(TestCase):
def setUp(self):
self.target = Employee("foo bar", "baz", 3000)
def test_monthlyCost(self):
self.assertEqual(self.target.monthlyCost, 3000)
def test_name(self):
self.assertEqual(self.target.name, "foo bar")
def test_id(self):
self.assertEqual(self.target.id, "baz")
def test_annualCost(self):
self.assertEqual(self.target.annualCost, 36000)
class TestDepartment(TestCase):
def setUp(self):
self.target = Department("hanya", [Employee("foo bar", "baz", 3000)])
def test_staff(self):
self.assertEqual(self.target.staff, [Employee("foo bar", "baz", 3000)])
def test_name(self):
self.assertEqual(self.target.name, "hanya")
def test_totalMonthlyCost(self):
self.assertEqual(self.target.totalMonthlyCost, 3000)
def test_headCount(self):
self.assertEqual(self.target.headCount, 1)
def test_totalAnnualCost(self):
self.assertEqual(self.target.totalAnnualCost, 36000)
ここをこうこうこう
from dataclasses import dataclass
from functools import reduce
from operator import add
@dataclass # add
class Party: # add
pass # add
@dataclass
class Employee(Party): # edit
__name: str
__id: str
__monthlyCost: int
@property
def name(self):
return self.__name
@property
def id(self):
return self.__id
@property
def monthlyCost(self):
return self.__monthlyCost
@property
def annualCost(self):
return self.monthlyCost * 12
@dataclass
class Department(Party): # edit
__name: str
__staff: list
@property
def name(self):
return self.__name
@property
def staff(self):
return self.__staff[:]
@property
def totalMonthlyCost(self):
return reduce(add, (e.monthlyCost for e in self.staff))
@property
def headCount(self):
return len(self.staff)
@property
def totalAnnualCost(self):
return self.totalMonthlyCost * 12
from dataclasses import dataclass
from functools import reduce
from operator import add
@dataclass
class Party:
pass
@dataclass
class Employee(Party):
_name: str # edit
__id: str
__monthlyCost: int
@property
def name(self):
return self._name # edit
@property
def id(self):
return self.__id
@property
def monthlyCost(self):
return self.__monthlyCost
@property
def annualCost(self):
return self.monthlyCost * 12
@dataclass
class Department(Party):
_name: str # edit
__staff: list
@property
def name(self):
return self._name # edit
@property
def staff(self):
return self.__staff[:]
@property
def totalMonthlyCost(self):
return reduce(add, (e.monthlyCost for e in self.staff))
@property
def headCount(self):
return len(self.staff)
@property
def totalAnnualCost(self):
return self.totalMonthlyCost * 12
from dataclasses import dataclass
from functools import reduce
from operator import add
@dataclass
class Party:
_name: str # add
@dataclass
class Employee(Party):
# _name: str # del
__id: str
__monthlyCost: int
@property
def name(self):
return self._name
@property
def id(self):
return self.__id
@property
def monthlyCost(self):
return self.__monthlyCost
@property
def annualCost(self):
return self.monthlyCost * 12
@dataclass
class Department(Party):
# _name: str # del
__staff: list
@property
def name(self):
return self._name
@property
def staff(self):
return self.__staff[:]
@property
def totalMonthlyCost(self):
return reduce(add, (e.monthlyCost for e in self.staff))
@property
def headCount(self):
return len(self.staff)
@property
def totalAnnualCost(self):
return self.totalMonthlyCost * 12
from dataclasses import dataclass
from functools import reduce
from operator import add
@dataclass
class Party:
_name: str
@property # add
def name(self): # add
return self._name # add
@dataclass
class Employee(Party):
__id: str
__monthlyCost: int
# @property # del
# def name(self): # del
# return self._name # del
@property
def id(self):
return self.__id
@property
def monthlyCost(self):
return self.__monthlyCost
@property
def annualCost(self):
return self.monthlyCost * 12
@dataclass
class Department(Party):
__staff: list
# @property # del
# def name(self): # del
# return self._name # del
@property
def staff(self):
return self.__staff[:]
@property
def totalMonthlyCost(self):
return reduce(add, (e.monthlyCost for e in self.staff))
@property
def headCount(self):
return len(self.staff)
@property
def totalAnnualCost(self):
return self.totalMonthlyCost * 12
from dataclasses import dataclass
from functools import reduce
from operator import add
@dataclass
class Party:
__name: str # edit
@property
def name(self):
return self.__name # edit
@dataclass
class Employee(Party):
__id: str
__monthlyCost: int
@property
def id(self):
return self.__id
@property
def monthlyCost(self):
return self.__monthlyCost
@property
def annualCost(self):
return self.monthlyCost * 12
@dataclass
class Department(Party):
__staff: list
@property
def staff(self):
return self.__staff[:]
@property
def totalMonthlyCost(self):
return reduce(add, (e.monthlyCost for e in self.staff))
@property
def headCount(self):
return len(self.staff)
@property
def totalAnnualCost(self):
return self.totalMonthlyCost * 12
from unittest import TestCase
class TestEmployee(TestCase):
def setUp(self):
self.target = Employee("foo bar", "baz", 3000)
def test_monthlyCost(self):
self.assertEqual(self.target.monthlyCost, 3000)
def test_name(self):
self.assertEqual(self.target.name, "foo bar")
def test_id(self):
self.assertEqual(self.target.id, "baz")
def test_annualCost(self):
self.assertEqual(self.target.annualCost, 36000)
class TestDepartment(TestCase):
def setUp(self):
self.target = Department("hanya", [Employee("foo bar", "baz", 3000)])
def test_staff(self):
self.assertEqual(self.target.staff, [Employee("foo bar", "baz", 3000)])
def test_name(self):
self.assertEqual(self.target.name, "hanya")
def test_monthlyCost(self): # edit
self.assertEqual(self.target.monthlyCost, 3000) # edit
def test_headCount(self):
self.assertEqual(self.target.headCount, 1)
def test_totalAnnualCost(self):
self.assertEqual(self.target.totalAnnualCost, 36000)
from dataclasses import dataclass
from functools import reduce
from operator import add
@dataclass
class Party:
__name: str
@property
def name(self):
return self.__name
@dataclass
class Employee(Party):
__id: str
__monthlyCost: int
@property
def id(self):
return self.__id
@property
def monthlyCost(self):
return self.__monthlyCost
@property
def annualCost(self):
return self.monthlyCost * 12
@dataclass
class Department(Party):
__staff: list
@property
def staff(self):
return self.__staff[:]
@property
def monthlyCost(self): # edit
return reduce(add, (e.monthlyCost for e in self.staff))
@property
def headCount(self):
return len(self.staff)
@property
def totalAnnualCost(self):
return self.monthlyCost * 12 # edit
from unittest import TestCase
class TestEmployee(TestCase):
def setUp(self):
self.target = Employee("foo bar", "baz", 3000)
def test_monthlyCost(self):
self.assertEqual(self.target.monthlyCost, 3000)
def test_name(self):
self.assertEqual(self.target.name, "foo bar")
def test_id(self):
self.assertEqual(self.target.id, "baz")
def test_annualCost(self):
self.assertEqual(self.target.annualCost, 36000)
class TestDepartment(TestCase):
def setUp(self):
self.target = Department("hanya", [Employee("foo bar", "baz", 3000)])
def test_staff(self):
self.assertEqual(self.target.staff, [Employee("foo bar", "baz", 3000)])
def test_name(self):
self.assertEqual(self.target.name, "hanya")
def test_monthlyCost(self):
self.assertEqual(self.target.monthlyCost, 3000)
def test_headCount(self):
self.assertEqual(self.target.headCount, 1)
def test_annualCost(self): # edit
self.assertEqual(self.target.annualCost, 36000) # edit
from dataclasses import dataclass
from functools import reduce
from operator import add
@dataclass
class Party:
__name: str
@property
def name(self):
return self.__name
@dataclass
class Employee(Party):
__id: str
__monthlyCost: int
@property
def id(self):
return self.__id
@property
def monthlyCost(self):
return self.__monthlyCost
@property
def annualCost(self):
return self.monthlyCost * 12
@dataclass
class Department(Party):
__staff: list
@property
def staff(self):
return self.__staff[:]
@property
def monthlyCost(self):
return reduce(add, (e.monthlyCost for e in self.staff))
@property
def headCount(self):
return len(self.staff)
@property
def annualCost(self): # edit
return self.monthlyCost * 12
from dataclasses import dataclass
from functools import reduce
from operator import add
@dataclass
class Party:
__name: str
@property
def name(self):
return self.__name
@property # add
def annualCost(self): # add
return self.monthlyCost * 12 # add
@dataclass
class Employee(Party):
__id: str
__monthlyCost: int
@property
def id(self):
return self.__id
@property
def monthlyCost(self):
return self.__monthlyCost
# @property # del
# def annualCost(self): # del
# return self.monthlyCost * 12 # del
@dataclass
class Department(Party):
__staff: list
@property
def staff(self):
return self.__staff[:]
@property
def monthlyCost(self):
return reduce(add, (e.monthlyCost for e in self.staff))
@property
def headCount(self):
return len(self.staff)
# @property # del
# def annualCost(self): # del
# return self.monthlyCost * 12 # del
クラス Party は関数 annualCost が自身に存在しない変数 monthlyCost を参照してます、ので、リファクタリング手順外ですが、ちょっと追加しておきます
from dataclasses import dataclass
from functools import reduce
from operator import add
@dataclass
class Party:
__name: str
@property
def name(self):
return self.__name
@property # add
def monthlyCost(self): # add
raise NotImplementedError # add
@property
def annualCost(self):
return self.monthlyCost * 12
@dataclass
class Employee(Party):
__id: str
__monthlyCost: int
@property
def id(self):
return self.__id
@property
def monthlyCost(self):
return self.__monthlyCost
@dataclass
class Department(Party):
__staff: list
@property
def staff(self):
return self.__staff[:]
@property
def monthlyCost(self):
return reduce(add, (e.monthlyCost for e in self.staff))
@property
def headCount(self):
return len(self.staff)
出来上がり
変数名の変更や関数宣言の変更はエディタのリファクタリング機能が助けてくれることもあると思います
以上
参考
(Youtube)マーチンファウラーによろしく - リファクタリングカタログ - スーパークラスの抽出 @ Tommy109
Author And Source
この問題について(【リファクタリングカタログ】スーパークラスの抽出), 我々は、より多くの情報をここで見つけました https://qiita.com/kfjt/items/188b7e6bdc049df26d26著者帰属:元の著者の情報は、元のURLに含まれています。著作権は原作者に属する。
Content is automatically searched and collected through network algorithms . If there is a violation . Please contact us . We will adjust (correct author information ,or delete content ) as soon as possible .