1、定义
将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示
2、在什么情况下使用建造模式
1. 需要生成的产品对象有复杂的内部结构,每一个内部成分本身可以是对象,也可以仅仅是一个对象(即产品对象)的一个组成部分。
2. 需要生成的产品对象的属性相互依赖。建造模式可以强制实行一种分步骤进行的建造过程,因此,如果产品对象的一个属性必须在另一个属性被赋值之后才可以被赋值,使用建造模式是一个很好的设计思想。
3. 在对象创建过程中会使用到系统中的其他一些对象,这些对象在产品对象的创建过程中不易得到。
4. 与工厂模式相比,建造者模式一般用来创建更为复杂的对象
3、实例
这里以魔兽世界游戏中创建游戏角色为例:
游戏角色是一个复杂的对象,在创建的时候,我们可以选择它的性别、种族、阵营、职业(或者通过其它途径设置角色的服装和外形)
第一种表现形式:
传统意义上Builder模式包含具体的产品类、抽象Builder类、具体的Builder类、Director类
人物角色类Persona
public abstract class Persona {
protected String sex;// 性别
protected String race;// 种族
protected String camp;// 阵营
protected String occupation;// 职业
public void setSex(String sex) {
this.sex = sex;
}
public void setRace(String race) {
this.race = race;
}
public abstract void setCamp();
public void setOccupation(String occupation) {
this.occupation = occupation;
}
@Override
public String toString() {
// TODO Auto-generated method stub
return "Persona [sex=" + sex +",race=" + race + ",camp=" + camp + ",occupation=" + occupation + "]";
}
}
联盟英雄类AllianceHero
public class AllianceHero extends Persona {
public void setCamp() {
camp = "联盟";
}
}
抽象的builder类
public abstract class Builder {
public abstract void buildSex(String sex);
public abstract void buildRace(String race);
public abstract void buildCamp();
public abstract void buildOccupation(String occupation);
public abstract Persona create();
}
具体的联盟英雄构建类
public class AllianceHeroBuilder extends Builder {
private Persona mPersona = new AllianceHero();
@Override
public void buildSex(String sex) {
mPersona.setSex(sex);
}
@Override
public void buildRace(String race) {
mPersona.setRace(race);
}
@Override
public void buildCamp() {
mPersona.setCamp();
}
@Override
public void buildOccupation(String occupation) {
mPersona.setOccupation(occupation);
}
@Override
public Persona create() {
return mPersona;
}
}
导演类
public class Director {
Builder mBuilder = null;
public Director(Builder builder) {
this.mBuilder = builder;
}
public void construct(String sex, String race, String occupation) {
mBuilder.buildSex(sex);
mBuilder.buildRace(race);
mBuilder.buildOccupation(occupation);
mBuilder.buildCamp();
}
}
客户端类
public class Client {
public static void main(String[] args) {
Builder builder = new AllianceHeroBuilder();
Director director = new Director(builder);
director.construct("男", "矮人", "牧师");
System.out.println("人物角色信息 :"+ builder.create().toString());
}
}
第二种表现形式:
1、由于是用Builder模式来创建某个对象,因此就没有必要再定义一个Builder接口,直接提供一个具体的建造者类就可以了
2、现实开发过程中,Director角色经常会被省略,而直接使用一个Builder来进行对象的组装,这个Builder通常为链式调用,它的关键点是每个setter方法都返回本身,也就是return this,这样使得setter方法可以链式调用,可以参考下面代码
3、在本例中将具体建造者合并到了产品对象中,并将产品对象的构造函数私有化,防止客户端不使用构建器来构建产品对象,而是直接去使用new来构建产品对象所导致的问题。另外,这个构建器的功能就是为了创建被构建的对象,完全可以不用单独一个类
英雄类Hero
public class Hero {
protected String sex;// 性别
protected String race;// 种族
protected String camp;// 阵营
protected String occupation;// 职业
protected String clothingData;// 服装数据
protected String appearanceData;// 外形数据
private Hero(HeroBuilder heroBuilder) {
this.sex = heroBuilder.sex;
this.race = heroBuilder.race;
this.camp = heroBuilder.camp;
this.occupation = heroBuilder.occupation;
this.clothingData = heroBuilder.clothingData;
this.appearanceData = heroBuilder.appearanceData;
}
public void create() {
System.out.println("Hero [sex=" + sex + ",race=" + race + ",camp=" + camp + ",occupation=" + occupation
+ ",clothingData=" + clothingData + ",appearanceData=" + appearanceData + "]");
}
public static class HeroBuilder {
protected String sex;// 性别
protected String race;// 种族
protected String camp;// 阵营
protected String occupation;// 职业
protected String clothingData;// 服装数据
protected String appearanceData;// 外形数据
public HeroBuilder(String sex, String race, String camp, String occupation) {
this.sex = sex;
this.race = race;
this.camp = camp;
this.occupation = occupation;
}
public HeroBuilder setClothingData(String clothingData) {
this.clothingData = clothingData;
return this;
}
public HeroBuilder setAppearanceData(String appearanceData) {
this.appearanceData = appearanceData;
return this;
}
public Hero build() {
return new Hero(this);
}
}
}
客户端类
public class Client {
public static void main(String[] args) {
Hero.HeroBuilder builder = new Hero.HeroBuilder("男", "人类", "联盟", "牧师");
Hero hero = builder.setClothingData("附魔师的长袍").setAppearanceData("秃头圆脸").build();
hero.create();
//另外一种结构
new Hero.HeroBuilder("男", "人类", "联盟", "牧师")
.setClothingData("附魔师的长袍")
.setAppearanceData("秃头圆脸")
.build()
.create();
}
}
3、知名案例
1、AlertDialog
AlertDialog的构造方法全部是Protected的,所以不能直接通过new一个AlertDialog来创建出一个AlertDialog。
要创建一个AlertDialog,就要用到AlertDialog.Builder中的create()方法
Dialog alertDialog = new AlertDialog.Builder(this).
setTitle("确定删除?").
setMessage("您确定删除该条信息吗?").
setIcon(R.drawable.ic_launcher).
setPositiveButton("确定", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
// TODO Auto-generated method stub
}
}).
setNegativeButton("取消", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
// TODO Auto-generated method stub
}
}).
setNeutralButton("查看详情", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
// TODO Auto-generated method stub
}
}).
create();
alertDialog.show();
2、Android-Universal-Image-Loader(著名的图片加载库)
imageloader在APP中的全局配置,配置的构建过程中所有的选项都是可选的,可以进行自我定制File cacheDir = StorageUtils.getCacheDirectory(context);
ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(context)
.memoryCacheExtraOptions(480, 800) // default = device screen dimensions
.diskCacheExtraOptions(480, 800, null)
.taskExecutor(...)
.taskExecutorForCachedImages(...)
.threadPoolSize(3) // default
.threadPriority(Thread.NORM_PRIORITY - 2) // default
.tasksProcessingOrder(QueueProcessingType.FIFO) // default
.denyCacheImageMultipleSizesInMemory()
.memoryCache(new LruMemoryCache(2 * 1024 * 1024))
.memoryCacheSize(2 * 1024 * 1024)
.memoryCacheSizePercentage(13) // default
.diskCache(new UnlimitedDiskCache(cacheDir)) // default
.diskCacheSize(50 * 1024 * 1024)
.diskCacheFileCount(100)
.diskCacheFileNameGenerator(new HashCodeFileNameGenerator()) // default
.imageDownloader(new BaseImageDownloader(context)) // default
.imageDecoder(new BaseImageDecoder()) // default
.defaultDisplayImageOptions(DisplayImageOptions.createSimple()) // default
.writeDebugLogs()
.build();