设计模式——Builder设计模式

什么是Builder设计模式?

又称建造者模式,将构建过程和表示过程进行分离,让(参数)构建过程变得更加的简单和直观。另一种差不多的解释:建造者模式(Builder Pattern),是创造性模式之一,Builder 模式的目的则是为了将对象的构建与展示分离。Builder 模式是一步一步创建一个复杂对象的创建型模式,它允许用户在不知道内部构建细节的情况下,可以更精细地控制对象的构造流程。

Builder设计模式和链式调用的区别

这个完全是两个概念,Builer设计模式是一种设计模式,链式调用只是一种调用方式,
但是一般来讲 Builder 设计模式一般会采用链调用的这种方式,那么并不是所有的链式调用都是 Builer 设计模式

链式调用有一个体现,就是在调用方法的时候返回自身对象,Builder 一般也有一种体现,就是一般都会出现 Builder 对象。

Android中的应用

AlertDialog、Glide、Picasso、Okhttp...

public class MainActivity extends AppCompatActivity {
    ImageView mImageView;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // Builder设计模式应用场景1:Glide
        String url = "";
        Glide.with(this)
                .load(url)

                .into(mImageView);

        // Builder设计模式应用场景2:Dialog
        AlertDialog dialog = new AlertDialog.Builder(this)
                .setTitle("标题")
                .setMessage("内容")
                .setNegativeButton("确定", null)
                .setPositiveButton("取消", null)
                .create();
        dialog.show();

    }
}

为什么要用Builder构建者模式呢?

参考文章:
https://blog.csdn.net/peter_water/article/details/78415576
https://blog.csdn.net/ljcITworld/article/details/80387378

假如我们有一个dog类:

public class Dog {
    private int identifier;//编号
    private String name;//名字
    private int age;//年龄
    private int weight;//体重
    private String breed;//品种
    private boolean gender;//性别,true:公狗;false:母狗
    private String sickness;//疾病

    public void setIdentifier(int identifier) {
        this.identifier = identifier;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public void setWeight(int weight) {
        this.weight = weight;
    }

    public void setBreed(String breed) {
        this.breed = breed;
    }

    public void setGender(boolean gender) {
        this.gender = gender;
    }

    public void setSickness(String sickness) {
        this.sickness = sickness;
    }

    @Override
    public String toString() {
        return "Dog{" +
                "identifier=" + identifier +
                ", name='" + name + '\'' +
                ", age=" + age +
                ", weight=" + weight +
                ", breed='" + breed + '\'' +
                ", gender=" + gender +
                ", sickness='" + sickness + '\'' +
                '}';
    }

}

测试:

public class DogTest {
    public static void main(String[] args) {
        Dog dog = new Dog();
        dog.setIdentifier(2222);//因为是一只二哈,2炸了
        dog.setName("sijia");//撕家小能手呀
        dog.setAge(3);
        dog.setWeight(20);
        dog.setBreed("Husky");
        dog.setGender(true);
        dog.setSickness("doubi");//这只哈士奇太逗逼了,还能治吗?
        System.out.println(dog);
    }
}

控制台输出:

Dog{identifier=2222, name='sijia', age=3, weight=20, breed='Husky', gender=true, sickness='doubi'}

上述代码的问题是dog类变量出现好多遍,我们会想能不能有更简洁点的方法,于是我们想到在Dog类增加设置方法setInfo():

    public void setInfo(int identifier, String name, int age, int weight, String breed, boolean gender, String sickness) {
        this.identifier = identifier;
        this.name = name;
        this.age = age;
        this.weight = weight;
        this.breed = breed;
        this.gender = gender;
        this.sickness = sickness;
    }

测试:

Dog dog1 = new Dog();
dog1.setInfo(333, "sijia", 3, 20, "Husky", true, "doubi");
System.out.println(dog1);

控制台输出:

Dog{identifier=333, name='sijia', age=3, weight=20, breed='Husky', gender=true, sickness='doubi'}

可以发现我们一行赋值语句搞定了,但是我们还会发现数字和字符串的含义不是一目了然,还是得查看定义。新增参数就得修改定义,调用的地方也得修改,而且如果参数更多的话,看起来会更乱。

因此,这里我们引出Builder设计模式。接下来我们来看些如何实现:

public class Dog {
    private int identifier;//编号
    private String name;//名字
    private int age;//年龄
    private int weight;//体重
    private String breed;//品种
    private boolean gender;//性别,true:公狗;false:母狗
    private String sickness;//疾病

    public Dog(int identifier, String name, int age, int weight, String breed, boolean gender, String sickness) {
        this.identifier = identifier;
        this.name = name;
        this.age = age;
        this.weight = weight;
        this.breed = breed;
        this.gender = gender;
        this.sickness = sickness;
    }

