对建造者模式的理解

目录

一、为什么需要建造者模式?

  • 什么时候使用建造者模式?

1、场景1

  • 有时候,一个类中有很多字段,或者有些字段也是引用类型。这时候,当我们实例化这个类时(建造这个类的对象时),会比较麻烦:
  • 方案:提供入参巨多的构造器
java 复制代码
public class ComplexClass {
	private String name;
	private String email;
	private Integer age;
	private Pet pet;
	...

	public ComplexClass(String name, String email, ...) {
	
	}
}

2、场景2

  • 有各种各样的房子,它们有一些共同的属性,也有一些独特的属性。
  • 一种设计是:抽象父类 + 多个子类。
  • 这可能导致子类爆炸,明明也是房子对象,却不得不搞一个新的房子类。

3、解决上述场景的办法:建造者模式

  • 示例:
java 复制代码
public class House { 
	private List<Wall> walls;
	private List<Door> doors;
	...
}

public interface IHouseBuilder {
	void buildWalls(List<Wall> walls);
	void buildDoors(List<Door> doors);
	...
	House getProduct();
}

public class HouseWithGarageBuilder implements IHouseBuilder {
	private House house;
	public HouseWithGarageBuilder() {
		house = new House();
	}
	
	public buildWalls(List<Wall> walls) {
		this.walls = walls;
	}
	
	public buildDoors(List<Door> doors) {
		this.doors = doors;	
	}

	...
	
	public House getProduct() {
		return house;
	}
}

public class HouseDirector {
	private IHouseBuilder builder;

	public HouseDirector(IHouseBuilder builder) {
		this.builder = builder;
	}
	
	public House constructHouse() {
		builder.buildWalls(...);
		builder.buildDoors(...);
		return builder.getProduct();
	}
}
  • 本质:分步骤建造对象。

二、练习

1、题目描述 【来源

小明家新开了一家自行车工厂,用于使用自行车配件(车架 frame 和车轮 tires )进行组装定制不同的自行车,包括山地车和公路车。

山地车使用的是Aluminum Frame(铝制车架)和 Knobby Tires(可抓地轮胎),公路车使用的是 Carbon Frame (碳车架)和 Slim Tries。

现在它收到了一笔订单,要求定制一批自行车,请你使用【建造者模式】告诉小明这笔订单需要使用那些自行车配置吧。

2、输入描述

输入的第一行是一个整数 N(1 ≤ N ≤ 100),表示订单的数量。

接下来的 N 行,每行输入一个字符串,字符串表示客户的自行车需求。

字符串可以包含关键词 "mountain" 或 "road",表示客户需要山地自行车或公路自行车。

3、输出描述

对于每笔订单,输出该订单定制的自行车配置。

4、输入示例

3

mountain

road

mountain

5、输出示例

Aluminum Frame Knobby Tires

Carbon Frame Slim Tires

Aluminum Frame Knobby Tires

6、参考

java 复制代码
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Bike {
    private Frame frame;
    private Tires tires;

    @Override
    public String toString() {
        return frame.getType() + " " + tires.getType();
    }
}

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Frame {
    private String type;
}

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Tires {
    private String type;
}

public interface IBikeBuilder {
    void buildFrame();
    void buildTires();
    Bike getProduct();
}

public class MountainBikeBuilder implements IBikeBuilder {
    private Bike bike;

    public MountainBikeBuilder() {
        bike = new Bike();
    }

    @Override
    public void buildFrame() {
        bike.setFrame(new Frame("Aluminum Frame"));
    }

    @Override
    public void buildTires() {
        bike.setTires(new Tires("Knobby Tires"));
    }

    @Override
    public Bike getProduct() {
       return bike;
    }
}

public class RoadBikeBuilder implements IBikeBuilder {
    private Bike bike;

    public RoadBikeBuilder() {
        bike = new Bike();
    }

    @Override
    public void buildFrame() {
        bike.setFrame(new Frame("Carbon Frame"));
    }

    @Override
    public void buildTires() {
        bike.setTires(new Tires("Slim Tries"));
    }

    @Override
    public Bike getProduct() {
        return bike;
    }
}

public class BikeDirector {
    public Bike constructBike(IBikeBuilder bikeBuilder) {
        bikeBuilder.buildFrame();
        bikeBuilder.buildTires();
       return bikeBuilder.getProduct();
    }
}

public class Main {
    public static void main(String[] args) {
        BikeDirector bikeDirector = new BikeDirector();
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt();
        scanner.nextLine();
        for (int i = 0; i < n; i++) {
            String type = scanner.nextLine();
            IBikeBuilder bikeBuilder;
            if ("mountain".equals(type)) {
                bikeBuilder = new MountainBikeBuilder();
            } else if ("road".equals(type)) {
                bikeBuilder= new RoadBikeBuilder();
            } else {
                throw new RuntimeException("type error");
            }
            Bike bike = bikeDirector.constructBike(bikeBuilder);
            System.out.println(bike.toString());
        }
    }
}

