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对象,消耗内存。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值