建造者模式使用也有一段時間了,之前對它的概念也是雲里霧里的,只是會用,卻也拿不定主意什么時候用好,今天突然間有所領悟,特記錄之。首先說說它的定義,基本上看到所有關於建造者模式的介紹大概都是這樣說的:
意圖:將一個復雜的構建與其表示相分離,使得同樣的構建過程可以創建不同的表示。 主要解決:主要解決在軟件系統中,有時候面臨着"一個復雜對象"的創建工作,其通常由各個部分的子對象用一定的算法構成;由於需求的變化,這個復雜對象的各個部分經常面臨着劇烈的變化,但是將它們組合在一起的算法卻相對穩定。 何時使用:一些基本部件不會變,而其組合經常變化的時候。 如何解決:將變與不變分離開。 關鍵代碼:建造者:創建和提供實例,導演:管理建造出來的實例的依賴關系。 應用實例: 1、去肯德基,漢堡、可樂、薯條、炸雞翅等是不變的,而其組合是經常變化的,生成出所謂的"套餐"。 2、JAVA 中的 StringBuilder。 優點: 1、建造者獨立,易擴展。 2、便於控制細節風險。 缺點: 1、產品必須有共同點,范圍有限制。 2、如內部變化復雜,會有很多的建造類。 使用場景: 1、需要生成的對象具有復雜的內部結構。 2、需要生成的對象內部屬性本身相互依賴。 注意事項:與工廠模式的區別是:建造者模式更加關注與零件裝配的順序。
然后隨便舉了個例子完事,且不說舉的例子是否恰當,能讓人看懂,光是上面啰里巴嗦的一段話就讓人頭大,什么是變與不變?什么叫基本部件不會變,組合經常變?后來想想吧,也可能是自己蠢,反正看完了這種定義,心里有無數個懵逼。用最簡單的Person類舉例,先復習下它的寫法吧:
首先要有這樣一個實體類:
public class Person { private String name; private int age; private double height; private double weight; }
實體類中包含了這個實體的各個屬性,比如上面Person類就包含了name, age, height, weight等屬性,像android的AlertDialog,可能還會有title, message, PositiveButtonListener, NegativeButtonListener, 這些都是這個實體類的組成部分。
其次要提供一個公共的靜態內部類Builder 和 一個私有的構造方法。靜態內部類Builder中包含有與這個實體類相同的成員變量,提供私有構造方法的目的是只允許外部通過Builder類來構造這個實體類,不能通過實體類默認的構造函數來構造。
public class Person { private String mName; private int mAge; private double mHeight; private double mWeight; private Person(Builder builder) { this.mName = builder.mName; this.mAge = builder.mAge; this.mHeight = builder.mHeight; this.mWeight = builder.mWeight; } public static class Builder { private String mName; private int mAge; private double mHeight; private double mWeight; } }
然后要給Builder類添加一個公共的build()方法供別人調用,畢竟你把實體類默認的公共構造方法給寫成private的了,總得要給別人再提供一種途徑去實體化這個實體類吧。
public class Person { private String mName; private int mAge; private double mHeight; private double mWeight; private Person(Builder builder) { this.mName = builder.mName; this.mAge = builder.mAge; this.mHeight = builder.mHeight; this.mWeight = builder.mWeight; } public static class Builder { private String mName; private int mAge; private double mHeight; private double mWeight; public Person build() { return new Person(this); } } }
既然選擇了使用Builder類來構造未來的實體對象Person,那還需要添加一些具體的setter()方法來幫助構建這個Person, 以前是在Person中寫setter()方法,現在既然使用了Builder類來構建,那么這些方法理應移動中Builder中,而且每個setter的東西都是實體類的組成部分,本身並不相關,為了將來能夠使用鏈式編程的寫法,在返回時直接返回這個Builder類。
public class Person { private String mName; private int mAge; private double mHeight; private double mWeight; private Person(Builder builder) { this.mName = builder.mName; this.mAge = builder.mAge; this.mHeight = builder.mHeight; this.mWeight = builder.mWeight; } public static class Builder { private String mName; private int mAge; private double mHeight; private double mWeight; public Builder setName(String name) { mName = name; return this; } public Builder setAge(int age) { mAge = age; return this; } public Builder setHeight(double height) { mHeight = height; return this; } public Builder setWeight(double weight) { mWeight = weight; return this; } public Person build() { return new Person(this); } } }
這個地方有個小驚喜,我使用android studio 在Builder里創建setter()方法時,居然能自動return this, 只需要選擇下拉列表中的“Builder”選項即可,看來Jetbrains的工程師已經考慮到這一點,果然是智能的Java IDE,JB大法好!
考慮到未來還有可能會讀取這個Person類上的成員變量,所以還要給這個Person提供幾個getter()方法,方便之后讀取。
public class Person { private String mName; private int mAge; private double mHeight; private double mWeight; private Person(Builder builder) { this.mName = builder.mName; this.mAge = builder.mAge; this.mHeight = builder.mHeight; this.mWeight = builder.mWeight; } public String getName() { return mName; } public int getAge() { return mAge; } public double getHeight() { return mHeight; } public double getWeight() { return mWeight; } public static class Builder { private String mName; private int mAge; private double mHeight; private double mWeight; public Builder setName(String name) { mName = name; return this; } public Builder setAge(int age) { mAge = age; return this; } public Builder setHeight(double height) { mHeight = height; return this; } public Builder setWeight(double weight) { mWeight = weight; return this; } public Person build() { return new Person(this); } } }
如此這個使用建造者模式的Person類就寫好,我們仿照之前調用Android 創建AlertDialog時的鏈式編程的方式測試一下:
/** * This Demo is used to demonstrate the use of Builder Pattern */ public class BuildPatternApiUseDemoActivity extends AppCompatActivity { public static final String TAG = "xp.chen"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_build_pattern_api_use_demo); Person person = new Person.Builder() .setName("ZhangSan") .setAge(11) .setHeight(18.0d) .setWeight(20.0d) .build(); Log.i(TAG, "name: "+person.getName()+", age: "+person.getAge()+", height: "+person.getHeight()+", weight: "+person.getWeight()); } }
Log如下:
I/xp.chen: name: ZhangSan, age: 11, height: 18.0, weight: 20.0
以上就是建造者模式的全部創建過程,過程本身並不復雜,麻煩的是不知道什么時候用,胡亂用的話只會適得其反,今天又遇到了一種使用建造者模式的場景,所以結合之前的場景總結下:
一. 作為篩選條件時
經常會在App中看到這樣的界面:
這是一個典型的過濾篩選界面,上面的界面是用於根據各個選項(設備名稱、藍牙地址、服務UUID、線損值)來篩選搜索到的藍牙列表的。這個時候就很適合使用建造者模式來創建一個篩選類,在篩選類中定義各個篩選條件的成員變量,因為篩選條件的數量是不確定的,比如我只想根據設備名稱搜索,我只想根據藍牙地址搜索,或者我既想根據設備名稱又想根據藍牙地址來搜索,等等, 這個時候就可以通過給Builder設置不同的篩選條件來構造出一個符合預期的篩選對象,然后將篩選對象傳過去即可。有個現成的例子就是Android本身定義了這樣的一個篩選類,位於:android-sdk\sources\android-28\android\bluetooth\le\ScanFilter.java, 它里面就用到了建造者模式。