三、思考

  • 现实情况下,我还没这么用过建造者模式,因为按照上面的写法,还是很费劲的。

1、一般常用的是lombok库的@Builder注解。

  • 示例:
java 复制代码
@Builder
@ToString
public class User {
    private String name;
    private Integer age;
    private String email;
    private String gender;
}

public class Main {
    public static void main(String[] args) {
        User user = User.builder()
                .name("Forrest")
                .age(20)
                .email("forrest@qq.com")
                .gender("male")
                .build();

        System.out.println(user);
    }
}

这种建造对象的写法看起来还是很舒服的。

  • 编译后的User.class
java 复制代码
public class User {
    private String name;
    private Integer age;
    private String email;
    private String gender;

    User(String name, Integer age, String email, String gender) {
        this.name = name;
        this.age = age;
        this.email = email;
        this.gender = gender;
    }

    public static UserBuilder builder() {
        return new UserBuilder();
    }

    public String toString() {
        return "User(name=" + this.name + ", age=" + this.age + ", email=" + this.email + ", gender=" + this.gender + ")";
    }

    public static class UserBuilder {
        private String name;
        private Integer age;
        private String email;
        private String gender;

        UserBuilder() {
        }

        public UserBuilder name(String name) {
            this.name = name;
            return this;
        }

        public UserBuilder age(Integer age) {
            this.age = age;
            return this;
        }

        public UserBuilder email(String email) {
            this.email = email;
            return this;
        }

        public UserBuilder gender(String gender) {
            this.gender = gender;
            return this;
        }

        public User build() {
            return new User(this.name, this.age, this.email, this.gender);
        }

        public String toString() {
            return "User.UserBuilder(name=" + this.name + ", age=" + this.age + ", email=" + this.email + ", gender=" + this.gender + ")";
        }
    }
}
  • 可以清楚地看到,User中生成了一个内部类UserBuilder,来分步骤设置字段。

2、但@Builder注解有很多坑点

  • 显而易见的一点:
java 复制代码
@Builder
@ToString
public class User {
    private String name = "Forrest";
    private Integer age;
    private String email;
    private String gender;
}

public class Main {
    public static void main(String[] args) {
        User user = User.builder()
                .age(20)
                .email("forrest@qq.com")
                .gender("male")
                .build();

        System.out.println(user);
    }
}

User(name=null, age=20, email=forrest@qq.com, gender=male)
  • 默认值就这样丢了...
java 复制代码
@Builder
@ToString
@AllArgsConstructor
public class User {
    private String name = "Forrest";
    private Integer age;
    private String email;
    private String gender;

    public User(Integer age, String email, String gender) {
        this.age = age;
        this.email = email;
        this.gender = gender;
    }
}

public class Main {
    public static void main(String[] args) {
        User user = new User(20, "forrest@qq.com", "male");
        System.out.println(user);
    }
}

User(name=Forrest, age=20, email=forrest@qq.com, gender=male)
  • 不得不加上@AllArgsConstructor,因为@Builder生成的UserBuilder中用到了全参构造器。

3、不如用@Accessors(chain = true)

java 复制代码
@Data
@Accessors(chain = true)
public class User {
    private String name = "Forrest";
    private Integer age;
    private String email;
    private String gender;
}

public class Main {
    public static void main(String[] args) {
        User user = new User()
                .setAge(18)
                .setEmail("forrest@qq.com")
                .setGender("male");
        System.out.println(user);
    }
}

User(name=Forrest, age=18, email=forrest@qq.com, gender=male)
相关推荐
monkey_meng2 天前
【Rust设计模式之建造者模式】
后端·设计模式·rust·建造者模式
谷丘-CODER4 天前
JAVA设计模式之【建造者模式】
java·设计模式·建造者模式
WaaTong4 天前
《重学Java设计模式》之 建造者模式
设计模式·建造者模式
->yjy4 天前
创建者模式之【建造者模式】
android·建造者模式
编程、小哥哥4 天前
设计模式之建造者模式(各项装修物料组合套餐选配场景)
设计模式·建造者模式
morning_judger11 天前
【设计模式系列】建造者模式(十)
java·设计模式·建造者模式
wrx繁星点点14 天前
创建型模式-建造者模式:构建复杂对象的优雅解决方案
java·开发语言·数据结构·数据库·spring·maven·建造者模式
zzzhpzhpzzz19 天前
设计模式——建造者模式
设计模式·建造者模式
小白19 天前
C# 创建型设计模式----建造者模式
设计模式·建造者模式
阳光开朗_大男孩儿23 天前
原型模式和建造模式的区别
开发语言·c++·设计模式·建造者模式·原型模式