    public void setIdentifier(int identifier) {
        this.identifier = identifier;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public void setWeight(int weight) {
        this.weight = weight;
    }

    public void setBreed(String breed) {
        this.breed = breed;
    }

    public void setGender(boolean gender) {
        this.gender = gender;
    }

    public void setSickness(String sickness) {
        this.sickness = sickness;
    }

    @Override
    public String toString() {
        return "Dog{" +
                "identifier=" + identifier +
                ", name='" + name + '\'' +
                ", age=" + age +
                ", weight=" + weight +
                ", breed='" + breed + '\'' +
                ", gender=" + gender +
                ", sickness='" + sickness + '\'' +
                '}';
    }

    final static class Builder {
        private int identifier;//编号
        private String name;//名字
        private int age;//年龄
        private int weight;//体重
        private String breed;//品种
        private boolean gender;//性别,true:公狗;false:母狗
        private String sickness;//疾病

        public Builder identifier(int identifier) {
            this.identifier = identifier;
            return this;
        }

        public Builder name(String name) {
            this.name = name;
            return this;
        }

        public Builder age(int age) {
            this.age = age;
            return this;
        }

        public Builder weight(int weight) {
            this.weight = weight;
            return this;
        }

        public Builder breed(String breed) {
            this.breed = breed;
            return this;
        }

        public Builder gender(boolean gender) {
            this.gender = gender;
            return this;
        }

        public Builder sickness(String sickness) {
            this.sickness = sickness;
            return this;
        }

        public Dog build() {
            return new Dog(identifier, name, age, weight, breed, gender, sickness);
        }
    }

}

调用:

public class DogTest {
    public static void main(String[] args) {

        Dog dog = new Dog.Builder()
                .age(11)
                .breed("xx")
                .gender(true)
                .identifier(123)
                .name("cookie")
                .sickness("dd")
                .weight(10).build();
        System.out.println(dog);

    }
}

控制台输出

Dog{identifier=123, name='cookie', age=11, weight=10, breed='xx', gender=true, sickness='dd'}

注意:

首先Builder是一个静态内部类,之所以需要静态是因为Builder类需要和外部类Person剥离关系,否则就会耦合在一起,这里的Builder相当于一个独立文件的类一样的效果;其次Person类的构造方法最好是外部类不可调用的,因为我们提供了Builder的方式让外部实现了对Person类的初始化,那么我们就没必要让外部直接通过构造方法的方式创建,所以最好设置为private;再之,Builder类中的每一个方法都需要返回该构造者,因为这样,我们才能在每一次设置时都是针对同一个Builder实例进行实例;最后,在build方法才进行对Person类进行初始化,这样做的好处就是可以避免过早地对Person类初始化,当然你也是可以在Builder的构造函数里就对Person就进行初始化,这个见仁见智。

可以发现经过改造之后有下面有点:

  1. 可读性强
  2. 可选择性强

缺点:

  1. 增加了一个内部类Builder的代码量,即产生多余的 Builder 对象,消耗内存。
设计模式——Builder设计模式_第1张图片
Builder UML.png

Product 产品类 : 产品的抽象类;
Builder : 抽象类, 规范产品的组建,一般是由子类实现具体的组件过程;
ConcreteBuilder : 具体的构建器;
Director : 统一组装过程(可省略)。

builer设计模式构建NavigationBar

知道了Builder设计模式之后,我们就用这种设计模式来自己写一个自定义TitleView,效果如下图:


设计模式——Builder设计模式_第2张图片
实现效果.png

上面是DefaultNavigationBar,下面是MyNavigationBar。

设计模式——Builder设计模式_第3张图片
包结构.png

INavigation接口:(后来发现这个接口不要也行,可能是为了统一要求吧)

public interface INavigation {

    void inflateView();

    void addToRootView();

    void bindParams();
}

AbsNavigationBar 抽象类:

public abstract class AbsNavigationBar implements INavigation {

    E mBuilder;
    View mNavigationBarView;

    public E getBuilder() {
        return mBuilder;
    }

    protected AbsNavigationBar(E builder) {
        this.mBuilder = builder;

        // 下面三个方法,是实现INavigation这个接口的三个方法,其实不写实现也可以的。
        // 创建布局
        inflateView();
        // 加入到父布局
        addToRootView();
        // 绑定数据
        bindParams();
    }

    @Override
    public void inflateView() {
        mNavigationBarView = LayoutInflater.from(mBuilder.mContext).inflate(mBuilder.mLayoutId, mBuilder.mParent, false);

    }

    @Override
    public void addToRootView() {
        mBuilder.mParent.addView(mNavigationBarView, 0);
    }


