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

Ejemplo detallado del patrón de diseño Builder en programación Android

Este artículo describe un ejemplo de diseño de patrones de programación Android: el patrón Builder. Lo compartimos con todos para su referencia, como se muestra a continuación:

Uno, Introducción

El patrón Builder es un patrón de creación que crea un objeto complejo paso a paso, permitiendo al usuario controlar más detalladamente el flujo de construcción del objeto sin conocer los detalles internos de la construcción. Este patrón es para desacoplar el proceso de construcción del objeto de sus componentes, haciendo que el proceso de construcción y la representación de los componentes se separen.

Debido a que un objeto complejo tiene muchas partes componentes, por ejemplo, un coche tiene ruedas, volante, motor, y varios componentes pequeños, etc., cómo ensamblar estos componentes en un coche, este proceso de ensamblaje es muy largo y complejo. Para este tipo de situación, para ocultar los detalles de la implementación externa durante el proceso de construcción, se puede usar el patrón Builder para separar las piezas y el proceso de ensamblaje, haciendo que el proceso de construcción y las piezas puedan expandirse libremente, y reducir la耦合 al mínimo.

Dos, Definición

Separar la construcción de un objeto complejo de su representación, permitiendo que el mismo proceso de construcción cree diferentes representaciones.

Tres, Escenarios de uso

