建造者模式
情景模式:
需要写一个游戏程序,程序包含几个不同的角色,比如一个胖子角色,一个瘦子角色和一个比较帅气的角色。
大概代码如下:
- 比较瘦的人
public class ThinPeople {
private String leg;
private String head;
private String hand;
private String body;
private ThinPeople(){
leg="很瘦的腿";
head="很小的头";
hand = "很小的手";
body ="很小的身体";
}
public void show(){
System.out.println(String.format("我有%s,%s,%s,%s",leg,head,hand,body));
}
}
- 比较胖的人
public class FatPeople {
private String leg;
private String head;
private String hand;
private String body;
private FatPeople(){
leg="很粗的腿";
head="很胖的头";
hand = "很胖的手";
body ="很圆的身体";
}
public void show(){
System.out.println(String.format("我有%s,%s,%s,%s",leg,head,hand,body));
}
}
- 比较帅气的人
public class HandsomePeople {
private String leg;
private String head;
private String hand;
private String body;
private HandsomePeople(){
leg="很长的腿";
head="很帅气的头";
hand = "很好看的手";
body ="很强壮的身体";
}
public void show(){
System.out.println(String.format("我有%s,%s,%s,%s",leg,head,hand,body));
}
}
从第一版代码我们可以看出,代码的重复率很高,很多代码都可以抽象出来。于是稍微修改一下
- 新建一个抽象的
People
类
public abstract class People {
protected String leg;
protected String head;
protected String hand;
protected String body;
public void show(){
System.out.println(String.format("我有%s,%s,%s,%s",leg,head,hand,body));
}
}
- 修改其他的类
public class ThinPeople extends People {
public ThinPeople(){
leg="很瘦的腿";
head="很小的头";
hand = "很小的手";
body ="很小的身体";
}
}
public class FatPeople extends People{
public FatPeople(){
leg="很粗的腿";
head="很胖的头";
hand = "很胖的手";
body ="很圆的身体";
}
}
public class HandsomePeople extends People {
public HandsomePeople(){
leg="很长的腿";
head="很帅气的头";
hand = "很好看的手";
body ="很强壮的身体";
}
}
可以看见,经过抽象以后,每个类都只有关注初始化自己的属性即可。新建一个类也不违背任何设计原则,但是这样有个问题,如果新建一个角色的时候,由于粗心忘了初始化某个属性,这样的错误只会在运行的时候才会显现出来。
因此我们可以将每个属性的设置都作为抽象方法,让每个子类都强制实现:
public abstract class People {
protected String leg;
protected String head;
protected String hand;
protected String body;
public abstract void initLeg();
public abstract void initHead();
public abstract void initHand();
public abstract void initBody();
protected People(){
}
public final void show() {
initLeg();
initHead();
initHand();
initBody();
System.out.println(String.format("我有%s,%s,%s,%s", leg, head, hand, body));
}
}
- 其他子类
public class HandsomePeople extends People {
@Override
public void initLeg() {
leg="很长的腿";
}
@Override
public void initHead() {
head="很帅气的头";
}
@Override
public void initHand() {
hand = "很好看的手";
}
@Override
public void initBody() {
body ="很强壮的身体";
}
}
public class ThinPeople extends People {
@Override
public void initLeg() {
leg="很瘦的腿";
}
@Override
public void initHead() {
head="很小的头";
}
@Override
public void initHand() {
hand = "很小的手";
}
@Override
public void initBody() {
body ="很小的身体";
}
}
public class FatPeople extends People{
@Override
public void initLeg() {
leg="很粗的腿";
}
@Override
public void initHead() {
head="很胖的头";
}
@Override
public void initHand() {
hand = "很胖的手";
}
@Override
public void initBody() {
body ="很圆的身体";
}
}
到这里,我们的类已经完全抽象出来,不同的角色只用覆盖属性方法即可。
使用如下:
public class Main {
public static void main(String[] args) {
People people=new FatPeople();
people.show();
}
}
不过在一般大型系统中,如果People
的功能比较复杂,而初始化People
的过程也比较复杂的话,People
会看起来非常臃肿,比如上面的People
代码,我们真正使用People
的地方只有show()
一个方法而已,如果方法再多,People
这个类就会很大,更加不好维护,这个时候,我们可以考虑将People
的初始化抽离出来,单独为People
创建一个专门的初始化类:
整理代码如下:
- 创建一个
People
类
@Getter
@Setter
public class People {
private String leg;
private String head;
private String hand;
private String body;
public void show() {
System.out.println(String.format("我有%s,%s,%s,%s", leg, head, hand, body));
}
}
- 创建一个抽象的
Builder
public abstract class PeopleBuilder {
protected People people=new People();
abstract void builderHead();
abstract void builderHand();
abstract void builderBody();
abstract void builderLeg();
People getPeople(){
return people;
}
}
- 根据各个属性,创建对应的具体
Builder
public class FatPeopleBuilder extends PeopleBuilder {
@Override
public void builderHead() {
people.setHead("很胖的头");
}
@Override
public void builderHand() {
people.setHand("很胖的手");
}
@Override
public void builderBody() {
people.setBody("很圆的身体");
}
@Override
public void builderLeg() {
people.setLeg("很粗的腿");
}
}
//其他的省略
- 创建一个指导类,用于指导合成
People
的类
public class PeopleController {
public People create(PeopleBuilder builder) {
builder.builderBody();
builder.builderHand();
builder.builderHead();
builder.builderLeg();
return builder.getPeople();
}
}
- 使用
public class Main {
public static void main(String[] args) {
PeopleBuilder peopleBuilder=new FatPeopleBuilder();
PeopleController peopleController=new PeopleController();
People people= peopleController.create(peopleBuilder);
people.show();
}
}
经过整理后,我们可以发现,People
除了必要的属性和Getter&Setter
外,没有多余的初始化代码,而复杂的初始化过程被分离开来,这样People
可以专注功能方面的代码,而Builder
可以专注初始化功能。
建造者模式(Builder Pattern)
定义:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示
本质:将对象的复杂创建过程抽象到另外一个类中,让其他类来负责它的初始化
UML
可以看出来,UML图比较简单,其中Builder
作为抽象的接口,不同的实现产生不同属性的产品。
优点
- 将对象的创建与使用分离开来,使对象耦合度更低,更加易于创建相同创建过程的不同产品
- 可以精确控制对象的创建,对象创建的过程被
Director
控制,不会出现缺少的初始化信息 - 易于拓展,增加新的产品不用修改原本的代码,符合开闭原则
缺点
- 产生了更多的小类
- 只适用于仅仅是产品创建过程相同,但是产品的属性不同的创建,如果产品创建过程差异过大,则不适合使用该模式
应用场景
- 某个系统中,需要产生一系列生产过程相同,但是属性不同的类
- 某个复杂的类创建和使用过程比较复杂,需要将类的使用和创建分离开来
- 创建某个类的过程比较复杂,需要统一将类的创建统一整理
其他
有些时候,可省略Director
类,如果不需要将类的创建和使用分离开,可以简单使用上面例子的第二次优化结果,这样可以简化用户使用这个类的复杂程度。