Android设计模式-Builder模式

Posted vanpersie_9987

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android设计模式-Builder模式相关的知识,希望对你有一定的参考价值。

Builder模式介绍

Builder模式是一步一步创建一个复杂对象的创建型模式,它允许用户再不知道内部构建细节的情况下,更精细地控制对象的构造流程。该模式是为了将构建复杂对象的过程和它的部件解耦,使得构建过程和部件的表示隔离开来。

因为一个复杂的对象有很多大量组成的部分,如汽车,有车轮、方向盘、发动机,还有各种小零件等。如何将这些部件装配成一辆汽车,这个过程很复杂,对于这种情况,为了在构建过程中对外部隐藏实现细节,两者之间的耦合也降到最低。

Builder模式的定义

将一个复杂对象的构建与它的表示分离,使得同样的构建过长可以创建不同的表示。

Builder模式的使用场景

(1)相同的方法,不同的执行顺序,产生不同的事件结果时。

(2)多个部件或零件,都可以装配到一个对象中,但是产生的运行结果又不相同时。

(3)产品类非常复杂,或者产品类中的调用顺序不同产生了不同的作用,这个时候使用建造者模式非常合适。

(4)当初始化一个对象特别复杂,如参数多,且很多参数都具有默认值时。

Builder模式的简单实现

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

//计算机抽象类,即Productor角色
public abstract class Computer 
    protected String mBoard;
    protected String mDisplay;
    protected String mOS;

    protected Computer() 
    
    //设置主板
    public void setBoard(String board) 
        mBoard = board;
    
    //设置显示器
    public void setDisplay(String display) 
        mDisplay = display;
    
    //设置操作系统
    public abstract void setOS();

    @Override
    public String toString() 
        return "Computor [mBoard=" + mBoard + ", mDisplay=" + mDisplay + ", mOS=" + mOS + "]";
    


//具体的Computer类, MacBook
public class Macbook extends Computer 
    protected Macbook() 
    
    @Override
    public void setOs() 
        mOS = "Mac OS X 10.10";
    


//抽象Builder类
public abstract class Builder 
    //设置主机
    public abstract void buildBoard(String board);
    //设置显示器
    public abstract void buildDisplay(String display);
    //设置操作系统
    public abstract void buildOS();
    //创建Computer
    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;
    
    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 pcDirector = new Director(builder);
        pcDirector.construct("英特尔主板", "Retina显示器");
        //构建计算机,输出相关信息
        System.out.println("Computer Info : " + builder.create().toString());
    
//输出
Computer Info : Computer [mBoard=英特尔主板, mDisplay=Retina 显示器, MOS=Mac OS X 10.10]

上述示例中,通过具体的MacBookBuilder来构建Macbook对象,而Director封装了构建复杂产品对象的过程,对外隐藏构建细节。Builder与Director一起讲一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的对象。

在现实的开发中,Director角色常被省略。而直接使用一个Builder来进行对象的组装,这个Builder通常为链式调用,它的调用关键点是每个setter方法都返回吱声,也就是return this :

new TestBuilder().settA("A").setB("B").create();

这种形式不仅去除了Director角色,整个结构也更简单,也能对Product对象的组装过程有更精细的控制。

Builder模式实战

下面以配置ImageLoader为例,使用Builder模式为其设置图片在加载时ImageView显示的图片、加载失败后显示的图片、图片加载引擎线程数等。


public class ImageLoader 
    //图片缓存
    ImageCache mImageCache = new MemoryCache();
    //图片加载中显示的图片id
    int mLoadingImageId;
    //加载失败时显示的图片id
    int mLoadingFailedImageId;
    //图片加载策略
    LoaderPolicy mLoaderPolicy;
    //线程池,线程数量为CPU的数量
    ExecutorService mExecutorService = Executors.newFixedThreadPool(Runtime.getRuntime().avaliableProccessors());
    //省略单例模式的部分代码
    public void displayImage(String imageUrl, ImageView imageView) 
        Bitmap bitmap = mImageCache.get(imageUrl);
        if(bitmap != null) 
            imageView.setImageBitmap(bitmap);
            return;
        
        //提交图片加载请求
        submitLoadRequest(imageUrl, imageView);
    
    public void setImageCache(ImageCache cache) 
        mImageCache = cache;
    
    public void setLoadingCache(int resId) 
        mLoadingImageid = resId;
    
    public void setLoadingFailedImage(int resId) 
        mLoadingFailedImageId = resId;
    
    public void setLoadingPolicy(LoaderPolicy policy) 
        mLoadingPolicy = policy;
    

    public void setThreadCount(int count) 
        mExecutorService.shutdown();
        mExecutorService = null;
        //设置新的线程数量
        mExecutorService = Executors.newFixedThreadPool(count);
    
    private void submitLoadRequest(final String imageUrl, final ImageView imageView) 
        //设置加载中的图片
        imageView.setImageResource(mLoadingImageUrl);
        imageView.setTag(imageUrl);
        mExecutorService.submit(new Runnable() 
            @Override
            public void run() 
                //加载图片
                Bitmap bitmap = downloadImage(imageUrl);
                if(bitmap == null) 
                    //设置加载图片失败后显示的图片
                    imageUrl.setImageResource(mLoadingFailedImageId);
                    return;
                
                //显示加载到的图片
            
        );
    
    public Bitmap downloadImage(String imageUrl) 
        Bitmap bitmap = null;
        // ...省略下载过程
        return bitmap;
    

这段代码就是创建对应的成员变量,然后通过setter方法来设置这些变量值,使得这些特性都能被用户定制。

