小粒クラスから関数を上手に活用する#

関数とユーティリティクラスは禁止 において、
  • 関数とユーティリティクラスは禁止だが例外もある
という少し紛らわしい書き方をしました。 「安易に作らない」ということを強調するために禁止という言葉を使ったのですが、関数の使い道として次のようなものがあります。

氏名クラスから文字列編集関数を使って保守性を高める#

またもや氏名クラスを例にして説明します。
public class PersonName {
    private String familyName;
    private String givenName;
     
    public PersonName(String famiLyName, String givenName) {
      :
      :
}
この中に
  • フルネームを返す
というメソッドがあったとします。そのメソッドの要件は、
  • 姓と名の間にスペースを1つ入れて返す
というものです。以下です。
public String getFullName() {
    return familyName + " " + givenName;
}

一方で住所クラスがあります。属性は郵便番号と居所です。

public class Address {
    private String zipCode;
    private String location;
     
    public Address(String zipCode, String location) {
      :
      :
}

この中に住所全体を返すメソッドを実装します。要件は、

  • 郵便番号と居所の間にスペースを1つ入れて返す
です。
public String getAddress() {
    return zipCode + " " + location;
}
何やら似ていますね。そうgetFullName()メソッドとそっくりです。なぜなら、
  • AとBの間にスペースを1つ入れて返す
と考えれば同じだからです。そこで関数を作ってそれを利用するように変更します。
public class StringFunctions {
    public static String joinWithSpace(String a, String b) {
        return a + " " + b;
    }
}

public String getFullName() {
    return StringFunctions.joinWithSpace(familyName, givenName);
}

public String getAddress() {
    return StringFunctions.joinWithSpace(zipCode, location);
}
ところが仕様変更が発生し、フルネームの場合は間のスペースを2つにしなければならなくなりました。そこで以下のように修正します。
public class StringFunctions {
    public static String joinWithSpace(String a, String b) {
        return StringFunctions.joinWithSpace(a, b, 1);
    }

    public static String joinWithSpace(String a, String b, int spaceSize) {
        StringBuffer result = new StringBuffer(a);
        for (int i = 0; i < spaceSize; i++) {
            result.append(" ");
        }
        result.append(b);
        return result.toString();
    }
}

public String getFullName() {
    return StringFunctions.joinWithSpace(familyName, givenName, 2);
}

修正箇所はPersonName#getFullName()のみで、かつ、PersonNameクラスを利用している各アプリケーションの修正は一切不要です。社員クラスから利用していようが取引先クラスから利用していようが、人の姓名の間にはスペース2つが挿入されるように一括して変わります。

関数を利用する上での注意点#

ただし関数は、

  • 経験のある設計者がきちんと設計した上で実装する
必要があります。そうしないと、
  • データ構造を持たない処理が乱造される
結果になります。なぜなら、
  • AとBの間にスペースを1つ入れて返す
という要件を満たす上記関数は、
  • AとBが文字列でさえあればその属性の業務的特徴に関係なく使えてしまう
からです。そもそも業務要件としては「文字列」などという属性は出てこないので、「文字列を編集する」という仕様は極めてシステム的な機能です。「文字列」というのが「属性の型」であることを考えてもシステム的な抽象化の結果であると判ります。
この関数を小粒クラスからではなく、例えば画面アプリケーションや帳票アプリケーションから直接使ってしまうと上記に書いたようなスペースを2つに換えるという単純な仕様変更でさえ、あちこちのコードを修正する必要が出てしまいます。
これを避けるための一つの指標としては、
  • この種類の関数は小粒クラスからのみ利用する
という運用規則を用いることが言えます。小粒クラスは言ってみれば最小単位のクラスなので、それらに共通する場合は関数が必要なケースかもしれないと検討することには意義があると言えます。

まとめ#

  1. 異なるクラスの異なる属性を共通的に扱う関数とユーティリティクラスは小粒クラスから利用する
  2. それらは経験のある設計者が設計する

コラム#

関数やユーティリティクラスを禁止するというのはかなりショッキングな言葉だと思います。そもそもJavaやC#などのオブジェクト指向言語が手続き型である関数を仕様として持っている以上、それを排除しろというのはかなり困難です。
様々なコードを見ましたが、手続き型で実装できてしまうことがオブジェクト指向型の実装を阻害していることが多いのが現実です。これはJava EEの仕様についても感じます。
例えばJPAで良く使われるEntityクラスは「リンゴ1個クラス」であり、これを自動生成するツールはたくさんあるのですが、「リンゴ1山クラス」を自動生成するツールは見掛けません。そういうツールを過去自分で作ったこともあります。
リンゴ1山クラスが無いと、その中に実装すべき処理があちこちに実装されてしまうため保守性を下げます。リンゴ1山クラスが常に必要だと気付くエンジニアは相当上級の人なので、そのまま開発してしまうとかえって「オブジェクト指向は使いづらいね」という印象に繋がります。
最近に至っては、オブジェクト指向を理解することの難しさからか、オブジェクト指向そのものが役に立たないという論調さえ見るようになってしまいました。理解することを諦めてしまったのでしょうね。
しかしこのサイトを読んだ皆さんは、オブジェクト指向の正しい適用方法を理解し、出来るだけそれに沿った設計・実装をすることによってメリットを享受出来るようになって下さい。そしてそのことを他のエンジニアにも広めて下さることを切に願います。

添付ファイルの追加

ログイン済のユーザのみが添付ファイルをアップロード出来ます。
« This page (revision-3) was last changed on 18-5-2016 14:24 by ytp