最近在学习设计模式,在创建型模式中比较难理解的主要是工厂模式与建造者模式,二者有许多相似之处,但是本质上还是不同的。
目录
1.抽象工厂模式:是工厂模式的一种。工厂模式又分为工厂方法模式与抽象工厂模式。工厂方法模式在本文中不进行详细叙述,简单来说就是给一个工厂负责创建一个类。而抽象工厂模式负责创建一个产品族,例如本文要举的例子中,男士帽子、男士大衣、男士裤子属于一个产品族;女士帽子、女士大衣、女士裤子属于另一个产品族。如果只用工厂方法模式,那就需要创建6个工厂类。而用抽象工厂模式只需要2个工厂类,分别是MaleClothesFactory和FemaleClothesFactory,这两个工厂类又同时实现了AbsFactory接口(在后面的例子中会用到)
2.建造者模式:将部件和其组装过程分开,一步一步创建一个复杂的对象。相比于工厂模式创建一类对象,建造者模式更注重于该对象的属性,例如创造男士大衣时需要多少个口袋、衣服的材料如何等等。
1 实例
介绍完了概念,下面引入一个实例同时可以用到抽象工厂模式和建造者模式。
现在有一个工厂,可以同时生产男士帽子、男士大衣、男士裤子、女士帽子、女士大衣、女士裤子共6种产品。要是按照工厂方法模式,需要定义男士帽子工厂类、男士大衣工厂类、男士裤子工厂类、女士帽子工厂类、女士大衣工厂类、女士裤子工厂类以及各自的实体类,容易发生类爆炸。其中男士帽子、女士帽子是同一产品等级,都是帽子;男士大衣、女士大衣是同一产品等级,都是大衣;男士裤子、女士裤子是同一产品等级,都是裤子。男士帽子、男士大衣、男士裤子是同一产品族,都是男士衣服;女士帽子、女士大衣、女士裤子是同一产品族,都是女士衣服。所以这个案例可以使用抽象工厂模式实现。类图如下:
IDEA中目录如下:
1.1 工厂类的代码(抽象工厂模式)
抽象工厂:
public interface AbsFactory {
// 创建帽子
Hat createHat();
// 创建大衣
Coat createCoat();
// 创建裤子
Pant createPant();
}
具体工厂——男士衣服工厂
public class MaleClothesFactory implements AbsFactory {
@Override
public Hat createHat() {
return new MaleHat.Builder().build();
}
@Override
public Coat createCoat() {
return new MaleCoat.Builder().build();
}
@Override
public Pant createPant() {
return new MalePant.Builder().build();
}
}
具体工厂——女士衣服工厂
public class FemaleClothesFactory implements AbsFactory {
@Override
public Hat createHat() {
return new FemaleHat.Builder().build();
}
@Override
public Coat createCoat() {
return new FemaleCoat.Builder().build();
}
@Override
public Pant createPant() {
return new FemalePant.Builder().build();
}
}
可以看出工厂生成的实体类都是通过Builder来创建的,这是因为对于每一个实体类都是用建造者模式来创建的。
1.2 实体类(建造者模式)
我们以大衣为例,抽象类大衣下有两个子类:男士大衣类与女士大衣类。采用“链式调用”,在抽象大衣类中创建一个抽象Builder类,权限修饰符为protected,可以被子类访问。
抽象大衣类代码如下:
public abstract class Coat {
//Coat的必需属性 采用protected权限修饰符可以让子类访问
protected String pocket;
protected String material;
protected Coat(Builder builder) {
this.pocket = builder.pocket;
this.material = builder.material;
}
// 抽象方法由子类实现
public abstract void wearCoat();
// 抽象的Builder类 具体方法由子类实现 采用了链式调用
protected abstract static class Builder {
protected String pocket;
protected String material;
public abstract Builder pocket(String val);
public abstract Builder material(String val);
public abstract Coat build();
}
}
男士大衣类是继承了抽象大衣类的子类(女士大衣类同,所以只展示男士大衣类的代码)。男士大衣类中的内部类Builder继承了抽象大衣类中的内部类Builder。注:重写方法的返回值类型范围要小于等于原方法的返回值类型,例如抽象类Builder的build()方法返回值类型是Coat,但实现类Builder的build()方法返回值类型是MaleCoat:
public class MaleCoat extends Coat {
private MaleCoat(Builder builder) {
super(builder);
}
@Override
public void wearCoat() {
System.out.println("穿上男士大衣");
}
// 继承了抽象大衣类的内部类Builder
public static final class Builder extends Coat.Builder{
@Override
public Builder pocket(String val) {
pocket = val;
return this;
}
@Override
public Builder material(String val) {
material = val;
return this;
}
@Override
public MaleCoat build() {
return new MaleCoat(this);
}
}
@Override
public String toString() {
return "MaleCoat{" +
"pocket='" + pocket + '\'' +
", material='" + material + '\'' +
'}';
}
}
其余帽子类产品和裤子类产品的构建都同大衣类。
1.3 结果
创建一个客户端Client,以男士衣服产品族为例子,创建男士大衣,男士帽子、男士裤子
public class Client {
public static void main(String[] args) {
AbsFactory factory = new MaleClothesFactory();
Hat hat = factory.createHat();
System.out.println(hat);
hat.wearHat();
Coat coat = factory.createCoat();
System.out.println(coat);
coat.wearCoat();
Pant pant = factory.createPant();
System.out.println(pant);
pant.wearPant();
}
}
最终结果如下:
MaleHat{shape='鸭舌帽', material='布料'}
戴上男士帽子
MaleCoat{pocket='2个', material='全棉'}
穿上男士大衣
MalePant{pocket='2个', material='全棉'}
穿上男士裤子
2. 优点
采用抽象工厂模式和建造者模式最直接的优点就是代码解耦,提升了后续代码的维护与扩展。
2.1 抽象工厂类的优点
假如此时我们想要生产儿童大衣ChildCoat类,只需要新建一个ChildCoat类继承Coat抽象类。并且在ChildCoat类中创建内部类Buider继承Coat.Buider。
// 新增的需求
public class ChildCoat extends Coat{
private ChildCoat(Builder builder) {
super(builder);
}
@Override
public void wearCoat() {
System.out.println("穿上儿童大衣");
}
public static final class Builder extends Coat.Builder{
@Override
public Builder pocket(String val) {
pocket = val;
return this;
}
@Override
public Builder material(String val) {
material = val;
return this;
}
@Override
public ChildCoat build() {
return new ChildCoat(this);
}
}
}
2.2 建造者模式的优点
女士大衣的必要属性有pocket和material,这两个属性是在抽象类Coat中就已经定义好的。当我们想给女士大衣类再加一个属性decoration,增加一点装饰。只需要在女士大衣类增加该属性,并且在内部类Builder中也增加该属性,并且增加public Builder decorate(String val)的方法:
public class FemaleCoat extends Coat {
private String decoration; // 新增内容
private FemaleCoat(Builder builder) {
super(builder);
this.decoration = builder.decoration; // 新增内容
}
@Override
public String toString() {
return "FemaleCoat{" +
"decoration='" + decoration + '\'' +
", pocket='" + pocket + '\'' +
", material='" + material + '\'' +
'}';
}
@Override
public void wearCoat() {
System.out.println("穿上女士大衣");
}
public static final class Builder extends Coat.Builder {
private String decoration;// 新增内容
public Builder decorate(String val) {// 新增内容
this.decoration = val;
return this;
}
@Override
public Builder pocket(String val) {
pocket = val;
return this;
}
@Override
public Builder material(String val) {
material = val;
return this;
}
public FemaleCoat build() {
return new FemaleCoat(this);
}
}
}
最后在女士衣服的工厂类中链式调用增加decoration()方法即可,如下:
@Override
public Coat createCoat() {
return new FemaleCoat.Builder()
.pocket("2个")
.material("全棉")
.decorate("小花")
.build();
}
控制台输出如下:
FemaleCoat{decoration='小花', pocket='2个', material='全棉'}
穿上女士大衣
3.总结
一句话总结,抽象工厂模式注重于创建不同种类的对象,建造者模式着重于一个对象创建时的属性构成。