但是存在的问题是,ImageLoader中方法较多,且用户可以在任何时候修改ImageLoader的配置。比如已经初始化了一个指定线程数量的线程池的情况下,用户再调用setThreadCount时就会出现问题。里面过多的函数暴露,也让用户在每次调用函数时都要仔细选择。

而使用Builder就能解决上述问题,即“将一个复杂对象的构建与它的表示分离”,然后用Builder模式来构建一个不可变的配置对象,并且将这个配置对象注入到ImageLoader中,也就是说它只能在构建时设置各个参数,一旦调用了build()方法,它的属性就不可再修改,因为它没有setter方法,字段也是隐藏的,用户只能在初始化时一次性构造这个配置对象,然后注入给ImageLoader。

这是修改后的ImageLoader:

public final class ImageLoader     
    //图片加载配置对象
    private ImageLoaderConfig mConfig;
    //... 省略单例模式代码
    ... ... 
    //初始化ImageLoader
    public void init(ImageLoaderConfig config) 
        mConfig = config;
        //检查配置的合法性,内部会根据配置做一些初始化操作
        checkConfig();
        //...省略
    
    //加载图片的函数
    public void displayImage(String imageUrl, ImageView imageView) 
        Bitmap bitmap = mImageCache.get(imageUrl);
        if(bitmap != null) 
            imageView.setImageBitmap(bitmap);
            return;
        
        //添加加载请求
        submitLoadRequest(imageUrl, imageUrl);
    
    private void submitLoadRequest(final String imageUrl, final ImageView imageView) 
        //...省略
    
    public Bitmap downloadImage(String imageUrl) 
        Bitmap bitmap = null;
        //...省略
        ... ...
        return bitmap;
    

上面的代码,把配置的代码基本都封到了ImageLoaderConfig和Builder对象中。

public class ImageLoaderConfig 
    //图片缓存配置对象
    BitmapCache bitmapCache = new MemeoryCache();
    //加载图片时的loading和加载失败的图片配置对象
    DisplayConfig displayConfig = new DisplayConfig();
    //加载策略
    LoadPolicy loadPolicy = new SerialPolicy();
    //线程数量,默认为CPU数量 + 1
    int threadCount = Runtime.getRuntime().availableProccessors() + 1;
    private ImageLoaderConfig() 
    
    //配置类的Builder
    public static class Builder 
        //图片缓存配置对象
        BitmapCache bitmapCache = new MemeoryCache();
        //加载图片时的loading和加载失败的图片配置对象
        DisplayConfig displayConfig = new DisplayConfig();
        //加载策略
        LoadPolicy loadPolicy = new SerialPolicy();
        //线程数
        int threadCount = Runtime.getRuntime().availableProccessors() + 1;
        public Builder setThreadCount(int count) 
            threadCount = Math.max(1, count);
            return this;
        
        //设置缓存
        public Builder setCache(Bitmapcache cache) 
            bitmapCache = cache;

        
        //设置图片加载中显示的图片
        public Builder setLoadingPlaceholder(int resId) 
            displayConfig.laodingResId = resId;
            return this;
        
        //设置要加载的图片加载失败时显示的图片
        public Builder setNotPlaceholder(int resId) 
            displayConfig.failedResIs = resId;
            return this;

        
        //设置加载策略
        public Builder setLoadiPolicy(LoadPolicy policy) 
            if(policy != null)  
                loadPolicy = policy;
                return this;
            
        
        public applyConfig(ImageLoaderConfig config) 
            config.bitmapCache = this.bitmapCache;
            config.displayConfig = this.displayConfig;
            config.loadPolicy = this.loadPolicy;
            config.threadCount = this.threadCount; 
        
        //根据已经设置好的属性配置对象
        public ImageLoaderConfig create() 
            ImageLoaderConfig config = new ImageLoaderConfig();
            //应用配置
            applyConfig(config);
            return config;
        
    

通过将ImageLoaderConfig的构造函数、字段私有化,使得外部不能访问内部属性,用户唯一能够设置属性的地方就是通过Builder对象。

用户将这样使用Builder模式:

private void initImageLoader() 
    ImageLoaderConfig config = new ImageLoaderConfig.Builder().setLoadingPlaceholder(R.drawable.loading).setNotFoundPlaceholder(R.drawable.not_found).setCache(new DoubleCache(this)).setThreadCount(4).setLoadPolicy(new ReversePolicy()).create();
    //将配置初始化到ImageLoader中
    ImageLoader.getInstance().init(config);

调用init函数之后,ImageLoader就可以正常使用了,各种setter函数不会在用户调用ImageLoader方法时出现在视野中,它们已经被隔离到了Builder模式中。

总结

Builder模式在android开发中较为常用,通常作为配置类的构建器将配置的构建和表示分离开来,同时也是将配置从目标类中隔离出来,避免过多的setter方法。Builder模式比较常见的实现形式是通过调用链实现,这样的代码更简洁、易懂。

  • 优点:

(1)良好的封装性,使用构建者模式可以使客户端不必知道内部的组成细节。
(2)建造者独立,容易扩展。

缺点:

会产生多于的Builder对象以及Director对象,消耗内存。

以上是关于Android设计模式-Builder模式的主要内容,如果未能解决你的问题,请参考以下文章

4,建造者模式

设计模式学习总结创建者模式(Builder)

设计模式解密 - 建造者模式

设计模式 | 建造者模式/生成器模式(builder)

Android中的模式:Builder模式的优点赏析

Android设计模式源码解析之Builder模式