English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية

AndroidプログラミングデザインパターンのBuilderパターンの実例詳細

この記事では、AndroidプログラミングデザインパターンのBuilderパターンについて説明します。皆さんに参考にしていただくために、以下の通りです:

一、紹介

Builderパターンは、複雑なオブジェクトを一つずつ作成する構成パターンです。ユーザーは内部の構築詳細を知らなくても、オブジェクトの構築プロセスをより細かく制御できます。このパターンは、複雑なオブジェクトの構築プロセスとその部品を解耦し、構築プロセスと部品の表示を分離するために作成されました。

複雑なオブジェクトには多くの構成部分があります。例えば、車には車輪、ステアリング、エンジンがあり、さまざまな小部品もあります。これらの部品をどのように組み立てるか、この組み立てプロセスは非常に長く、複雑です。このような場合、外部に実装の詳細を隠すために、Builderパターンを使用して部品と組み立てプロセスを分離し、構築プロセスと部品が自由に拡張できるようにし、両者間の依存関係を最小限に抑えます。

二、定義

複雑なオブジェクトの構築をその表示から分離し、同じ構築プロセスが異なる表示を生成できるようにします。

三、使用シーン

(1)同じメソッドで異なる実行順序が異なるイベント結果を生じさせる場合。

(2)複数の部品や部品がオブジェクトに組み込まれることができますが、生成される実行結果が異なる場合。

(3)製品クラスが非常に複雑である場合、または製品クラス内の呼び出し順序が異なり、異なる効果が生じる場合、この時点でBuilderパターンを使用するのが非常に適切です。

(4)オブジェクトの初期化が特に複雑な場合、例えば多くのパラメータがあり、多くのパラメータがデフォルト値を持っている場合。

四、BuilderパターンのUMLクラス図

役割紹介:

Product製品クラス——製品の抽象クラス;

Builder——抽象的なBuilderクラス、製品の組み立てを規範化します。一般的にはサブクラスが具体的な組み立てプロセスを実現します;

ConcreateBuilder——具体的なBuilderクラス;

Director——統一された組み立てプロセス;

五、Builderパターンの簡単な実装

コンピュータの組み立てプロセスは比較的複雑で、組み立て順序は固定ではありません。理解しやすくするために、コンピュータの組み立てプロセスをメインボードの構築、オペレーティングシステムの設定、ディスプレイの設定に簡略化します。3個の部分を組み合わせ、Directorと具体的なBuilderを通じてコンピュータオブジェクトを構築します。

/**
 * コンピュータの抽象クラス、Product役割
 */
public abstract class Computer {
  protected String mBoard;
  protected String mDisplay;
  protected String mOS;
  protected Computer(){}
  /**
   * メインボードを設定します
   * @param board
   */
  public void setBoard(String board){
    this.mBoard = board;
  }
  /**
   * ディスプレイを設定します
   * @param display
   */
  public void setDisplay(String display){
    this.mDisplay = display;
  }
  /**
   * オペレーティングシステムの設定
   */
  public abstract void setOS();
  @Override
  public String toString(){
    return "Computer [mBoard=" + mBoard + ", mDisplay=" + mDisplay + ", mOS=" + mOS + "]";
  }
}
/**
 * 具体的なComputerクラス、Macbook
 */
public class Macbook extends Computer {
  protected Macbook(){}
  @Override
  public void setOS() {
    mOS = "Mac OS X 10";
  }
}
/**
 * 抽象的なBuilderクラス
 */
public abstract class Builder {
  /**
   * メインボードを設定します
   * @param board
   */
  public abstract void buildBoard(String board);
  /**
   * ディスプレイを設定します
   * @param display
   */
  public abstract void buildDisplay(String display);
  /**
   * オペレーティングシステムの設定
   */
  public abstract void buildOS();
  /**
   * コンピュータの作成
   * @return
   */
  public abstract Computer create();
}
/**
 * 具体的なBuilderクラス、MacbookBuilder
 */
