设计模式系列:
0.Android开发常用设计模式;
一、常见需求场景
三毛:“小白,你平常项目里使用建造者模式多不多”
小白:建造者模式?毛毛哥,能说人话嘛
三毛:“就是builder模式,链式调用那种”
小白:思~索~片~刻~~,奥,你说的是下面这种吗,这面这样的链式形式我还没写过呢,只使用过,感觉挺清晰好看的
new AlertDialog.Builder(this)
.setTitle("对话框")
.setMessage("测试")
.setIcon(R.mipmap.ic_launcher)
.create()
.show();
Glide.with()
.load()
.into();
Picasso.with()
.load()
.into();
二、基本解决方法
三毛:“不是吧,我的白,那你开发中例如遇到一个商品或用户注册有很多属性需要设置,你是怎么写的捏”
小白:喔,这个简单,根据情况分2种写法,第一种是属性不多的时候使用构造方法,如果属性很多,那就用set和get,像下面酱紫:
//第一种(属性不是很多的时候)
class 商品{
private String 属性1;//必传的参数
private String 属性2;//必传的参数
private String 属性3;//选传的参数
public 商品(属性1) {
this.(属性1)
}
public 商品(属性1,属性2,属性3) {
this.(属性1,属性2)
}
public 商品(属性1,属性2,属性3) {
this.属性1 = 属性1;
this.属性2 = 属性2;
this.属性3 = 属性3;
}
}
//第二种(属性很多的时候)
class 商品{
private String x1;
private String x2;
.........
public void setX1(String x1){
this.x1=x1;
}
public void setX2(String x2){
this.x2=x2;
}
public String getX1(){
return x1;
public String getX2(){
return x2;
}
......
}
三、基本解决方法存在的问题
三毛:“小白,你上面使用构造函数那一种写法,当使用者,用或看你的构造方法时,第一个,第二个,第三个参数很难知道要传什么或传的是什么,你要去查看才知道,同时,拓展和维护也会很迷糊的啵”
小白:我吸口奶压压惊,老哥你继续
三毛:“属性多的时候,使用set,get第二种方式,是解决了上面的问题,但是会让调用set方法的对象重复了20次或更多,同时也属于不连续的情况,该类的其他属性在被创建之后被修改,给程序带来了一些不安全性,像下面”
//属性越多,调用者(商品)重复越多
商品.setX1("");
商品.setX2("");
商品.setX3("");
.....
//修改已经设置好了的某个属性
商品.setX1("修改后的属性");
小白:呐,毛毛哥,那应该怎么搞会好一点捏
四、变种Builder设计模式写法
建造者模式定义:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
三毛:”你可以使用变种建造者(builder)模式试试,根据自己情况来”
小白:等下,三毛哥,刚刚你说”变种”是啥子情况
三毛:“建造者(builder)模式起初是有自己的标准的,后面随着时间推移,在这基础上Android演变出了变种建造者(builder)模式,不过Android中一般使用变种buildr模式就够用了,至于经典builder模式,类有点多,怕等下把你搞乱了,先说完变种Builder模式,后面在说经典builder模式”
小白:那毛毛哥,你先告诉我变种builder模式怎么写
三毛:“那就用前面那个例子吧,变种builder模式很简单的,主要是在类里建立一个静态内部类,像下面这样”
public class A {
private final String mX1;
private int mX2;
private A(Builder builder) {
mX1 = builder.mX1;
mX2 = builder.mX2;
}
public static final class Builder {
private final String mX1;
private int mX2;
public Builder() {
}
public Builder setX1(String val) {
mX1 = val;
return this;
}
public Builder setX2(int val) {
mX2 = val;
return this;
}
public A build() {
return new A(this);
}
}
}
//使用
new A.Builder()
.setX1()
.setX2()
.build();
五、变种Builder模式和普通写法的区别
1、链式调用,结构清晰;
2、属性设置后,不会被修改,安全性较高;(也是变种builder基本不使用单例理由之一)
3、因为变种builder模式主要是以静态内部类实现,需求改变后,只需要替换新的静态内部类,原来的静态内部类可以原封不动,拓展性和维护较好
========================经典Builder模式分割线=====================
小白:毛毛哥,快说,快说,啥是经典Builder模式
三毛:“哇,小白你辣么可爱,咳咳咳!,下面说正事”
三毛:“有这样一个例子,‘有2个商品,它们都要经历生产-包装-标价过程‘,我的白,你用正常写法模拟下”
小白:毛毛哥,这还不简单,下面是我对上面的案例的模拟过程
//来个公用的接口
public interface IGoods {
void produc();//生产商品
void packageGoods();//包装商品
void price();//给商品标价
}
//商品A类
public class GoodsA implements IGoods{
@Override
public void produc() {
//生产商品A...
System.out.println("生产商品A……");
}
@Override
public void packageGoods() {
//包装商品A...
System.out.println("包装商品A……");
}
@Override
public void price() {
//给商品A标价...
System.out.println("给商品A标价……");
}
public void excuteOrder(){
produc();
packageGoods();
price();
}
}
//商品B和上面一模一样,就不贴重复代码了
...
//使用
new GoodsA().excuteOrder();
new GoodsB().excuteOrder();
小白:这里可以优化一下下,优化后代码
//把接口换成抽象类,把组装商品的过程放到这
public abstract class IGoods {
abstract void produc();//生产商品
abstract void packageGoods();//包装商品
abstract void price();//给商品标价
//执行一条龙服务
public void excuteOrder(){
this.produc();//生产
this.packageGoods();//打包
this.price();//标价
}
}
//商品A类
public class GoodsA extends IGoods{
@Override
public void produc() {
//生产商品A...
System.out.println("生产商品A……");
}
@Override
public void packageGoods() {
//包装商品A...
System.out.println("包装商品A……");
}
@Override
public void price() {
//给商品A标价...
System.out.println("给商品A标价……");
}
}
//商品B和上面一模一样,就不贴重复代码了
...
//使用
new GoodsA().excuteOrder();
new GoodsB().excuteOrder();
三毛:“可以哇,我的白,你上面其实就是经典Builder设计模式了,只不过不是完整的”
小白:啊,我不知不觉就用了经典Builder设计模式了?毛毛哥,那完整版是啥样子的,说来看看呗。
三毛:”不管怎样,首先你都要知道经典Builder模式有4个模块,如下。 “
//经典Builder 4个模块
Product:被构造的复杂对象。
Builder:抽象接口。
BuilderImpl:抽象接口的具体实现。
Director:接口的构造者和使用者。
三毛:”其实经典Builder模式只是比变种Builder模式复杂一点,下面我用经典Builder模式的形式完成你上面的案例,你可以和你的代码对比对比,有啥不同“
//实体类,也可以是其他业务类,看情况嘛
public class GoodsBean {
private String producProcess;
private String packageProcess;
private String priceProcess;
private String goodsName;
public String getGoodsName() {
return goodsName;
}
public void setGoodsName(String goodsName) {
this.goodsName = goodsName;
}
public String getProducProcess() {
return producProcess;
}
public void setProducProcess(String producProcess) {
this.producProcess = producProcess;
}
public String getPackageProcess() {
return packageProcess;
}
public void setPackageProcess(String packageProcess) {
this.packageProcess = packageProcess;
}
public String getPriceProcess() {
return priceProcess;
}
public void setPriceProcess(String priceProcess) {
this.priceProcess = priceProcess;
}
//和你上面(没优化时)一样,只不过加了个获取商品类型的方法
public interface IGoods {
void produc();//生产商品
void packageGoods();//包装商品
void price();//给商品标价
GoodsBean getGoodsType();//得到商品类型
}
//还是和你上面一样,没啥子区别
public class GoodsABuilder implements IGoods{
private GoodsBean goodsBean;
public GoodsABuilder() {
this.goodsBean = new GoodsBean();
}
@Override
public void produc() {
//生产商品A...
goodsBean.setProducProcess("生产商品A……");
}
@Override
public void packageGoods() {
//包装商品A...
goodsBean.setPackageProcess("包装商品A……");
}
@Override
public void price() {
//给商品A标价...
goodsBean.setPriceProcess("给商品A标价……");
}
@Override
public GoodsBean getGoodsType() {
goodsBean.setGoodsName("得到商品A……");
return goodsBean;
}
//商品B一摸一样,就不贴重复代码了
...
}
//这是商品的组装类了
public class Goods {
private IGoods mIGoods;
public Goods(IGoods mIGoods) {
this.mIGoods = mIGoods;
}
//执行一条龙服务
public void excuteOrder(){
mIGoods.produc();//生产
mIGoods.packageGoods();//打包
mIGoods.price();//标价
}
}
//使用
GoodsABuilder goodsABuilder = new GoodsABuilder();//得到商品构建对象
new Goods(goodsABuilder).excuteOrder();//组装商品
GoodsBean goodsType = goodsABuilder.getGoodsType();//最终得到某某商品
System.out.println(goodsType.getGoodsName());
//商品B和上面一摸一样,也不贴重复代码了
...
小白:毛毛哥,你上面的代码对应经典Builder模式4个模块是下面这样的吗
Product:被构造的复杂对象。 ---->GoodsBean
Builder:抽象接口。 ---->IGoods
BuilderImpl:抽象接口的具体实现。 ---->GoodsABuilder、GoodsBBuilder
Director:接口的构造者和使用者。 ---->Goods
三毛:”嗯,不错哇,我的白,理解那么快”
小白:嘿嘿,因为和我的想法一样嘛,虽然类有点多,不过每个模块职责挺清晰的,要是新加很多新商品,只需要添加对应的商品类(GoodsABuilder、GoodsBBuilder、GoodsCBuilder…)就好了,厉害了,我的哥。
总结:
“经典Builder模式使得同样的构建过程可以创建不同的表示。像这里的商品都经历生产-包装-标价过程,但是每个商品都不同。”
特点:
1)封装性:使用建造者模式可以是客户端不必知道产品内部组成的细节。
2)建造者独立,容易扩展:goodsABuilder和goodsBBuilder是相互独立的,对系统扩展非常有利。
3)便于控制细节风险:由于具体的建造者是独立的,因此可以对建造者过程逐步细化,而不对其他的模块产生任何影响。