    @Override
    public void bindParams() {

        // 绑定文本参数
        Map textMaps = mBuilder.mTextMaps;
        for (Map.Entry entry : textMaps.entrySet()) {
            Integer viewId = entry.getKey();
            CharSequence value = entry.getValue();
            TextView tv = findViewById(viewId);
            tv.setText(value);
        }

        // 绑定点击事件
        Map listenerMaps = mBuilder.mOnClickListenerMaps;
        for (Map.Entry entry : listenerMaps.entrySet()) {
            Integer viewId = entry.getKey();
            View.OnClickListener listener = entry.getValue();
            findViewById(viewId).setOnClickListener(listener);
        }

    }

    public  V findViewById(int viewId) {
        return (V) mNavigationBarView.findViewById(viewId);
    }

    // 构建AbsNavigationBar还有存储参数
    public static abstract class Builder {
        public Context mContext;
        public int mLayoutId;
        public ViewGroup mParent;
        Map mTextMaps;
        Map mOnClickListenerMaps;

        public Builder(Context context, int layoutId, ViewGroup parent) {
            mContext = context;
            mLayoutId = layoutId;
            mParent = parent;
            mTextMaps = new HashMap<>();
            mOnClickListenerMaps = new HashMap<>();
        }

        // 用来创建 AbsNavigationBar
        public abstract AbsNavigationBar build();

        public T setText(int viewId, String msg) {//使用泛型解决报错问题
            mTextMaps.put(viewId, msg);
            return (T) this;
        }

        public T setOnClickListener(int viewId, View.OnClickListener listener) {
            mOnClickListenerMaps.put(viewId, listener);
            return (T) this;
        }
    }
}

MyNavigationBar 初步实现的类:

public class MyNavigationBar extends AbsNavigationBar {

    protected MyNavigationBar(AbsNavigationBar.Builder builder) {
        super(builder);
    }

    public static class Builder extends AbsNavigationBar.Builder {

        public Builder(Context context, int layoutId, ViewGroup parent) {
            super(context, layoutId, parent);
        }

        @Override
        public AbsNavigationBar build() {
            return new MyNavigationBar(this);
        }
    }

}

DefaultNavigationBar默认的NavigationBar:

public class DefaultNavigationBar extends AbsNavigationBar {
    protected DefaultNavigationBar(Builder builder) {
        super(builder);
    }


    @Override
    public void bindParams() {
        super.bindParams();
        //findViewById(R.id.tv_left).setVisibility(getBuilder().mLeftVisible);//使用泛型解决报错问题
        findViewById(R.id.tv_left).setVisibility(getBuilder().mLeftVisible);
    }

    public static class Builder extends AbsNavigationBar.Builder {

        int mLeftVisible = View.VISIBLE;

        public Builder(Context context, ViewGroup parent) {
            super(context, R.layout.layout_default_title, parent);
        }

        @Override
        public AbsNavigationBar build() {
            return new DefaultNavigationBar(this);
        }

        public Builder setTitle(String title) {
            setText(R.id.tv_title, title);
            return this;
        }

        public Builder setLeftText(String title) {
            setText(R.id.tv_left, title);
            return this;
        }

        public Builder setRightText(String title) {
            setText(R.id.tv_right, title);
            return this;
        }

        public Builder setRightListener(View.OnClickListener listener) {
            setOnClickListener(R.id.tv_right, listener);
            return this;
        }

        public Builder hideLeftText() {
            mLeftVisible = View.INVISIBLE;
            return this;
        }
    }

}

MainActivity类:

public class MainActivity extends AppCompatActivity {
    ViewGroup rootView;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_builder);
        rootView = findViewById(R.id.ll_root);

        // 用作演示的NavigationBar
        new MyNavigationBar.Builder(this, R.layout.layout_title, rootView)
                .setText(R.id.tv_title, "首页")
                .setOnClickListener(R.id.tv_left, new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        Toast.makeText(MainActivity.this, "返回", Toast.LENGTH_LONG).show();
                    }
                })
                .build();

        //默认的NavigationBar
        new DefaultNavigationBar.Builder(this, rootView)
                .setTitle("这是默认标题")
                .setLeftText("返回")
                .setRightText("确认")
                .hideLeftText()
                .setRightListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        Toast.makeText(MainActivity.this, "确认", Toast.LENGTH_LONG).show();
                    }
                })
                .build();

    }


}

布局文件很简单,就不贴出来了。

总结:

在写代码的时候,一个是高扩展,并不是要把所有的内容和出现的问题都想到,而是在新增加功能的时候可以保证原来代码不变,对于开发者来说需要就好,最少知识原则,使用者并不想关注太多。

END.

你可能感兴趣的:(设计模式——Builder设计模式)