- 主要关注点是 “怎样创建对象”,主要特点是 “将对象的创建与使用分离”,这样可以降低系统的耦合度,使用者不需要关注对象的创建细节
- 创造者模式包括 单例模式、原型模式、工厂方法模式、抽象工厂模式、建造者模式
一、单例模式
- 该类负责创建自己的对象,同时确保只有单个对象被创建,这个类提供一种访问其唯一的对象的方式,可以直接访问,不需要直接实例化该类的对象
- 存在问题
- 破坏单例模式
- 使用单例类可以创建多个对象,枚举方式除外
- 序列化:向文件写对象,再从文件中读取对象,读取到的对象都不一样
- 反射:获取字节码对象(xxx.class) -> 获取无参构造(.getDeclaredConstructor()) -> 取消访问检查(.setAccessible(true)) -> 创建 xxx 对象(.newInstance())
- 问题解决
- 序列化:在类中添加 readResolve() 方法,在反序列化时被反射调用,定义了这个方法就返回这个方法的值,没有就 new 新对象
- 反射:在私有无参构造方法里面判断对象是否已经创建,如果已创建直接抛异常
1.1 饿汉式(类加载时创建)
- 存在内存浪费问题
- 不考虑内存浪费问题,极力推荐枚举方式的单例实现模式,线程安全,只会装载一次,唯一一种不会被破坏的单例实现模式
public class SingletonHungryStaticBlock {
private SingletonHungryStaticBlock(){}
private static SingletonHungryStaticBlock instance;
static {
instance = new SingletonHungryStaticBlock();
}
public static SingletonHungryStaticBlock getInstance() {
return instance;
}
}
public class SingletonHungryStaticValue {
private SingletonHungryStaticValue(){}
private static SingletonHungryStaticValue instance = new SingletonHungryStaticValue();
public static SingletonHungryStaticValue getInstance() {
return instance;
}
}
1.2 懒汉式(调用时创建)
- 存在线程安全问题,使用双重检查可以解决
- 多线程情况下可能会出现空指针问题,可以加 volatile 关键字解决,volatile 关键字可以保证可见性和有序性
- 添加 volatile 关键字之后的双重检查锁模式是一种比较好的单例实现模式,能够保证在多线程情况下线程安全不会有性能问题
- 静态内部类单例模式是一种优秀的单例模式,是开源项目中比较常用的一种单例模式,在没有加锁的情况下,保证类多线程下的安全,没有任何性能影响和空间浪费
public class SingletonLazyInsideClass {
private SingletonLazyInsideClass() {}
private static class SingletonHolder {
private static final SingletonLazyInsideClass INSTANCE = new SingletonLazyInsideClass();
}
public static SingletonLazyInsideClass getInstance() {
return SingletonHolder.INSTANCE;
}
}
public class SingletonLazyStaticValue {
private SingletonLazyStaticValue(){}
private static volatile SingletonLazyStaticValue instance;
public static SingletonLazyStaticValue getInstance() {
if (instance == null) {
synchronized (SingletonLazyStaticValue.class) {
if (instance == null) {
instance = new SingletonLazyStaticValue();
}
}
}
return instance;
}
}
二、工厂方法模式
2.1 简单工厂模式
- 优点:封装类创建对象的过程,可以直接通过参数获取对象,把对象的创建和业务逻辑层分开,避免修改客户代码,直接修改工厂类即可
- 缺点:增加新产品需要修改工厂类代码,违背 开闭原则
- 扩展:将工厂类中创建对象的功能定义为静态类,这个就是静态工厂模式
public class CoffeeStore {
public Coffee orderCoffee(String name) {
SimpleCoffeeFactory factory = new SimpleCoffeeFactory();
Coffee coffee = factory.createCoffee(name);
coffee.addMilk();
coffee.addSugar();
return coffee;
}
@Test
public void test() {
CoffeeStore coffeeStore = new CoffeeStore();
Coffee coffee = coffeeStore.orderCoffee("latte");
System.out.println(coffee.getName());
}
}
public abstract class Coffee {
public abstract String getName();
public void addSugar() {
System.out.println("加糖");
}
public void addMilk() {
System.out.println("加奶");
}
}
public class AmericanCoffee extends Coffee{
@Override
public String getName() {
return "美式";
}
}
public class LatteCoffee extends Coffee{
@Override
public String getName() {
return "拿铁";
}
}
public class SimpleCoffeeFactory {
public Coffee createCoffee(String type) {
Coffee coffee = null;
switch (type) {
case "american": {
coffee = new AmericanCoffee();
break;
}
case "latte": {
coffee = new LatteCoffee();
break;
}
default: {
throw new RuntimeException("没有 " + type + " 咖啡");
}
}
return coffee;
}
}
2.2 工厂方法模式
- 概念:定义一个用于创建对象的接口,让子类决定实例化哪个产品对象,工厂方法使一个产品类的实例化延迟到其工厂的子类
- 优点:只需要知道工厂名称就能获取,不需要知道具体创建过程,添加不需要对原工厂进行任何修改,满足开闭原则
- 缺点:每增加一个产品就要增加一个具体产品类和一个对应对具体工厂类,增加系统复杂度
public interface CoffeeFactory {
Coffee createCoffee();
}
public abstract class Coffee {
public abstract String getName();
public void addSugar() {
System.out.println("加糖");
}
public void addMilk() {
System.out.println("加奶");
}
}
public class AmericanCoffee extends Coffee {
@Override
public String getName() {
return "美式";
}
}
public class AmericanCoffeeFactory implements CoffeeFactory{
@Override
public Coffee createCoffee() {
return new AmericanCoffee();
}
}
public class CoffeeStore {
private CoffeeFactory factory;
public void setFactory(CoffeeFactory factory) {
this.factory = factory;
}
public Coffee orderCoffee() {
Coffee coffee = factory.createCoffee();
coffee.addMilk();
coffee.addSugar();
return coffee;
}
@Test
public void consumer() {
CoffeeStore store = new CoffeeStore();
CoffeeFactory coffeeFactory = new AmericanCoffeeFactory();
store.setFactory(coffeeFactory);
Coffee coffee = store.orderCoffee();
System.out.println(coffee.getName());
}
}
2.3 抽象工厂模式
- 概念:为访问类提供一个创建一组相关或者相互依赖对象的接口,且访问类无需指定所要产品的具体类就能获得同族的不同等级的产品模式结构
- 抽象工厂模式是工厂方法模式的升级版本,工厂方法模式只生产一个等级产品,抽象工厂模式可以生产多个等级的产品
- 优点:当一个产品族中多个对象被设计成一起工作时,能保证客户端始终只使用一个产品族中的对象
- 缺点;当需要增加一个新产品时,所有的工厂类都需要进行修改
- 场景:需要创建的对象是一系列相互关联或相互依赖的产品族时,系统有多个产品族但每次只使用其中某一族,提供类产品的类库,且所有产品的接口相同,客户端不依赖产品实例的创建细节和内部结构
public abstract class Coffee {
public abstract String getName();
public void addSugar() {
System.out.println("加糖");
}
public void addMilk() {
System.out.println("加奶");
}
}
public class LatteCoffee extends Coffee {
@Override
public String getName() {
return "拿铁";
}
}
public abstract class Dessert {
public abstract void show();
}
public class Trimisu extends Dessert{
@Override
public void show() {
System.out.println("提拉米苏");
}
}
public interface DessertFactory {
Coffee createCoffee();
Dessert crateDessert();
}
public class ItalyDessertFactory implements DessertFactory{
@Override
public Coffee createCoffee() {
return new LatteCoffee();
}
@Override
public Dessert crateDessert() {
return new Trimisu();
}
}
public class Main {
public static void main(String[] args) {
DessertFactory factory = new ItalyDessertFactory();
Coffee coffee = factory.createCoffee();
Dessert dessert = factory.crateDessert();
System.out.println(coffee.getName());
dessert.show();
}
}
三、原型模式
- 概述:用一个已经创建的实例作为原型,通过复制该原型对象来创建一个和原型对象相同的新对象
3.1 浅克隆
- 对于非基本类型属性,仍指向原有属性所指向的对象的内存地址
- 场景:对象创建非常复杂,性能和安全要求比较高
public class Citation implements Cloneable{
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void show() {
System.out.println(name + " 是三好学生");
}
@Override
public Citation clone() throws CloneNotSupportedException {
return (Citation) super.clone();
}
}
public class Client {
public static void main(String[] args) throws CloneNotSupportedException {
Citation citation = new Citation();
citation.setName("11");
Citation citation1 = citation.clone();
citation1.setName("22");
citation.show();
citation1.show();
}
}
3.2 深克隆
- 属性中引用的其他对象,不再指向原有对象地址,需要使用序列化进行克隆
- 场景:有非基本类型的对象克隆
public class Citation implements Cloneable, Serializable {
private Student student;
public Student getStudent() {
return student;
}
public void setStudent(Student student) {
this.student = student;
}
public void show() {
System.out.println(student.getName() + " 是三好学生");
}
@Override
public Citation clone() throws CloneNotSupportedException {
return (Citation) super.clone();
}
}
public class Student implements Serializable {
private String name;
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
'}';
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public class Client {
public static void main(String[] args) throws CloneNotSupportedException, IOException, ClassNotFoundException {
Citation citation = new Citation();
Student student = new Student();
student.setName("11");
citation.setStudent(student);
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("a.txt"));
oos.writeObject(citation);
oos.close();
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("a.txt"));
Citation citation1 = (Citation) ois.readObject();
ois.close();
citation1.getStudent().setName("22");
citation.show();
citation1.show();
}
}
四、建造者模式
- 概述:将一个复杂对象的构造与表示分离,使得同样的构建过程可创建不同表示
- 分离了部件的构造(由 Builder 来负责)和装配(由 Director 负责)
- 实现了构建和装配的解耦,不同的构建器,相同的装配,也可以做出不同的对象
- 建造者模式可以将部件和其组装过程分开,一步一步创建一个复杂的对象,用户只需要指定复杂对象的类型就可以得到该对象
- 优点
- 封装性好、更加精细的控制产品的创建过程、容易扩展
- 将产品本身与产品的创建过程解耦,相同的创建过程可以创建不同的产品对象
- 缺点
- 创建的产品具有较多的共同点,如果差异较大,不适合使用建造者模式,适用范围有一定的限制
- 使用场景
- 创建对象较复杂,由多个部件构成,各部件面临复杂的变化,但构件间的建造顺序是稳定的
- 创建对象的算法独立于该对象的组成部分以及他们的装配方式,即产品构建过程和最终的表示是独立的
public class Director {
private Builder builder;
public Director(Builder builder) {
this.builder = builder;
}
public Bike construct() {
builder.buildFrame();
builder.buildSeat();
return builder.createBike();
}
}
public abstract class Builder {
protected Bike bike = new Bike();
public abstract void buildFrame();
public abstract void buildSeat();
public abstract Bike createBike();
}
@Getter
@Setter
public class Bike {
private String frame;
private String seat;
}
public class MobileBuilder extends Builder{
@Override
public void buildFrame() {
bike.setFrame("碳纤维车架");
}
@Override
public void buildSeat() {
bike.setSeat("真皮车座");
}
@Override
public Bike createBike() {
return bike;
}
}
public class Client {
public static void main(String[] args) {
Director director = new Director(new MobileBuilder());
Bike bike = director.construct();
System.out.println(bike.getFrame());
System.out.println(bike.getSeat());
}
}
扩展:链式编程
public class Phone {
private String cpu;
private String screen;
private String memory;
private String mainBoard;
@Override
public String toString() {
return "Phone{" +
"cpu='" + cpu + '\'' +
", screen='" + screen + '\'' +
", memory='" + memory + '\'' +
", mainBoard='" + mainBoard + '\'' +
'}';
}
private Phone(Builder builder) {
this.cpu = builder.cpu;
this.screen = builder.screen;
this.memory = builder.memory;
this.mainBoard = builder.mainBoard;
}
public static final class Builder{
private String cpu;
private String screen;
private String memory;
private String mainBoard;
public Builder cpu(String cpu) {
this.cpu = cpu;
return this;
}
public Builder screen(String screen) {
this.screen = screen;
return this;
}
public Builder memory(String memory) {
this.memory = memory;
return this;
}
public Builder mainBoard(String mainBoard) {
this.mainBoard = mainBoard;
return this;
}
public Phone build() {
return new Phone(this);
}
}
}
public class Client {
public static void main(String[] args) {
Phone phone = new Phone.Builder()
.cpu("Intel")
.screen("三星")
.memory("金士顿")
.mainBoard("华硕")
.build();
System.out.println(phone);
}
}
五、模式对比
- 工厂方法模式 vs 建造者模式
- 工厂方法模式更注重整体对象的创建方式,建造者模式注重的是部件构建过程
- 抽象工厂模式 vs 建造者模式
- 抽象工厂模式实现对产品家族的创建,一个产品家族是这样的一系列产品,具有不同分类纬度的产品组合,抽象工厂模式不需要关心构建过程,只关心什么产品由什么工厂生产即可
- 建造者模式是按照指定的蓝图建造产品,目的是通过组装零配件而产生一个新产品