设计模式之美——建造者模式【超清晰讲解】

设计模式之美——建造者模式

.
.
.

前言:

我们在日常coding中经常遇到需要创建一个对象,有时候有些对象会非常复杂,由好几个对象组成。如果我们一个一个new出来,然后再组装难免有些不太灵活。
建造者模式就是为了解决这一痛点。
写这篇文章之前我看了很多讲解建造者模式的例子,可能是找的比较少,没找到特别透彻的例子。接下来我会尽我所能给大家说清楚,到底怎么才是建造者模式。(英文不太好,可能有拼写出错的地方,见谅)
.
.
.

1.建造者模式的使用流程:

我们以创建一个游戏角色为例:

首先这是一个角色类:


public class Human {


	private String name;//名称
	private int age;/年龄
	private String heigth;//身高
	private String country;//国籍
	
	//相应的get(),set()方法
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
	public String getHeigth() {
		return heigth;
	}
	public void setHeigth(String heigth) {
		this.heigth = heigth;
	}
	public String getCountry() {
		return country;
	}
	public void setCountry(String country) {
		this.country = country;
	}
//重写的toString()方法
	@Override
	public String toString() {
		return "Human [name=" + name + ", age=" + age + ", heigth=" + heigth + ", country=" + country + "]";
	}
	
}

在游戏里中我们每个人的角色都是不一样的,所以我们要定义一个标准接口或者抽象类来构建这个角色。


public abstract class HumanBuilder {
	
	abstract void buildName();//构建名字
	abstract void buildAge();//构建年龄
	abstract void buildHeight();//构建身高
	abstract void buildCountry();//构建国籍
	
	abstract Human buildHuman();//将构建好的角色返回
}

这样我们就有了构建一个角色的标准做法,接下来我们构建一个女性角色类,让她继承自我们上方的抽象类,并且重写相关方法:


public class MailBuilder extends HumanBuilder  {
	
	private Human human_mail;//创建一个没有“捏脸”的角色
	
	public MailBuilder() {
		human_mail = new Human();
	}

//给这个角色设置名字
	@Override
	void buildName() {
		
		human_mail.setName("feilong");
	}

//给这个角色设置年龄
	@Override
	void buildAge() {
		human_mail.setAge(20);
		
	}

//给这个角色设置身高
	@Override
	void buildHerght() {
		human_mail.setHeigth("160");
	}

//给这个角色设置国籍
	@Override
	void buildCountry() {
		human_mail.setCountry("china");
		
	}

//将设置好的角色返回
	@Override
	Human buildHuman() {
		// TODO Auto-generated method stub
		return this.human_mail;
	}
}

虽然我们有了女性角色的类,但是她现在还是一个没有捏脸的妹子(因为我们并没有调用这些设置属性的方法)。现在我们指挥系统给妹子捏脸:


public class Director {

	public Human create(HumanBuilder mBuilder)
	{
		mBuilder.buildAge();
		mBuilder.buildCountry();
		mBuilder.buildHerght();
		mBuilder.buildName();
		
		return mBuilder.buildHuman();
	}

}

现在我们已经将这个妹子设定成了我们喜欢的样子了。要将她变成“对象”才行。

Director director = new Director();
		
Human human = director.create(new MailBuilder());//这里human就是我们刚才建造出来的妹子

这就是整个建造者模式的思路。

Q&A

我当时看完很多例子之后有很多疑问:

疑问1:这样创建对象有什么好处呢?

答:

1.简便性,解耦性:我们在创建对象的时候,仅仅需要两行代码就拿到了我们需要的对象,让我们将更多的精力放在业务逻辑上,而不是创建对象的具体过程。

2.因为有相关的方法,所以我们可以精准地把控对象创建的流程。

3可扩展性:当我们需要一个男性角色的时候,只需要让他继承自HumanBuilder,实现相关方法即可。

疑问2:MailBuilder 的方法里都是具体的值,这样创建出来的对象不是写死了吗?

答:

确实存在这个问题,这也是建造者模式的缺点之一。建造者模式本身就是适合差异较小的对象的创建的。因为我们并没有特别的去关注创建对象的这个过程,而是关注于对象本身的使用。这里有两个方法解决这个问题:

方法1

.对于有较少参数需要动态化处理的时候,在我们的抽象类里,需要在动态化更改的地方加上参数:

abstract void buildName(String name);//构建名字

然后在实现类里也做相应的修改。

然后我们就会发现在Director 这个类调用create()方法的时候,会不知道参数从哪里拿。这里我们可以稍作修改:


public class Director {

	HumanBuilder mBuilder;
	
	public Director(HumanBuilder mBuilder) {
		this.mBuilder = mBuilder;
		
	}
	
	public Human create(String name)
	{
		mBuilder.buildAge();
		mBuilder.buildCountry();
		mBuilder.buildHerght();
		mBuilder.buildName(name);
		
		return mBuilder.buildHuman();
	}

}

然后我们调用的时候就可以这样写:

Human human = new Director(new MailBuilder()).create("feilong");

但这种方法其实有点破坏了建造者模式的理念,调用过程和创建过程耦合了。但灵活运用就好。

方法2:

给每一个想要的对象设计一个builder类。

2.建造者模式的另外一个是使用场景

当我们创建一个对象的时候,需要传入好多好多个参数,有时候传参传得自己都晕了。
那我们就可以掏出建造者模式啦!

看下面这个场景:
我们要组装一个电脑:

package computer;

public class Computer {
	
	private String cpu; 
    private String screen; 
    private String memory; 
    private String mainboard;
	
	public Computer(String cpu, String screen, String memory, String mainboard) {
		this.cpu = cpu;
		this.screen = screen;
		this.memory = memory;
		this.mainboard = mainboard;
	} 
    
}

这里需要传入4个参数,才能创建一个对象。那如果是40个参数呢?或者是可选参数呢?
且往下看:

package computer;

public class NewComputer {
	
		private String cpu; 
	    private String screen; 
	    private String memory; 
	    private String mainboard;
		
		public NewComputer() {
			
			 throw new RuntimeException("can not init");
		} 
		
		public NewComputer(Builder builder)
		{
			 	cpu = builder.cpu; 
		        screen = builder.screen; 
		        memory = builder.memory; 
		        mainboard = builder.mainboard; 
		}
		
		
		
		
		
		@Override
		public String toString() {
			return "NewComputer [cpu=" + cpu + ", screen=" + screen + ", memory=" + memory + ", mainboard=" + mainboard
					+ "]";
		}





//用内部Builder类来实现NewComputer 对象的创建。对外不暴露相关属性的set()方法。


		public static final class Builder {

			 private String cpu; 
		     private String screen; 
		     private String memory; 
		     private String mainboard;
		     
			public Builder() {} 
			
			public Builder cpu(String val)
			{
				cpu = val;
				return this;
			}
			
			public Builder screen(String val)
			{
				screen = val;
				return this;
			}
			
			public Builder memory(String val)
			{
				memory = val;
				return this;
			}
			
			public Builder mainboard(String val)
			{
				mainboard = val;
				return this;
			}
			
			public NewComputer build()
			{
				return new NewComputer(this);
			}
			
			
		     
		}

}

这样我们在调用的时候就可以这样写:


		NewComputer newComputer = new NewComputer.Builder()
				.cpu("core_i9")
				.memory("64G")
				.mainboard("七彩虹")
				.screen("samsung")
				.build();
				System.out.println(newComputer);

链式调用,是不是很舒服。但缺点就是这个对象经过初始化后就不可以再进行更改了。就像okhttp也是这样使用的。

总结:

建造者模式其实就是让我们忽略创建对象的过程,不去考虑其中错综复杂的参数。一切交由建造者builder来完成。但有时候其实不太适合创建灵活的对象。而链式调用中,使用起来很方便但却没法进行属性运行时动态修改。每种方法都有自己的长处和缺点,关键在于我们能不能灵活地去使用。

如果有疑问或者错误的地方可以下方留言交流,谢谢。

发布了47 篇原创文章 · 获赞 15 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/qq_41525021/article/details/105122982