public class MacbookBuilder extends Builder {
  private Computer mComputer = new Macbook();
  @Override
  public void buildBoard(String board) {
    mComputer.setBoard(board);
  }
  @Override
  public void buildDisplay(String display) {
    mComputer.setDisplay(display);
  }
  @Override
  public void buildOS() {
    mComputer.setOS();
  }
  @Override
  public Computer create() {
    return mComputer;
  }
}
/**
 * Directorクラス、コンピュータの構築を担当
 */
public class Director {
  Builder mBuilder = null;
  public Director(Builder builder){
    mBuilder = builder;
  }
  /**
   * 構築オブジェクト
   * @param board マザーボード
   * @param display ディスプレイ
   */
  public void construct(String board, String display){
    mBuilder.buildBoard(board);
    mBuilder.buildDisplay(display);
    mBuilder.buildOS();
  }
}
/**
 * テストコード
 */
public class Test {
  public static void main(String[] args){
    //ビルダー
    Builder builder = new MacbookBuilder();
    //Director
    Director pcDirector = new Director(builder);
    //パッケージ構築プロセス
    pcDirector.construct("インテルマザーボード","レティナディスプレイ");
    //コンピュータを構築し、関連情報を出力します
    System.out.println("Computer Info : " + builder.create().toString());
  }
}

出力結果:

Computer Info : Computer [mBoard=インテルマザーボード, mDisplay=レティナディスプレイ, mOS=Mac OS X 10]

この例では、具体的なMacbookBuilderを使用してMacbookオブジェクトを構築し、Directorは複雑な製品オブジェクトの構築プロセスを封装し、外部には構築の詳細を隠します。BuilderとDirectorは、複雑なオブジェクトの構築とその表現を分離し、同じ構築プロセスで異なるオブジェクトを作成できます。

実際の開発プロセスでは、Director役割がしばしば省略されます。直接Builderを使用してオブジェクトのアセンブリを行い、このBuilderは通常リンク式呼び出しであり、各setterメソッドが自身を返す(つまりreturn this)ことが重要です。これによりsetterメソッドがリンク式呼び出し可能になります。以下のようになります:

new TestBuilder()
  .setA("A")
  .create();

この形では、Director役割を排除し、全体の構造もよりシンプルになり、Productオブジェクトのアセンブリプロセスをより詳細に制御できます。

六、Builderパターンのバリエーション——リンク式呼び出し

public class User {
  private final String name;     //必須
  private final String cardID;    //必須
  private final int age;       //オプション
  private final String address;   //オプション
  private final String phone;    //オプション
  private User(UserBuilder userBuilder){
    this.name=userBuilder.name;
    this.cardID=userBuilder.cardID;
    this.age=userBuilder.age;
    this.address=userBuilder.address;
    this.phone=userBuilder.phone;
  }
  public String getName() {
    return name;
  }
  public String getCardID() {
    return cardID;
  }
  public int getAge() {
    return age;
  }
  public String getAddress() {}}
    return address;
  }
  public String getPhone() {
    return phone;
  }
  public static class UserBuilder{
    private final String name;
    private final String cardID;
    private int age;
    private String address;
    private String phone;
    public UserBuilder(String name,String cardID){
      this.name=name;
      this.cardID=cardID;
    }
    public UserBuilder age(int age){
      this.age=age;
      return this;
    }
    public UserBuilder address(String address){
      this.address=address;
      return this;
    }
    public UserBuilder phone(String phone){
      this.phone=phone;
      return this;
    }
    public User build(){
      return new User(this);
    }
  }
}

注意すべき点:

Userクラスのコンストラクタはプライベートであり、呼び出し者はUserオブジェクトを直接作成できません。

Userクラスの属性は変更不可です。すべての属性にはfinal修飾子が追加され、構造メソッドで値が設定され、外部にはgetterメソッドのみが提供されます。

