1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【初心者向け】引数が多すぎるメソッドを改善する-パラメータオブジェクト

Posted at

対象読者層

  • パラメータオブジェクトという設計パターンに詳しくない方
  • 引数の多いメソッドのリファクタリングを検討している方

パラメーターオブジェクトとは

パラメーターオブジェクトとは、メソッドの引数をまとめたクラスのことです。
複数のパラメーターをひとつのオブジェクトにカプセル化することで、コードの可読性と保守性を向上させる設計パターンです。

例えば、以下のようなメソッドがあるとします:

public void createUser(String name, String email, int age, String address, 
                      String phoneNumber, String occupation) {
    // ユーザー作成の処理
}

このメソッドには以下のような問題点があります:

  1. 引数の多さによる可読性の低下

    • 6個もの引数があり、メソッドの呼び出し時に引数の順序を間違えやすい
    • メソッドのシグネチャ^1が長くなり、コードの見通しが悪くなる
  2. 引数の型の同一性

    • 多くの引数がString型のため、誤って順序を入れ替えてもコンパイル時にエラーにならない
    • 例:createUser("john@example.com", "John", 30, ...) と書いても気づきにくい
  3. メソッドの変更が困難

    • 新しいパラメーターを追加する度に、メソッドのシグネチャ1を変更する必要がある
    • 全ての呼び出し箇所を修正しなければならない
  4. 関連するパラメーターのグループ化ができない

    • 住所に関する情報(都道府県、市区町村、番地など)を別々の引数として扱わなければならない
    • パラメーター間の関連性が明確でない

これをパラメーターオブジェクトを使用して以下のように書き換えることができます:

public void createUser(UserParameters params) {
    // ユーザー作成の処理
}

この改善により:

  • メソッドのシグネチャ1が簡潔になる
  • パラメーターの順序を気にする必要がなくなる
  • パラメーターの追加・削除が容易になる
  • 関連するパラメーターをグループ化できる

なぜパラメーターオブジェクトを導入するのか

パラメーターオブジェクトを導入する主なメリットは以下の通りです:

  1. 可読性の向上

    • メソッドのシグネチャ1がすっきりする
    • パラメーターの意味が明確になる
  2. 保守性の向上

    • パラメーターの追加・削除が容易
    • 関連するパラメーターをまとめて管理できる
  3. 再利用性の向上

    • 同じパラメーターセットを複数のメソッドで使用可能
    • パラメーターの検証ロジックを一箇所にまとめられる
  4. テストの容易性

    • テストデータの作成が簡単
    • パラメーターセットの一括管理が可能

導入方法とサンプルコード

実際の実装例を見てみましょう。

パラメータオブジェクト導入前のコード

// Before: パラメーターオブジェクト導入前
public class UserService {
    public void createUser(String name, String email, int age, String address, 
                          String phoneNumber, String occupation) {
        // バリデーション
        if (name == null || name.isEmpty()) {
            throw new IllegalArgumentException("Name is required");
        }
        if (email == null || !email.contains("@")) {
            throw new IllegalArgumentException("Invalid email");
        }
        if (age < 0) {
            throw new IllegalArgumentException("Invalid age");
        }
        
        // ユーザー作成の処理
        User user = new User(name, email, age, address, phoneNumber, occupation);
        // ...
    }
}

パラメータオブジェクト導入後のコード

// After: パラメーターオブジェクト導入後
public class UserParameters {
    private final String name;
    private final String email;
    private final int age;
    private final String address;
    private final String phoneNumber;
    private final String occupation;

    public UserParameters(String name, String email, int age, String address, 
                         String phoneNumber, String occupation) {
        this.name = name;
        this.email = email;
        this.age = age;
        this.address = address;
        this.phoneNumber = phoneNumber;
        this.occupation = occupation;
    }

    // バリデーションメソッド
    public void validate() {
        if (name == null || name.isEmpty()) {
            throw new IllegalArgumentException("Name is required");
        }
        if (email == null || !email.contains("@")) {
            throw new IllegalArgumentException("Invalid email");
        }
        if (age < 0) {
            throw new IllegalArgumentException("Invalid age");
        }
    }

    // getterメソッド
    public String getName() { return name; }
    public String getEmail() { return email; }
    public int getAge() { return age; }
    public String getAddress() { return address; }
    public String getPhoneNumber() { return phoneNumber; }
    public String getOccupation() { return occupation; }
}

public class UserService {
    public void createUser(UserParameters params) {
        // バリデーション
        params.validate();
        
        // ユーザー作成の処理
        User user = new User(params.getName(), params.getEmail(), params.getAge(),
                           params.getAddress(), params.getPhoneNumber(), 
                           params.getOccupation());
        // ...
    }
}

応用編:Builderパターンの導入

パラメーターオブジェクトは、Builderパターンと組み合わせることでさらに使いやすくなります。

Builderパターンとは

Builderパターンは、複雑なオブジェクトを段階的に構築するためのデザインパターンです。
実際にサンプルコードを見てみましょう。

Builderパターンを適用してオブジェクトの生成を簡易化

public class UserParameters {
    // 既存のフィールドとコンストラクタ...

    public static class Builder {
        private String name;
        private String email;
        private int age;
        private String address;
        private String phoneNumber;
        private String occupation;

        public Builder name(String name) {
            this.name = name;
            return this;
        }

        public Builder email(String email) {
            this.email = email;
            return this;
        }

        public Builder age(int age) {
            this.age = age;
            return this;
        }

        public Builder address(String address) {
            this.address = address;
            return this;
        }

        public Builder phoneNumber(String phoneNumber) {
            this.phoneNumber = phoneNumber;
            return this;
        }

        public Builder occupation(String occupation) {
            this.occupation = occupation;
            return this;
        }

        public UserParameters build() {
            return new UserParameters(name, email, age, address, phoneNumber, occupation);
        }
    }

    // 使用例
    public static void main(String[] args) {
        UserParameters params = new UserParameters.Builder()
            .name("John Doe")
            .email("john@example.com")
            .age(30)
            .address("Tokyo, Japan")
            .phoneNumber("090-1234-5678")
            .occupation("Engineer")
            .build();

        UserService service = new UserService();
        service.createUser(params);
    }
}

Builderパターンを導入することで以下のメリットが得られます。

  • 必要なパラメーターだけを設定できる
  • コードの可読性が向上する
  • メソッドチェーンによる直感的な記述が可能

さいごに

以上が、パラメーターオブジェクトの基本から応用までの解説になります。
はじめは、メソッドの引数のためだけにクラスを定義することに躊躇するかもしれません。
しかし、バラバラだった引数を1つにまとめることのできるメリットは非常に大きいです。

引数の多いメソッドに導入を検討してみてください!

  1. シグネチャ(Signature):メソッドを一意に識別する情報のことで、メソッド名と引数(パラメーター)の組み合わせを指します。例えば、createUser(String name, String email, int age)というメソッドのシグネチャは、メソッド名が"createUser"で、引数が"String型のname"、"String型のemail"、"int型のage"という情報になります。シグネチャは、メソッドの定義や呼び出し方を決定する重要な要素です。 2 3

1
0
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
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?