SoFunction
Updated on 2025-03-11

Android design pattern analysis of Builder pattern

Design patterns are often needed in daily development, but there are 23 design patterns. How to understand these design patterns and be able to be easily applied in actual development? Let’s learn and apply it with me along the book “Android Source Code Design Pattern Analysis and Practical Practice”!

Today we are going to talk about Builder mode (Builder mode)

definition

Separate the construction of a complex object from its representation, so that the same construction process can create different representations

Use scenarios

When initializing an object is particularly complicated, such as many parameters and many parameters have default values
The same method, different execution order, produce different event results
Multiple parts or parts can be assembled into one object, but the running effect produced is different
The product class is very complex, or the call order in the product class has different effects. It is very suitable to use the builder model at this time.

Use Example

AlertDialog
universal-image-loader

accomplish

Key points of implementation

In short, it is to encapsulate multiple attributes that need to be set through the set method in a configuration class.
Each attribute should have a default value
The specific set method is placed in the internal class Builder class of the configuration class, and each set method returns itself for chain calls.

Implementation method

Let’s take our image loading framework ImageLoder as an example to see the benefits of Builder mode

ImageLoader without Builder mode

public class ImageLoader {
 //Picture loading configuration private int loadingImageId;
 private int loadingFailImageId;

 // Image cache, depend on interface ImageCache mImageCache = new MemoryCache();

 // Thread pool, the number of threads is the number of CPUs ExecutorService mExecutorService = (().availableProcessors());

 //Omit the singleton mode implementation
 /**
   * Set image cache
   * @param cache
   */
 public void setImageCache(ImageCache cache) {
  mImageCache = cache;
 }

 /**
   * Set the picture displayed during image loading
   * @param resId
   */
 public Builder setLoadingPlaceholder(int resId) {
  loadingImageId = resId;
 }

 /**
   * Set the picture displayed when loading failed
   * @param resId
   */
 public Builder setLoadingFailPlaceholder(int resId) {
  loadingFailImageId = resId;
 }

 /**
   * Show pictures
   * @param imageUrl
   * @param imageView
   */
 public void displayImage(String imageUrl, ImageView imageView) {
  Bitmap bitmap = (imageUrl);
  if (bitmap != null) {
   (bitmap);
   return;
  }
  // The image is not cached, submit it to the thread pool to download  submitLoadRequest(imageUrl, imageView);
 }

 /**
   * Download the picture
   * @param imageUrl
   * @param imageView
   */
 private void submitLoadRequest(final String imageUrl, final ImageView imageView) {
  (loadingImageId);
  (imageUrl);
  (new Runnable() {
   @Override
   public void run() {
    Bitmap bitmap = downloadImage(imageUrl);
    if (bitmap == null) {
     (loadingFailImageId);
     return;
    }
    if ((())) {
     (bitmap);
    }
    (imageUrl, bitmap);
   }
  });
 }

 /**
   * Download the picture
   * @param imageUrl
   * @return
   */
 private Bitmap downloadImage(String imageUrl) {
  Bitmap bitmap = null;
  //Omit some download code  return bitmap;
 }
}

From the above code, we can see that whenever a setting option is needed, the code of ImageLoader needs to be modified, which violates the principle of opening and closing, and there will be more and more codes in ImageLoader, which is not conducive to maintenance
Let's take a look at how to use the Builder mode to transform ImageLoader

First, put the ImageLoader settings in a separate configuration class, and each set method returns this, thereby achieving the purpose of chain calling.

public class ImageLoaderConfig {
 // Image cache, depend on interface public ImageCache mImageCache = new MemoryCache();

 //Loading and loading image configuration objects when loading pictures and failing to load public DisplayConfig displayConfig = new DisplayConfig();

 //The number of threads, the default is +1 CPU number; public int threadCount = ().availableProcessors() + 1;

 private ImageLoaderConfig() {
 }


 /**
   * Builder for configuration class
   */
 public static class Builder {
  // Image cache, depend on interface  ImageCache mImageCache = new MemoryCache();

  //Loading and loading image configuration objects when loading pictures and failing to load  DisplayConfig displayConfig = new DisplayConfig();

  //The number of threads, the default is +1 CPU number;  int threadCount = ().availableProcessors() + 1;

  /**
    * Set the number of threads
    * @param count
    * @return
    */
  public Builder setThreadCount(int count) {
   threadCount = (1, count);
   return this;
  }

  /**
    * Set image cache
    * @param cache
    * @return
    */
  public Builder setImageCache(ImageCache cache) {
   mImageCache = cache;
   return this;
  }

  /**
    * Set the picture displayed during image loading
    * @param resId
    * @return
    */
  public Builder setLoadingPlaceholder(int resId) {
    = resId;
   return this;
  }

  /**
    * Set the picture displayed when loading failed
    * @param resId
    * @return
    */
  public Builder setLoadingFailPlaceholder(int resId) {
    = resId;
   return this;
  }

  void applyConfig(ImageLoaderConfig config) {
    = ;
    = ;
    = ;
  }

  /**
    * Create configuration objects based on the already set properties
    * @return
    */
  public ImageLoaderConfig create() {
   ImageLoaderConfig config = new ImageLoaderConfig();
   applyConfig(config);
   return config;
  }
 }
}

Modification of ImageLoader

public class ImageLoader {
 //Picture loading configuration ImageLoaderConfig mConfig;

 // Image cache, depend on interface ImageCache mImageCache = new MemoryCache();

 // Thread pool, the number of threads is the number of CPUs ExecutorService mExecutorService = (().availableProcessors());

 //Omit the singleton mode implementation
 //Initialize ImageLoader public void init(ImageLoaderConfig config) {
  mConfig = config;
  mImageCache = ;
 }

 /**
   * Show pictures
   * @param imageUrl
   * @param imageView
   */
 public void displayImage(String imageUrl, ImageView imageView) {
  Bitmap bitmap = (imageUrl);
  if (bitmap != null) {
   (bitmap);
   return;
  }
  // The image is not cached, submit it to the thread pool to download  submitLoadRequest(imageUrl, imageView);
 }

 /**
   * Download the picture
   * @param imageUrl
   * @param imageView
   */
 private void submitLoadRequest(final String imageUrl, final ImageView imageView) {
  ();
  (imageUrl);
  (new Runnable() {
   @Override
   public void run() {
    Bitmap bitmap = downloadImage(imageUrl);
    if (bitmap == null) {
     ();
     return;
    }
    if ((())) {
     (bitmap);
    }
    (imageUrl, bitmap);
   }
  });
 }

 /**
   * Download the picture
   * @param imageUrl
   * @return
   */
 private Bitmap downloadImage(String imageUrl) {
  Bitmap bitmap = null;
  //Omit some download code  return bitmap;
 }
}

Is the call form very familiar?

ImageLoaderConfig config = new ()
  .setImageCache(new MemoryCache())
  .setThreadCount(2)
  .setLoadingFailPlaceholder(.loading_fail)
  .setLoadingPlaceholder()
  .create();
().init(config);

Summarize

When the built object needs a lot of configuration, you can consider the Builder mode, which can avoid too many set methods, and at the same time isolate the configuration process from the target class, making the code structure clearer

The most commonly used implementation form of the Builder mode is implemented through chain calls, which is more concise and intuitive.

Source code address:/snowdream1314/ImageLoader

The above is all the content of this article. I hope it will be helpful to everyone's study and I hope everyone will support me more.