Builderの内部クラスのコンストラクタは必須の引数を受け取り、これらの必須の引数にはfinal修飾子が使用されています。

呼び出し方法:

new User.UserBuilder("Jack","10086)
    .age(25)
    .address("GuangZhou")
    .phone("13800138000")
    .build();

前のコンストラクタとsetter比べて/getterメソッドは二つの方法があり、可読性が高くなります。唯一の問題点は、余分なBuilderオブジェクトが生成され、メモリを消費することです。しかし、多くの場合、Builder内部クラスは静的修飾子(static)を使用しているため、この問題はあまり重要ではありません。

スレッドセーフについて

Builderパターンはスレッドセーフではありません。Builder内部クラス内で引数の合法性を確認する場合、オブジェクトの作成が完了した後に確認する必要があります。

正しいサンプル:

public User build() {
 User user = new user(this);
 if (user.getAge() > 120) {
  throw new IllegalStateException("Age out of range"); // 同期安全
 }
 return user;
}

エラーサンプル:

public User build() {
 if (age > 120) {
  throw new IllegalStateException("Age out of range"); // 非同期安全
 }
 return new User(this);
}

七、Builderパターンの使用例

1、AndroidにおけるAlertDialog.Builder

private void showDialog(){
    AlertDialog.Builder builder=new AlertDialog.Builder(context);
    builder.setIcon(R.drawable.icon);
    builder.setTitle("Title");
    builder.setMessage("Message");
    builder.setPositiveButton("Button"1", new DialogInterface.OnClickListener() {
      @Override
      public void onClick(DialogInterface dialog, int which) {
        //TODO
      }
    });
    builder.setNegativeButton("Button"2", new DialogInterface.OnClickListener() {
      @Override
      public void onClick(DialogInterface dialog, int which) {
        //TODO
      }
    });
    builder.create().show();
}

2、OkHttpにおけるOkHttpClientの作成

OkHttpClient okHttpClient = new OkHttpClient.Builder()
         .cache(getCache())
         .addInterceptor(new HttpCacheInterceptor())
         .addInterceptor(new LogInterceptor())
         .addNetworkInterceptor(new HttpRequestInterceptor())
         .build();

3、RetrofitにおけるRetrofitオブジェクトの作成

Retrofit retrofit = new Retrofit.Builder()
     .client(createOkHttp())
    .addConverterFactory(GsonConverterFactory.create())
     .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
     .baseUrl(BASE_URL)
     .build();

実際の使用では、Directorロールは省略され、多くのフレームワークのソースコードでBuilderパターンに関連する場合、多くの場合、標準的なGOFのBuilderパターンではなく、よりシンプルな後者を選択しています。

第8章 优点缺点

优点:

良い封装性があり、クライアントは製品の内部実装の詳細を知る必要がありません

建造者は独立しており、拡張性が高い

欠点:

余分なBuilderオブジェクト、Directorオブジェクトが生成され、メモリを消費します

Androidに関連する内容に興味を持つ読者の方は、本サイトの特集を確認してください:《Android開発入門と進階教程》、《Androidデバッグ技術とよくある問題の解決方法集》、《Android基本コンポーネントの使用法総覧》、《AndroidビューViewの技術的な総覧》、《Androidレイアウトlayoutの技術的な総覧》および《Androidコントロールの使用法総覧》

本稿の内容が皆様のAndroidプログラム設計に役立つことを願っています。

声明:本文の内容はインターネットから取得しており、著作権者に帰属します。インターネットユーザーが自発的に貢献し、アップロードしたものであり、本サイトは所有権を持ちません。また、人工的な編集処理もなく、関連する法的責任も負いません。著作権侵害が疑われる内容を見つけた場合は、メールで notice#w までお知らせください。3codebox.com(メールを送信する際には、#を@に置き換えてください。報告を行い、関連する証拠を提供してください。一旦確認がとれましたら、本サイトは即座に侵害が疑われるコンテンツを削除します。)

おすすめ