(1Cuando diferentes órdenes de ejecución de métodos producen diferentes resultados de eventos.

(2Cuando varios componentes o piezas pueden ensamblarse en un objeto, pero los resultados de ejecución no son los mismos.

(3Cuando la clase de producto es muy compleja o el orden de las llamadas en la clase de producto produce diferentes efectos, en este caso, el patrón Builder es muy adecuado.

(4Cuando la inicialización de un objeto es particularmente compleja, como tener muchos parámetros y muchos de ellos tienen valores predeterminados.

Cuatro, Diagrama de clase UML del patrón Builder

Introducción a los roles:

Clase Producto—clase abstracta del producto;

Builder——clase Builder abstracta, que normaliza la composición del producto, generalmente implementada por subclases para la composición específica del proceso;

ConcreateBuilder——clase Builder específica;

Director——proceso de ensamblaje unificado;

Cinco, implementación simple del patrón Builder

El proceso de ensamblaje de la computadora es bastante complejo y el orden de ensamblaje no es fijo, para facilitar la comprensión, simplificamos el proceso de ensamblaje de la computadora en la construcción de la máquina principal, la configuración del sistema operativo, y la configuración del monitor3partes, luego se construye el objeto de computadora a través de Director y Builder específico.

Ejemplo de código:

/**
 * clase abstracta de computadora, es decir, el rol de Producto
 */
public abstract class Computer {
  protected String mBoard;
  protected String mDisplay;
  protected String mOS;
  protected Computer(){}
  /**
   * establecer la placa base
   * @param board
   */
  public void setBoard(String board){
    this.mBoard = board;
  }
  /**
   * establecer el monitor
   * @param display
   */
  public void setDisplay(String display){
    this.mDisplay = display;
  }
  /**
   * Set operating system
   */
  public abstract void setOS();
  @Override
  public String toString(){
    return "Computer [mBoard=" + mBoard + ", mDisplay=" + mDisplay + ", mOS=" + mOS + "]";
  }
}
/**
 * clase específica Computer, Macbook
 */
public class Macbook extends Computer {
  protected Macbook(){}
  @Override
  public void setOS() {
    mOS = "Mac OS X 10";
  }
}
/**
 * clase Builder abstracta
 */
public abstract class Builder {
  /**
   * establecer la máquina principal
   * @param board
   */
  public abstract void buildBoard(String board);
  /**
   * establecer el monitor
   * @param display
   */
  public abstract void buildDisplay(String display);
  /**
   * Set operating system
   */
  public abstract void buildOS();
  /**
   * Create Computer
   * @return
   */
  public abstract Computer create();
}
/**
 * Specific Builder class, 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 class, responsible for constructing Computer
 */
public class Director {
  Builder mBuilder = null;
  public Director(Builder builder){
    mBuilder = builder;
  }
  /**
   * Construct object
   * @param board Motherboard
   * @param display Display
   */
  public void construct(String board, String display){
    mBuilder.buildBoard(board);
    mBuilder.buildDisplay(display);
    mBuilder.buildOS();
  }
}
/**
 * Test code
 */
public class Test {
  public static void main(String[] args){
    //Builder
    Builder builder = new MacbookBuilder();
    //Director
    Director pcDirector = new Director(builder);
    //Encapsulation construction process
    pcDirector.construct("Intel motherboard","Retina display");
    //Construir una computadora, mostrar información relevante
    System.out.println("Computer Info: ", + builder.create().toString());
  }
}

Resultados de salida:

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

En el ejemplo anterior, se construye el objeto Macbook a través de MacbookBuilder específico, y Director encapsula el proceso de construcción del objeto producto complejo, ocultando los detalles de construcción. Builder y Director separan la construcción de un objeto complejo de su representación, permitiendo que el mismo proceso de construcción cree diferentes objetos.

Es importante destacar que en el proceso de desarrollo real, el rol de Director a menudo se omite. Y se utiliza directamente un Builder para ensamblar el objeto, que generalmente es una llamada en cadena. El punto clave es que cada método setter devuelve a sí mismo, es decir, return this, lo que permite que los métodos setter se llamen en cadena. El código es más o menos así:

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

De esta manera, no solo se elimina el rol de Director, sino que toda la estructura también se vuelve más simple y permite un control más fino del proceso de ensamblaje del objeto Product.

Seis, variante del patrón Builder - Llamadas en cadena

Ejemplo de código:

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

Puntos a tener en cuenta:

El constructor de la clase User es privado, los llamadores no pueden crear directamente el objeto User.

Las propiedades de la clase User son inmutables. Todas las propiedades han sido modificadas con el modificador final y se han configurado los valores en el constructor. Además, solo se proporcionan métodos getters.

El método de constructor de la clase interna de Builder solo recibe parámetros obligatorios, y estos parámetros obligatorios están modificados con el modificador final.

Método de llamada:

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

En comparación con lo anterior a través del constructor y setter/Hay dos formas de métodos getter, lo que mejora la legibilidad. El único problema posible es que puede generar objetos Builder adicionales, lo que consume memoria. Sin embargo, en la mayoría de los casos, la clase interna de Builder que usamos está modificada como estática (static), por lo que este problema no es muy significativo.

Acerca de la seguridad en hilos

El patrón Builder no es seguro para múltiples hilos, si se necesita verificar la legalidad de un parámetro dentro de la clase interna de Builder, es necesario verificarla después de que se haya completado la creación del objeto.

正确示例:

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();

En la práctica, se ha omitido el rol de Director, en muchos códigos fuente de marcos, cuando se trata del patrón Builder, la mayoría no es el patrón Builder clásico GOF, sino que se ha elegido el último con una estructura más simple.

Ocho, ventajas y desventajas

Ventajas:

Buena encapsulación, lo que hace que el cliente no necesite saber los detalles de la implementación interna del producto

Constructor independiente, alta capacidad de expansión

Desventajas:

Generación de objetos Builder adicionales, objetos Director, consumo de memoria

Los lectores interesados en más contenido relacionado con Android pueden ver la sección especial de este sitio: 'Tutorial de Inicio y Avanzado de Desarrollo de Android', 'Técnicas de Depuración y Resolución de Problemas Comunes en Android', 'Resumen de Uso de Componentes Básicos de Android', 'Resumen de Técnicas de Vista View en Android', 'Resumen de Técnicas de Layout en Android' y 'Resumen de Uso de Controles en Android'.

Espero que lo descrito en este artículo pueda ayudar a todos en el diseño de programas Android.

Declaración: Este artículo se ha redactado en línea, pertenece al propietario original, el contenido se ha contribuido y subido por los usuarios de Internet, este sitio web no posee los derechos de propiedad, no ha sido editado por humanos y no asume ninguna responsabilidad legal relacionada. Si encuentra contenido sospechoso de infracción de derechos de autor, por favor envíe un correo electrónico a: notice#w3Declaración: El contenido de este artículo se ha obtenido de la red, pertenece al propietario original, el contenido se ha contribuido y subido por los usuarios de Internet, este sitio web no posee los derechos de propiedad, no ha sido editado por humanos y no asume ninguna responsabilidad legal relacionada. Si encuentra contenido sospechoso de infracción de derechos de autor, por favor envíe un correo electrónico a: notice#w

Te gustará