/* * Copyright (C) 2014 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.bluetooth.le; import android.annotation.Nullable; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.os.Parcel; import android.os.ParcelUuid; import android.os.Parcelable; import com.android.internal.util.BitUtils; import java.util.Arrays; import java.util.List; import java.util.Objects; import java.util.UUID; /** * Criteria for filtering result from Bluetooth LE scans. A {@link ScanFilter} allows clients to * restrict scan results to only those that are of interest to them. * <p> * Current filtering on the following fields are supported: * <li>Service UUIDs which identify the bluetooth gatt services running on the device. * <li>Name of remote Bluetooth LE device. * <li>Mac address of the remote device. * <li>Service data which is the data associated with a service. * <li>Manufacturer specific data which is the data associated with a particular manufacturer. * * @see ScanResult * @see BluetoothLeScanner */ public final class ScanFilter implements Parcelable { @Nullable private final String mDeviceName; @Nullable private final String mDeviceAddress; @Nullable private final ParcelUuid mServiceUuid; @Nullable private final ParcelUuid mServiceUuidMask; @Nullable private final ParcelUuid mServiceDataUuid; @Nullable private final byte[] mServiceData; @Nullable private final byte[] mServiceDataMask; private final int mManufacturerId; @Nullable private final byte[] mManufacturerData; @Nullable private final byte[] mManufacturerDataMask; /** @hide */ public static final ScanFilter EMPTY = new ScanFilter.Builder().build(); private ScanFilter(String name, String deviceAddress, ParcelUuid uuid, ParcelUuid uuidMask, ParcelUuid serviceDataUuid, byte[] serviceData, byte[] serviceDataMask, int manufacturerId, byte[] manufacturerData, byte[] manufacturerDataMask) { mDeviceName = name; mServiceUuid = uuid; mServiceUuidMask = uuidMask; mDeviceAddress = deviceAddress; mServiceDataUuid = serviceDataUuid; mServiceData = serviceData; mServiceDataMask = serviceDataMask; mManufacturerId = manufacturerId; mManufacturerData = manufacturerData; mManufacturerDataMask = manufacturerDataMask; } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(mDeviceName == null ? 0 : 1); if (mDeviceName != null) { dest.writeString(mDeviceName); } dest.writeInt(mDeviceAddress == null ? 0 : 1); if (mDeviceAddress != null) { dest.writeString(mDeviceAddress); } dest.writeInt(mServiceUuid == null ? 0 : 1); if (mServiceUuid != null) { dest.writeParcelable(mServiceUuid, flags); dest.writeInt(mServiceUuidMask == null ? 0 : 1); if (mServiceUuidMask != null) { dest.writeParcelable(mServiceUuidMask, flags); } } dest.writeInt(mServiceDataUuid == null ? 0 : 1); if (mServiceDataUuid != null) { dest.writeParcelable(mServiceDataUuid, flags); dest.writeInt(mServiceData == null ? 0 : 1); if (mServiceData != null) { dest.writeInt(mServiceData.length); dest.writeByteArray(mServiceData); dest.writeInt(mServiceDataMask == null ? 0 : 1); if (mServiceDataMask != null) { dest.writeInt(mServiceDataMask.length); dest.writeByteArray(mServiceDataMask); } } } dest.writeInt(mManufacturerId); dest.writeInt(mManufacturerData == null ? 0 : 1); if (mManufacturerData != null) { dest.writeInt(mManufacturerData.length); dest.writeByteArray(mManufacturerData); dest.writeInt(mManufacturerDataMask == null ? 0 : 1); if (mManufacturerDataMask != null) { dest.writeInt(mManufacturerDataMask.length); dest.writeByteArray(mManufacturerDataMask); } } } /** * A {@link android.os.Parcelable.Creator} to create {@link ScanFilter} from parcel. */ public static final Creator<ScanFilter> CREATOR = new Creator<ScanFilter>() { @Override public ScanFilter[] newArray(int size) { return new ScanFilter[size]; } @Override public ScanFilter createFromParcel(Parcel in) { Builder builder = new Builder(); if (in.readInt() == 1) { builder.setDeviceName(in.readString()); } if (in.readInt() == 1) { builder.setDeviceAddress(in.readString()); } if (in.readInt() == 1) { ParcelUuid uuid = in.readParcelable(ParcelUuid.class.getClassLoader()); builder.setServiceUuid(uuid); if (in.readInt() == 1) { ParcelUuid uuidMask = in.readParcelable( ParcelUuid.class.getClassLoader()); builder.setServiceUuid(uuid, uuidMask); } } if (in.readInt() == 1) { ParcelUuid servcieDataUuid = in.readParcelable(ParcelUuid.class.getClassLoader()); if (in.readInt() == 1) { int serviceDataLength = in.readInt(); byte[] serviceData = new byte[serviceDataLength]; in.readByteArray(serviceData); if (in.readInt() == 0) { builder.setServiceData(servcieDataUuid, serviceData); } else { int serviceDataMaskLength = in.readInt(); byte[] serviceDataMask = new byte[serviceDataMaskLength]; in.readByteArray(serviceDataMask); builder.setServiceData( servcieDataUuid, serviceData, serviceDataMask); } } } int manufacturerId = in.readInt(); if (in.readInt() == 1) { int manufacturerDataLength = in.readInt(); byte[] manufacturerData = new byte[manufacturerDataLength]; in.readByteArray(manufacturerData); if (in.readInt() == 0) { builder.setManufacturerData(manufacturerId, manufacturerData); } else { int manufacturerDataMaskLength = in.readInt(); byte[] manufacturerDataMask = new byte[manufacturerDataMaskLength]; in.readByteArray(manufacturerDataMask); builder.setManufacturerData(manufacturerId, manufacturerData, manufacturerDataMask); } } return builder.build(); } }; /** * Returns the filter set the device name field of Bluetooth advertisement data. */ @Nullable public String getDeviceName() { return mDeviceName; } /** * Returns the filter set on the service uuid. */ @Nullable public ParcelUuid getServiceUuid() { return mServiceUuid; } @Nullable public ParcelUuid getServiceUuidMask() { return mServiceUuidMask; } @Nullable public String getDeviceAddress() { return mDeviceAddress; } @Nullable public byte[] getServiceData() { return mServiceData; } @Nullable public byte[] getServiceDataMask() { return mServiceDataMask; } @Nullable public ParcelUuid getServiceDataUuid() { return mServiceDataUuid; } /** * Returns the manufacturer id. -1 if the manufacturer filter is not set. */ public int getManufacturerId() { return mManufacturerId; } @Nullable public byte[] getManufacturerData() { return mManufacturerData; } @Nullable public byte[] getManufacturerDataMask() { return mManufacturerDataMask; } /** * Check if the scan filter matches a {@code scanResult}. A scan result is considered as a match * if it matches all the field filters. */ public boolean matches(ScanResult scanResult) { if (scanResult == null) { return false; } BluetoothDevice device = scanResult.getDevice(); // Device match. if (mDeviceAddress != null && (device == null || !mDeviceAddress.equals(device.getAddress()))) { return false; } ScanRecord scanRecord = scanResult.getScanRecord(); // Scan record is null but there exist filters on it. if (scanRecord == null && (mDeviceName != null || mServiceUuid != null || mManufacturerData != null || mServiceData != null)) { return false; } // Local name match. if (mDeviceName != null && !mDeviceName.equals(scanRecord.getDeviceName())) { return false; } // UUID match. if (mServiceUuid != null && !matchesServiceUuids(mServiceUuid, mServiceUuidMask, scanRecord.getServiceUuids())) { return false; } // Service data match if (mServiceDataUuid != null) { if (!matchesPartialData(mServiceData, mServiceDataMask, scanRecord.getServiceData(mServiceDataUuid))) { return false; } } // Manufacturer data match. if (mManufacturerId >= 0) { if (!matchesPartialData(mManufacturerData, mManufacturerDataMask, scanRecord.getManufacturerSpecificData(mManufacturerId))) { return false; } } // All filters match. return true; } /** * Check if the uuid pattern is contained in a list of parcel uuids. * * @hide */ public static boolean matchesServiceUuids(ParcelUuid uuid, ParcelUuid parcelUuidMask, List<ParcelUuid> uuids) { if (uuid == null) { return true; } if (uuids == null) { return false; } for (ParcelUuid parcelUuid : uuids) { UUID uuidMask = parcelUuidMask == null ? null : parcelUuidMask.getUuid(); if (matchesServiceUuid(uuid.getUuid(), uuidMask, parcelUuid.getUuid())) { return true; } } return false; } // Check if the uuid pattern matches the particular service uuid. private static boolean matchesServiceUuid(UUID uuid, UUID mask, UUID data) { return BitUtils.maskedEquals(data, uuid, mask); } // Check whether the data pattern matches the parsed data. private boolean matchesPartialData(byte[] data, byte[] dataMask, byte[] parsedData) { if (parsedData == null || parsedData.length < data.length) { return false; } if (dataMask == null) { for (int i = 0; i < data.length; ++i) { if (parsedData[i] != data[i]) { return false; } } return true; } for (int i = 0; i < data.length; ++i) { if ((dataMask[i] & parsedData[i]) != (dataMask[i] & data[i])) { return false; } } return true; } @Override public String toString() { return "BluetoothLeScanFilter [mDeviceName=" + mDeviceName + ", mDeviceAddress=" + mDeviceAddress + ", mUuid=" + mServiceUuid + ", mUuidMask=" + mServiceUuidMask + ", mServiceDataUuid=" + Objects.toString(mServiceDataUuid) + ", mServiceData=" + Arrays.toString(mServiceData) + ", mServiceDataMask=" + Arrays.toString(mServiceDataMask) + ", mManufacturerId=" + mManufacturerId + ", mManufacturerData=" + Arrays.toString(mManufacturerData) + ", mManufacturerDataMask=" + Arrays.toString(mManufacturerDataMask) + "]"; } @Override public int hashCode() { return Objects.hash(mDeviceName, mDeviceAddress, mManufacturerId, Arrays.hashCode(mManufacturerData), Arrays.hashCode(mManufacturerDataMask), mServiceDataUuid, Arrays.hashCode(mServiceData), Arrays.hashCode(mServiceDataMask), mServiceUuid, mServiceUuidMask); } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null || getClass() != obj.getClass()) { return false; } ScanFilter other = (ScanFilter) obj; return Objects.equals(mDeviceName, other.mDeviceName) && Objects.equals(mDeviceAddress, other.mDeviceAddress) && mManufacturerId == other.mManufacturerId && Objects.deepEquals(mManufacturerData, other.mManufacturerData) && Objects.deepEquals(mManufacturerDataMask, other.mManufacturerDataMask) && Objects.equals(mServiceDataUuid, other.mServiceDataUuid) && Objects.deepEquals(mServiceData, other.mServiceData) && Objects.deepEquals(mServiceDataMask, other.mServiceDataMask) && Objects.equals(mServiceUuid, other.mServiceUuid) && Objects.equals(mServiceUuidMask, other.mServiceUuidMask); } /** * Checks if the scanfilter is empty * * @hide */ public boolean isAllFieldsEmpty() { return EMPTY.equals(this); } /** * Builder class for {@link ScanFilter}. */ public static final class Builder { private String mDeviceName; private String mDeviceAddress; private ParcelUuid mServiceUuid; private ParcelUuid mUuidMask; private ParcelUuid mServiceDataUuid; private byte[] mServiceData; private byte[] mServiceDataMask; private int mManufacturerId = -1; private byte[] mManufacturerData; private byte[] mManufacturerDataMask; /** * Set filter on device name. */ public Builder setDeviceName(String deviceName) { mDeviceName = deviceName; return this; } /** * Set filter on device address. * * @param deviceAddress The device Bluetooth address for the filter. It needs to be in the * format of "01:02:03:AB:CD:EF". The device address can be validated using {@link * BluetoothAdapter#checkBluetoothAddress}. * @throws IllegalArgumentException If the {@code deviceAddress} is invalid. */ public Builder setDeviceAddress(String deviceAddress) { if (deviceAddress != null && !BluetoothAdapter.checkBluetoothAddress(deviceAddress)) { throw new IllegalArgumentException("invalid device address " + deviceAddress); } mDeviceAddress = deviceAddress; return this; } /** * Set filter on service uuid. */ public Builder setServiceUuid(ParcelUuid serviceUuid) { mServiceUuid = serviceUuid; mUuidMask = null; // clear uuid mask return this; } /** * Set filter on partial service uuid. The {@code uuidMask} is the bit mask for the * {@code serviceUuid}. Set any bit in the mask to 1 to indicate a match is needed for the * bit in {@code serviceUuid}, and 0 to ignore that bit. * * @throws IllegalArgumentException If {@code serviceUuid} is {@code null} but {@code * uuidMask} is not {@code null}. */ public Builder setServiceUuid(ParcelUuid serviceUuid, ParcelUuid uuidMask) { if (mUuidMask != null && mServiceUuid == null) { throw new IllegalArgumentException("uuid is null while uuidMask is not null!"); } mServiceUuid = serviceUuid; mUuidMask = uuidMask; return this; } /** * Set filtering on service data. * * @throws IllegalArgumentException If {@code serviceDataUuid} is null. */ public Builder setServiceData(ParcelUuid serviceDataUuid, byte[] serviceData) { if (serviceDataUuid == null) { throw new IllegalArgumentException("serviceDataUuid is null"); } mServiceDataUuid = serviceDataUuid; mServiceData = serviceData; mServiceDataMask = null; // clear service data mask return this; } /** * Set partial filter on service data. For any bit in the mask, set it to 1 if it needs to * match the one in service data, otherwise set it to 0 to ignore that bit. * <p> * The {@code serviceDataMask} must have the same length of the {@code serviceData}. * * @throws IllegalArgumentException If {@code serviceDataUuid} is null or {@code * serviceDataMask} is {@code null} while {@code serviceData} is not or {@code * serviceDataMask} and {@code serviceData} has different length. */ public Builder setServiceData(ParcelUuid serviceDataUuid, byte[] serviceData, byte[] serviceDataMask) { if (serviceDataUuid == null) { throw new IllegalArgumentException("serviceDataUuid is null"); } if (mServiceDataMask != null) { if (mServiceData == null) { throw new IllegalArgumentException( "serviceData is null while serviceDataMask is not null"); } // Since the mServiceDataMask is a bit mask for mServiceData, the lengths of the two // byte array need to be the same. if (mServiceData.length != mServiceDataMask.length) { throw new IllegalArgumentException( "size mismatch for service data and service data mask"); } } mServiceDataUuid = serviceDataUuid; mServiceData = serviceData; mServiceDataMask = serviceDataMask; return this; } /** * Set filter on on manufacturerData. A negative manufacturerId is considered as invalid id. * <p> * Note the first two bytes of the {@code manufacturerData} is the manufacturerId. * * @throws IllegalArgumentException If the {@code manufacturerId} is invalid. */ public Builder setManufacturerData(int manufacturerId, byte[] manufacturerData) { if (manufacturerData != null && manufacturerId < 0) { throw new IllegalArgumentException("invalid manufacture id"); } mManufacturerId = manufacturerId; mManufacturerData = manufacturerData; mManufacturerDataMask = null; // clear manufacturer data mask return this; } /** * Set filter on partial manufacture data. For any bit in the mask, set it the 1 if it needs * to match the one in manufacturer data, otherwise set it to 0. * <p> * The {@code manufacturerDataMask} must have the same length of {@code manufacturerData}. * * @throws IllegalArgumentException If the {@code manufacturerId} is invalid, or {@code * manufacturerData} is null while {@code manufacturerDataMask} is not, or {@code * manufacturerData} and {@code manufacturerDataMask} have different length. */ public Builder setManufacturerData(int manufacturerId, byte[] manufacturerData, byte[] manufacturerDataMask) { if (manufacturerData != null && manufacturerId < 0) { throw new IllegalArgumentException("invalid manufacture id"); } if (mManufacturerDataMask != null) { if (mManufacturerData == null) { throw new IllegalArgumentException( "manufacturerData is null while manufacturerDataMask is not null"); } // Since the mManufacturerDataMask is a bit mask for mManufacturerData, the lengths // of the two byte array need to be the same. if (mManufacturerData.length != mManufacturerDataMask.length) { throw new IllegalArgumentException( "size mismatch for manufacturerData and manufacturerDataMask"); } } mManufacturerId = manufacturerId; mManufacturerData = manufacturerData; mManufacturerDataMask = manufacturerDataMask; return this; } /** * Build {@link ScanFilter}. * * @throws IllegalArgumentException If the filter cannot be built. */ public ScanFilter build() { return new ScanFilter(mDeviceName, mDeviceAddress, mServiceUuid, mUuidMask, mServiceDataUuid, mServiceData, mServiceDataMask, mManufacturerId, mManufacturerData, mManufacturerDataMask); } } }
二. 作為配置文件時
這樣說的話比較抽象,還是看幾個例子吧,比如之前經常使用的圖片加載框架UniversalImageLoader,或者是現在大熱的Glide 它們本身就包含了幾個模塊(磁盤緩存大小、下載時線程池的核心線程數大小、圖片的緩存路徑、默認的解碼方式、內存的緩存策略....),這些東西本身就因人而異,要按實際情況去配置,而且每個人的偏好又不同,手機的物理環境也不同,所以使用建造者模式來構造這樣一個配置類時,極大的增加了程序的靈活性,每個人都可以視自己的實際情況去配置和更改,使用起來更加舒服。

/** * Init UIL */ private void initImageLoader() { photoCacheDir = new File(SystemUtil.getCacheDir(), "BreezeResources"); int cacheSize = SystemUtil.getAppMaxRunningMemory() / 5; // set Image Cache Pool Size, Now I set it is 5; ImageLoaderConfiguration configuration = new ImageLoaderConfiguration.Builder( getApplicationContext()) // set memory cache strategy .memoryCache(new LruMemoryCache(cacheSize)) .memoryCacheSize(cacheSize) .memoryCacheSizePercentage(13) // .memoryCacheExtraOptions(480, 800) .denyCacheImageMultipleSizesInMemory() // default = device screen dimensions .taskExecutor(null) .taskExecutorForCachedImages(null) // set ThreadPool nums .threadPoolSize(8) // set Thread priority .threadPriority(Thread.NORM_PRIORITY - 1) .tasksProcessingOrder(QueueProcessingType.FIFO) // set disk cache strategy // .diskCacheExtraOptions(480, 800, null) .diskCacheSize(50 * 1024 * 1024) .diskCacheFileCount(100000) // .diskCacheFileNameGenerator(new Md5FileNameGenerator()) .diskCache(new UnlimitedDiskCache(photoCacheDir, null, new BaseFileNameGenerator())) .imageDownloader(new BaseImageDownloader(getApplicationContext())) .imageDecoder(new BaseImageDecoder(true)) .defaultDisplayImageOptions(DisplayImageOptions.createSimple()) // .writeDebugLogs() // set is logcat debug info .build(); ImageLoader.getInstance().init(configuration); }
又比如之前做的FFmpeg播放器,一個Player需要有解封裝器、視頻解碼器、音頻解碼器、硬件解碼器、OpenGL ES 渲染、libYUV 等等這些東西,我們可以做成建造者模式,在配置時指定哪些Feature開啟,或者是使用哪些Feature, 這樣通過一個配置類,播放器就能夠及時的調整內部的功能模塊,創建出一個符合要求的播放器。
三. 單純的構造出一個東西
這種情況其實和上面的兩種情況有些重復,最典型的莫過於android的AlertDialog了,AlertDialog本身具有title , message , contentView這些屬性,這些屬性不是全部必須的,比如你不設置title那顯示出來的Dialog就沒有title,你不更改Dialog上左右兩個按鈕的文字,那它顯示時就顯示默認文字,所以通過建造者模式,你可以構建出各種各樣不同的Dialog, 這也算是建造者模式的應用場景之一吧。
參考鏈接: