设计模式学习(二):建造者模式

jkouu 67 0

什么是建造者模式?

建造者模式也是一种很常见的用于创建对象的设计模式。其实这个模式并不神秘,我们经常用到它。比如在android开发时,我们有时候会使用到AlertDialog组件,它就采用了建造者模式。除此之外,StringBuilder也使用了建造者模式。由于AlertDialog和StringBuilder正好使用率建造者模式的两种实现模式,所以这次我们就用它们来做一个讲解。

建造者模式的使用情景

方便用户创建复杂的对象

这里我们以AlertDialog为例。我们知道,作为一个比较复杂的控件,AlertDialog有很多成员变量,比如Button、View、Icon等。正因为它的结构复杂,所以要想弄清楚它的实现原理就很困难。幸运的是,用户在大多数情况下并不关系它实现的原理,只关心它能实现的功能。那位了方便用户使用,在设计AlertDialog时我们就需要把它的实现封装好,只为用户提供一个改变效果的接口。比如说我们创建一个AlertDialog:

new AlertDialog.Builder(this)
                .setIcon(R.mipmap.ic_launcher)
                .setTitle("Title")
                .setPositiveButton("确认", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        
                    }
                })
                .setNegativeButton("取消", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        
                    }
                })
                .create()
                .show();

虽然我们不知道Title到底是怎么实现的效果,create又到底是怎样的一个流程,但是这并不影响我们使用AlertDialog。通过它提供的接口,即使不知道实现的原理,我们一样能更换标题内容或者更换图标。这就是建造者模式的第一个使用情景:方便用户创建复杂的对象,即使用户不知道它是如何实现的,也不妨碍用户使用它。

一般来说,在这类情境下,这种对象的各个属性都要有一个默认值,否则建造者模式就退化成了封装好的构造函数了。

方便用户对对象操作

这里我们以StringBuilder为例子。我们知道,字符串的拼接并不简单。最简单的问题是各种数据类型的字符串化,更复杂的问题还有当现有空间不够时要申请新内存并转移数据等等。虽然这些操作对于字符串拼接来说是必要的,但是对用户来说却不是必须可见的。这是显然的,我作为一个程序员,我只关心你能不能完成字符串的拼接,至于你到底干了什么,我一点兴趣都没有。既然这部分操作对用户来说不是必须的,那我们就应该把它封装起来,只对用户开放一个可直接实现效果的接口,这就是建造者模式使用的第二个使用情景。

所以,虽然我们不知道StringBuilder到底是怎么维护一个长字符串的管理的,但是我们还是可以无脑append来加长这个字符串。这就是建造者模式带来的便利。

建造者模式的实现

抽象类实现

一般来说,如果有一系列相似的类都需要使用建造者模式,那么就可以为他们定义一个抽象化的父类,子类通过继承父类来实现它们的创建。StringBuilder和StirngBuffer就是这种情形之一。

首先我们看一下StirngBuffer的声明:

public final class StringBuffer
    extends AbstractStringBuilder
    implements java.io.Serializable, CharSequence
{

    /**
     * A cache of the last value returned by toString. Cleared
     * whenever the StringBuffer is modified.
     */
    private transient char[] toStringCache;

    /** use serialVersionUID from JDK 1.0.2 for interoperability */
    static final long serialVersionUID = 3388685877147921107L;

    /**
     * Constructs a string buffer with no characters in it and an
     * initial capacity of 16 characters.
     */
    public StringBuffer() {
        super(16);
    }
    ......
}

我们看到,StringBuffer是AbstractStringBuilder的子类。

再看看StringBuilder:

public final class StringBuilder
    extends AbstractStringBuilder
    implements java.io.Serializable, CharSequence
{

    /** use serialVersionUID for interoperability */
    static final long serialVersionUID = 4383685877147921099L;

    /**
     * Constructs a string builder with no characters in it and an
     * initial capacity of 16 characters.
     */
    public StringBuilder() {
        super(16);
    }
    ......
}

可以看到它也是AbstractStringBuilder的子类。

静态内部类实现

抽象类实现的方法一般是用在相似的类很多的情况下。如果我们只有一个类采用建造者模式,或者说有若干个不相似的类采用建造者模式,那抽象类其实没有多大作用——反正你都只有一个类去实现它,多一个抽象类文件还会加大程序文件管理的任务量。

所以在这种情况下,我们一般都采用静态内部类实现建造者。AlertDialog就是这种方式的一个代表:

public static class Builder {
    private final AlertController.AlertParams P;
    private final int mTheme;
    ......
}

上面就是AlertDialog源码中的一个片段。我们可以很清楚的看到这个名为Builder的静态内部类。这也就是我们在创建AlertDialog时写的new AlertDialog.Builder(this)中Builder的由来。

 

发表评论 取消回复
您必须 [登录] 才能发表评论!
分享