At line 9 changed 2 lines |
[{Image src='img.jpg'}] |
「担当者姓」と「担当者名」という属性がこのクラスの中にはあります。そしてこの二つの値を使ったメソッド「担当者氏名を返す」を考えてみます。姓と名の間にスペースを挟んで返す仕様だとします。コードは次のようになるでしょう。 |
[{Image src='custocompany1.png'}] |
「担当者姓」と「担当者名」という属性がこのクラスの中にはあります。そしてこの二つの値を使ったメソッド「担当者氏名を返す」を作ってみます。姓と名の間にスペースを挟んで返す仕様だとすると、コードは次のようになるでしょう。 |
At line 20 added one line |
[{Image src='custocompany2.png'}] |
At line 45 changed 2 lines |
が、果たして本当にそうでしょうか? |
例えば「仕入れ先」クラスが別にあったとして、その属性にも「担当者姓」「担当者名」があったとしたらどうでしょう? そしてそのクラスにも「担当者氏名を返す」というメソッドが必要で、「姓と名の間にスペースを2つ挟む」という仕様だったら? そのクラスにも同じようなprivateメソッドが必要になってしまい、仕様変更への対応が1ヶ所というわけにいかなくなってしまいます。\\ |
が、果たして本当にそうでしょうか?\\ |
[{Image src='custocompany1.png'}] |
例えば「仕入れ先会社」クラスが別にあったとして、その属性にも「担当者姓」「担当者名」があったとしたらどうでしょう? そしてそのクラスにも「担当者氏名を返す」というメソッドが必要で、「姓と名の間にスペースを2つ挟む」という仕様だったら? そのクラスにも同じようなprivateメソッドが必要になってしまい、仕様変更への対応が1ヶ所というわけにいかなくなってしまいます。\\ |
At line 48 changed one line |
!!privateメソッドとは何か? |
!!データ構造を見逃すな! |
At line 50 changed one line |
privateメソッドはクラス内の共通関数に当たるのですが、共通関数を作ろうとすれば、その関数に渡すパラメータをそもそもまず共通化する必要があります。そのパラメータをひとかたまり(データ構造)とするクラスを作るべきなんです。 |
privateメソッドはクラス内の共通関数に当たるのですが、共通関数を作ろうとすれば、その関数に渡す__パラメータをそもそもまず共通化する必要があります__。そのパラメータをひとかたまり(データ構造)とするクラスを作るべきなのです。\\ |
この場合は、属性として「姓」と「名」を持つ「氏名クラス」を作るべきです。 |
|
At line 53 changed one line |
パラメータの無い、つまり「共通関数ですらない」privateメソッドがなぜ必要なのでしょうか?\\ |
パラメータの無い、つまり__共通関数ですらないprivateメソッド__がなぜ必要なのでしょうか?\\ |
At line 55 changed one line |
なぜならコードが長くなるのは、上記のような小物クラスを見逃しまくっていて、結果的に手続き的な記述になっているからだと断言できます。そもそも一連の処理ならばprivateメソッドなどに分割せず、処理の流れの通りに1ヶ所に書いてある方が、第三者が見た時の可読性は高いはずです。下の例を比べてみて下さい。 |
なぜならコードが長くなるのは、上記のような小物クラスを見逃しまくっていて、結果的に手続き的な記述になっているからだと断言できます。クラスという単位で本来分割されているべきものが一つになってしまっていれば、その中の記述が長くなってしまうのは当然です。\\ |
そもそも一連の処理ならばprivateメソッドなどに分割せず、処理の流れの通りに1ヶ所に書いてある方が、第三者が見た時の可読性は高いはずです。下の例を比べてみて下さい。\\ |
!privateメソッドに分割した例 |
At line 91 added one line |
!一連の処理が1ヶ所に書いてある例 |
At line 108 changed one line |
属性の周りをメソッドが取り巻いている形が解ると思いますが、これは裏返すと、「属性を隠蔽するためにメソッドが口を開けている」ということです。つまり公開されないメソッドは原則に反するのです。privateによって隠蔽するべきなのは属性であってメソッドではありません。\\ |
フィールド(属性)の周りをメソッドが取り巻いている形が見えますが、これは裏返すと、「フィールドを隠蔽するためにメソッドが口を開けている」ということです。つまり公開されないメソッドはこの原則に反するのです。privateによって隠蔽するべきなのはフィールドであってメソッドではないという原則がこの図からも解ります。\\ |
At line 111 changed one line |
こういう場合に皆さんが思いつくのはユーティリティクラスと呼ばれる「関数」ではありませんか? 次のようなコードです。 |
「『担当者氏名を返す』メソッドはprivateではなくユーティリティクラスでいいじゃないか?」と思った人もいるでしょう。\\ |
次のようなコードです。 |
At line 133 changed 6 lines |
別のページでも書きましたが、このようなユーティリティクラス(インスタンス変数にもクラス変数にもアクセスしないメソッド群)はそもそもオブジェクト指向の原則である「データ構造と関数の一体化」を崩してしまいます。その結果どういう事が起きるかを考えてみます。\\ |
システムを国際化対応する必要がもしも出てきて、「担当者ミドルネーム」「代表者ミドルネーム」という属性が追加になったとします。この時、「氏名を返すメソッドは全て『姓 ミドルネーム 名』(間のスペースは一つずつ)とする」という要件に替わったとします。\\ |
この場合の変更箇所はStringUtility.getFullName()メソッドのみではなく、それを呼び出している全メソッドが対象となってしまいます。\\ |
なぜそうなってしまうかというと、「データ構造と関数が分離されているから」です。オブジェクト指向のメリットは「データ構造と関数の一体化」 によって産まれるのに、それに従っていない造りだからです。\\ |
!!だからprivateメソッド禁止! |
上記のコードがどこで間違ったかというと、privateメソッドを作りたくなった時にクラスを見逃したからです。 |
[ユーティリティクラス禁止]でも書きましたが、このようなユーティリティクラス(インスタンス変数にもクラス変数にもアクセスしないメソッド群)はそもそもオブジェクト指向の原則である「データ構造と関数の一体化」を崩してしまいます。その結果どういう事が起きるかを考えてみます。\\ |
上記のStringUtility#getFullName()メソッドは単なる関数なので、姓名の連結のみに使われる保証はありません。極端な場合は次のような使われ方をするかもしれません。\\ |
%%prettify |
{{{ |
public class Jimusho { |
private String yuubinBangou = ""; |
private String jyuusho = ""; |
: |
: |
public String getAtesaki() { |
return StringUtility.getFullName(this.yuubinBangou, this.jyuusho); |
} |
} |
}}} |
/% |
StringUtility#getFullName()メソッドの機能は、「1つめのパラメータと2つめのパラメータを2個のスペースを挟んで連結する」というものです。上記のように、「事務所クラスの宛先を返すメソッド」にこの機能がたまたま使えたとすれば使われる可能性を排除できません。\\ |
この時、システムを国際化対応する必要がもしも出てきて、「担当者ミドルネーム」「代表者ミドルネーム」という属性が追加になったとします。そして、「氏名を返すメソッドは全て『姓 ミドルネーム 名』(間のスペースは一つずつ)とする」という要件に替わったとします。\\ |
この場合の変更箇所はStringUtility.getFullName()メソッドのみではなく、それを呼び出している全メソッドが対象となります(当然ですが)。その際、「従業員クラスの宛先を返すメソッド」も該当しますが、事務所クラスは「ミドルネーム」を持たないため破綻してしまいます。別の関数を新たに作る必要が出てきます。\\ |
一方で、「氏名クラス」を利用している場合はどうでしょう? |