This is version . It is not the current version, and thus it cannot be edited.
[Back to current version]   [Restore this version]

privateメソッドを作りたくなった時は存在するべきクラスを見逃している#

このページの見出しを見て「えっ?!」と思う人は多いでしょう。でも、私がマネージメントする開発では「privateメソッド禁止」は当たり前なんです。
例を使った下の説明で詳しい内容は理解して欲しいのですが、要点だけを言うと、

「privateメソッドに渡すパラメータをひとかたまり(データ構造)とするクラスを見逃している。」#

ということです。

顧客会社クラスで考えてみる#

商取引を扱うシステムで良く出てくる「顧客会社」クラスを使って考えてみます。
「担当者姓」と「担当者名」という属性がこのクラスの中にはあります。そしてこの二つの値を使ったメソッド「担当者氏名を返す」を考えてみます。姓と名の間にスペースを挟んで返す仕様だとします。コードは次のようになるでしょう。
public String getTantoushaFullName() {
    return this.tantoushaSei + " " + this.tantoushaMei;
}

次に、このクラスが仕様変更となり、「代表者姓」と「代表者名」という属性が追加されたとします。「代表者氏名を返す」というメソッドもこの時必要になったとすると、以下のようなコードを追加することでしょう。
public String getDaihyoushaFullName() {
    return this.daihyoushaSei + " " + this.daihyoushaMei;
}

そして次に、「いずれの氏名を返す時も、姓と名の間のスペースを2つに替えて欲しい。」という要望が挙がったとします。よく見てみると上記二つのメソッドは非常に良く似ています。「スペースを挟んで姓と名を連結する」という仕様だからです。こういう時、privateメソッドを作っておくと変更が一ヶ所で済んだはずです。次のようなコードです。
private String getFullName(String sei, String mei) {
    return sei + "  " + mei;
}

public String getTantoushaFullName() {
    return getFullName(this.tantoushaSei, this.tantoushaMei);
}

public String getDaihyoushaFullName() {
    return getFullName(this.daihyoushaSei, this.daihyoushaMei);
}
こうしておけば、間に挟むスペースが3つに替わろうが変更箇所は1カ所で済みます。
が、果たして本当にそうでしょうか? 例えば「仕入れ先」クラスが別にあったとして、その属性にも「担当者姓」「担当者名」があったとしたらどうでしょう? そしてそのクラスにも「担当者氏名を返す」というメソッドが必要で、「姓と名の間にスペースを2つ挟む」という仕様だったら? そのクラスにも同じようなprivateメソッドが必要になってしまい、仕様変更への対応が1ヶ所というわけにいかなくなってしまいます。

privateメソッドとは何か?#

上記の例で解るように、「privateメソッドを作りたくなった時はクラスを見逃している」のです。
privateメソッドはクラス内の共通関数に当たるのですが、共通関数を作ろうとすれば、その関数に渡すパラメータをそもそもまず共通化する必要があります。そのパラメータをひとかたまり(データ構造)とするクラスを作るべきなんです。

パラメータを持たないprivateメソッドは?#

この話をした際によくある反論が「パラメータを持たないprivateメソッドだってあるじゃないか!」というものです。
パラメータの無い、つまり「共通関数ですらない」privateメソッドがなぜ必要なのでしょうか?
彼らの答えは「可読性をあげるため長いコードを分割するんだ。」なのですが、私に言わせると「ちゃんちゃら可笑しいw」です。
なぜならコードが長くなるのは、上記のような小物クラスを見逃しまくっていて、結果的に手続き的な記述になっているからだと断言できます。そもそも一連の処理ならばprivateメソッドなどに分割せず、処理の流れの通りに1ヶ所に書いてある方が、第三者が見た時の可読性は高いはずです。下の例を比べてみて下さい。
public String getSomething() {
    doSomething1();
    doSomething2();
    doSomething3();
}

public String getAnotherthing() {
    他の処理が書いてある;
}

private void doSomething1() {
    何かの処理1-1が書いてある;
    何かの処理1-2が書いてある;
}

private void doSomething2() {
    何かの処理2-1が書いてある;
    何かの処理2-2が書いてある;
}

private void doSomething3() {
    何かの処理3-1が書いてある;
    何かの処理3-2が書いてある;
}
public String getSomething () {
    何かの処理1-1が書いてある;
    何かの処理1-2が書いてある;

    何かの処理2-1が書いてある;
    何かの処理2-2が書いてある;

    何かの処理3-1が書いてある;
    何かの処理3-2が書いてある;
}

public String getAnotherthing() {
    他の処理が書いてある;
}
正しいクラスを定義した結果、それでもメソッドの記述が長くなる場合、例えば属性が100個ぐらいあって一つの処理が長くなる場合、それはそれで正しいのです。意味も無く分割する必要は無いというのが私の考えです。

オブジェクト指向の原則を考えるとそもそもprivateメソッドは不要#

クラスとはデータ構造で説明した次の図をもう一度見て下さい。
属性の周りをメソッドが取り巻いている形が解ると思いますが、これは裏返すと、「属性を隠蔽するためにメソッドが口を開けている」ということです。つまり公開されないメソッドは原則に反するのです。privateによって隠蔽するべきなのは属性であってメソッドではありません。

ユーティリティクラスだとどうだ?#

こういう場合に皆さんが思いつくのはユーティリティクラスと呼ばれる「関数」ではありませんか? 次のようなコードです。
public class StringUtility {
    public String getFullName(String sei, String mei) {
        return sei + "  " + mei;
    }
}

public class KokyakuKaisha {
  :
  :
    public String getTantoushaFullName() {
        return StringUtility.getFullName(this.tantoushaSei, this.tantoushaMei);
    }

    public String getDaihyoushaFullName() {
        return StringUtility.getFullName(this.daihyoushaSei, this.daihyoushaMei);
    }
}
別のページでも書きましたが、このようなユーティリティクラス(インスタンス変数にもクラス変数にもアクセスしないメソッド群)はそもそもオブジェクト指向の原則である「データ構造と関数の一体化」を崩してしまいます。その結果どういう事が起きるかを考えてみます。
システムを国際化対応する必要がもしも出てきて、「担当者ミドルネーム」「代表者ミドルネーム」という属性が追加になったとします。この時、「氏名を返すメソッドは全て『姓 ミドルネーム 名』(間のスペースは一つずつ)とする」という要件に替わったとします。
この場合の変更箇所はStringUtility.getFullName()メソッドのみではなく、それを呼び出している全メソッドが対象となってしまいます。
なぜそうなってしまうかというと、「データ構造と関数が分離されているから」です。オブジェクト指向のメリットは「データ構造と関数の一体化」 によって産まれるのに、それに従っていない造りだからです。

だからprivateメソッド禁止!#

上記のコードがどこで間違ったかというと、privateメソッドを作りたくなった時にクラスを見逃したからです。

添付ファイルの追加

ログイン済のユーザのみが添付ファイルをアップロード出来ます。

添付ファイル一覧

Kind Attachment Name Size Version Date Modified Author Change note
png
custocompany1.png 3.3 kB 1 25-6-2011 03:28 ytp
png
custocompany2.png 4.1 kB 1 25-6-2011 03:28 ytp
png
name.png 8.2 kB 4 31-12-2011 21:46 ytp
png
name2.png 8.3 kB 3 31-12-2011 21:43 ytp
png
supplyer.png 3.4 kB 1 25-6-2011 03:28 ytp
« This particular version was published on 24-6-2011 20:46 by ytp.