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

Beispiel zur Erklärung des Builder-Patterns im Android-Programmierungsentwurf

Dieser Artikel beschreibt das Beispiel von Android-Programmierungsdesignpattern Builder-Pattern. Hiermit geteilt, um alle zu referenzieren, wie folgt:

Eins, Einführung

Das Builder-Pattern ist ein Schritt-für-Schritt-Entwurfsmuster zum Erstellen eines komplexen Objekts, das es dem Benutzer ermöglicht, den Konstruktionsprozess des Objekts in unbekannter Internalität genauer zu steuern. Dieses Muster ist dazu da, den Konstruktionsprozess und die Teile eines komplexen Objekts zu entkoppeln, so dass der Konstruktionsprozess und die Darstellung der Teile voneinander getrennt werden.

Weil ein komplexes Objekt viele viele Bestandteile hat, z.B. ein Auto hat Räder, Lenkräder, Motoren und verschiedene kleine Teile usw., wie man diese Teile zu einem Auto zusammenbaut, dieser Zusammenbauvorgang ist sehr lang und komplex. Für diese Situation kann das Builder-Pattern verwendet werden, um die Teile und den Zusammenbauvorgang zu trennen, so dass der Konstruktionsprozess und die Teile beide frei erweitert werden können und die Kopplung zwischen beiden auf das Minimum reduziert wird.

Zwei, Definition

Die Konstruktion eines komplexen Objekts von seiner Darstellung zu trennen, so dass der gleiche Konstruktionsprozess unterschiedliche Darstellungen erstellen kann.

Drei, Anwendungsszenarien

(1Wenn gleiche Methoden unterschiedliche Ausführungsreihenfolgen haben und unterschiedliche Ereignisergebnisse erzeugen.

(2Wenn mehrere Teile oder Bauteile in ein Objekt eingebaut werden können, aber unterschiedliche Laufzeitergebnisse erzeugt werden.

(3Wenn die Produktklasse sehr komplex ist oder unterschiedliche Aufrufreihenfolgen in der Produktklasse unterschiedliche Wirkungen erzeugen, ist das Builder-Pattern in diesem Fall sehr angebracht.

(4Wenn die Initialisierung eines Objekts besonders komplex ist, z.B. viele Parameter und viele davon haben Standardwerte.

Vier, UML-Klassendiagramm des Builder-Patterns

角色介绍:

Product产品类——产品的抽象类;

Builder——抽象Builder类,规范产品的组建,一般是由子类实现具体的组建过程;

ConcreateBuilder——具体的Builder类;

Director——统一组装过程;

五、Builder模式的简单实现

计算机的组装过程较为复杂,并且组装顺序是不固定的,为了易于理解,我们把计算机的组装过程简化为构建主机、设置操作系统、设置显示器3各个部分,然后通过Director和具体的Builder来构建计算机对象。

Example code:

/**
 * 计算机抽象类,即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();
  /**
   * 创建Computer
   * @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类,负责构造Computer
 */
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("英特尔主板","Retina显示器");
    //Build a computer and output relevant information
    System.out.println("Computer Info: ", + builder.create().toString());
  }
}

Output result:

Computer Info: Computer [mBoard=Intel Motherboard, mDisplay=Retina Display, mOS=Mac OS X 10]

In the above example, the MacbookBuilder is used to build the Macbook object, and the Director encapsulates the process of building complex product objects, hiding the construction details. Together with the Director, the Builder separates the construction of a complex object from its representation, allowing the same construction process to create different objects.

It is worth noting that in the process of actual development, the Director role is often omitted. Instead, a Builder is used directly to assemble objects, which is usually a chained invocation. The key point is that each setter method returns itself, that is, return this, which allows for chained invocation of setter methods. The code is roughly as follows:

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

In this form, not only is the Director role removed, but the entire structure is also simpler, and it allows for more precise control over the assembly process of the Product object.

6. Variant of Builder Pattern - Chained Invocation

Example code:

public class User {
  private final String name;     //mandatory
  private final String cardID;    //mandatory
  private final int age;       //optional
  private final String address;   //optional
  private final String phone;    //optional
  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);
    }
  }
}

Zu beachtende Punkte:

Der Konstruktor der User-Klasse ist privat, der Aufrufer kann das User-Objekt nicht direkt erstellen.

Die Eigenschaften der User-Klasse sind nicht änderbar. Alle Eigenschaften sind mit dem final-Modifikator versehen und werden im Konstruktor festgelegt. Und nur Getter-Methoden werden nach außen bereitgestellt.

Der Konstruktor der Builder-Innerklasse akzeptiert nur erforderliche Parameter und diese erforderlichen Parameter sind mit dem final-Modifikator versehen.

Aufrufweise:

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

Im Vergleich zu den vorherigen Methoden über den Konstruktor und die Setter/Es gibt zwei Arten von Getter-Methoden, die Lesbarkeit erhöhen. Das einzige mögliche Problem könnte sein, dass zusätzliche Builder-Objekte erstellt werden, was den Speicher verbraucht. Allerdings verwenden wir in den meisten Fällen Builder-Innerklassen, die mit static versehen sind, daher ist dieses Problem nicht so wichtig.

Über die Thread-Sicherheit

Der Builder-Pattern ist nicht thread-sicher. Wenn man innerhalb der Builder-Innerklasse die Gültigkeit eines Parameters überprüfen muss, muss dies nach der Erstellung des Objekts erfolgen.

正确示例:

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模式,而是选择了结构更加简单的后者。

八、优缺点

优点:

良好的封装性,使得客户端不需要知道产品内部实现的细节

建造者独立,扩展性强

缺点:

产生多余的Builder对象、Director对象,消耗内存

更多关于Android相关内容感兴趣的读者可查看本站专题:《Android开发入门与进阶教程》、《Android调试技巧与常见问题解决方法汇总》、《Android基本组件用法总结》、《Android视图View技巧总结》、《Android布局layout技巧总结》及《Android控件用法总结》

希望本文所述对大家的Android程序设计有所帮助。

声明:本文内容来源于网络,版权归原作者所有。内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:notice#oldtoolbag.com(在发送邮件时,请将#更换为@)进行举报,并提供相关证据。一经查实,本站将立即删除涉嫌侵权内容。

Gefällt mir