什么是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就进行初始化,这个见仁见智。
可以发现经过改造之后有下面有点:
- 可读性强
- 可选择性强
缺点:
- 增加了一个内部类Builder的代码量,即产生多余的 Builder 对象,消耗内存。
Product 产品类 : 产品的抽象类;
Builder : 抽象类, 规范产品的组建,一般是由子类实现具体的组件过程;
ConcreteBuilder : 具体的构建器;
Director : 统一组装过程(可省略)。
builer设计模式构建NavigationBar
知道了Builder设计模式之后,我们就用这种设计模式来自己写一个自定义TitleView,效果如下图:
上面是DefaultNavigationBar,下面是MyNavigationBar。
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.