0
1

More than 3 years have passed since last update.

【リファクタリングカタログ】クラスの抽出

Last updated at Posted at 2021-02-04

書籍 リファクタリング―既存のコードを安全に改善する 第2版

またはWeb版(の方が完全版なんですが)には、
これを補完する リファクタリングカタログ が公開されています

これは何

書籍およびカタログはサンプルコードが JavaScript です、
カタログをもとに Python でリファクタリングのサンプルコードを示します

詳細解説は本にお任せして、「ここをこうこうこう」くらいのノリで示します

カタログ:Extract Class

当初コード、JavaScript版
class Person {
  get officeAreaCode() {return this._officeAreaCode;}
  get officeNumber()   {return this._officeNumber;}
}
リファクタリング後
class Person {
  get officeAreaCode() {return this._telephoneNumber.areaCode;}
  get officeNumber()   {return this._telephoneNumber.number;}
}
class TelephoneNumber {
  get areaCode() {return this._areaCode;}
  get number()   {return this._number;}
}

Python版

当初コード
from dataclasses import dataclass


@dataclass()
class Person:
    officeAreaCode: str = ""
    officeNumber: str = ""

テストコード

テストコード
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")

ここをこうこうこう

クラスTelephoneNumberを追加、テスト追加
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):  # add
    target = TelephoneNumber()  # add
クラスTelephoneNumberを追加
from dataclasses import dataclass


class TelephoneNumber:  # add
    pass  # add


@dataclass()
class Person:
    officeAreaCode: str = ""
    officeNumber: str = ""
TelephoneNumberにofficeAreaCodeを移動の準備、テスト
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_officeAreaCode(self):
        self.assertEqual(self.target.officeAreaCode, "")  # add
TelephoneNumberにofficeAreaCodeを移動
from dataclasses import dataclass


@dataclass()  # add
class TelephoneNumber:
    officeAreaCode: str = ""  # add


@dataclass()
class Person:
    officeAreaCode: str = ""
    officeNumber: str = ""
PersonのofficeAreaCodeはTelephoneNumberを参照
from dataclasses import dataclass


@dataclass()
class TelephoneNumber:
    officeAreaCode: str = ""


@dataclass()
class Person:
    __telephoneNumber: TelephoneNumber = TelephoneNumber()  # add
    officeAreaCode: str = __telephoneNumber.officeAreaCode  # edit
    officeNumber: str = ""
officeNumberも同様に、テスト
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_officeAreaCode(self):
        self.assertEqual(self.target.officeAreaCode, "")

    def test_officeNumber(self):  # add
        self.assertEqual(self.target.officeNumber, "")  # add
officeNumberも同様に
from dataclasses import dataclass


@dataclass()
class TelephoneNumber:
    officeAreaCode: str = ""
    officeNumber: str = ""  # add


@dataclass()
class Person:
    __telephoneNumber: TelephoneNumber = TelephoneNumber()
    officeAreaCode: str = __telephoneNumber.officeAreaCode
    officeNumber: str = __telephoneNumber.officeNumber  # edit
変数名の変更、テスト
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):  # edit
        self.assertEqual(self.target.areaCode, "")  # edit

    def test_number(self):  # edit
        self.assertEqual(self.target.number, "")  # edit
変数名の変更
from dataclasses import dataclass


@dataclass()
class TelephoneNumber:
    areaCode: str = ""
    number: str = ""


@dataclass()
class Person:
    __telephoneNumber: TelephoneNumber = TelephoneNumber()
    officeAreaCode: str = __telephoneNumber.areaCode
    officeNumber: str = __telephoneNumber.number
今後のメンテのためにテストを整える
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"  # add
        self.assertEqual(self.target.areaCode, "312")  # edit

    def test_number(self):
        self.target.number = "555-0142"  # add
        self.assertEqual(self.target.number, "555-0142")  # edit

出来上がり

書籍では何を目的にしてモチベーション持ってリファクタリングするの?まで解説するため、
クラス Person に name, telephoneNumber があり、
さらにクラス TelephoneNumber にプロパティ telephoneNumber を作り、
TelephoneNumber.telephoneNumber() はイケてないから TelephoneNumber.toString() に 関数名の変更 します、
が本記事のお目当ての カタログ”クラスの抽出” の手順はここまでにします

(言い訳)

  • 書籍だとコード片だけ書かれていて、組み合わせて、実際に動かして確認するのしんどいじゃないすか
  • 紙版だと写経、電子版だとコピーですが、それでも全体に対して欠片を組み合わせるのしんどいです
  • すぐ動かして貰いたい!Qiita記事ではコード部の右上のボタンでコピーできる!なので、なるべく全部載せしてます
  • が、全部載せすると、当然ながーくなって、読みづらくもなるので、name, telephoneNumber は触れませんでした
  • 関数群のクラスへの集約」 のときは流石に全体が大きくてクラス単位にはしましたが、コピーしやすい単位のはず
  • 動画の人は書籍に倣って toString までされていますね、いいなー

以上

参考

(Youtube)マーチンファウラーによろしく - リファクタリングカタログ - クラスの抽出 @ Tommy109

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1