设计模式—— 创建型

81 阅读8分钟

创建型抽象了实例化过程

1 AbstractFactory抽象工厂

1.1 概念

抽象工厂可以理解为一个工厂的工厂,它提供了一种创建相关或者互相依赖对象家族的方式,而无需指定具体的类。这个“家族”可以由不同的工厂生产,但是它们所生产的对象必须是互相关联的。抽象工厂可以把这些关联对象的生产过程集中到一起,方便创建这个家族的各个成员对象。

当我们需要一个产品对象时,只需要找到对应的工厂就可以获取相应的产品.

以生产家具为例子,抽象工厂模式可以被理解为一家家具工厂的工厂,每个具体的工厂(如布艺工厂、皮制工厂),每个工厂会生产与自己相关的一组家具(沙发,茶几、电视柜等)但这些家具必须互相关联,以达到一个完整的家具家族

在这个例子中,抽象工厂模式将每个具体工厂)作为一个抽象工厂。每个家具(如沙发、茶几、电视柜)作为一个抽象产品。具体工厂实现了抽象工厂的接口,生产一组相互关联的抽象产品。

1.2代码示例

package com.cxq.creational;

public class AbstractFactory {
    public static void main(String[] args) {
        //布艺工厂
        Factory factory1 = new FabricFactory();
        System.out.println(factory1.createBed().getSize());
        System.out.println(factory1.createSofa().getType());
        // 皮具工厂
        Factory factory2 = new LeatherFactory();
        System.out.println(factory2.createSofa().getType());
        System.out.println(factory2.createBed().getSize());
    }
}
// 沙发接口
interface Sofa{
    String getType();
}
//床接口
interface Bed{
    String getSize();
}

class FabircSofa implements Sofa{
    @Override
    public String getType() {
        return "布艺沙发";
    }
}

class LeatherSofa implements Sofa{
    @Override
    public String getType() {
        return "皮具沙发";
    }
}

class SingleBed implements Bed{
    @Override
    public String getSize() {
        return "单人床";
    }
}

class DoubleBed implements Bed{
    @Override
    public String getSize() {
        return "双人床";
    }
}

// 定义抽象工厂
interface Factory{
    Sofa createSofa();
    Bed createBed();
}

//布艺工厂
class FabricFactory implements Factory{

    @Override
    public Sofa createSofa() {
        return new FabircSofa();
    }

    @Override
    public Bed createBed() {
        return new SingleBed();
    }
}

//皮具工厂
class LeatherFactory implements Factory {

    @Override
    public Sofa createSofa() {
        return new LeatherSofa();
    }

    @Override
    public Bed createBed() {
        return new DoubleBed();
    }
}

image.png

1.3 应用

2 Builder生成器

2.1 概念

生成器是为了创建复杂对象,它将对象的构造过程分解成多个简单的部分,并使用一个独立的构建器类来控制整个过程。这使得创建复杂对象的过程更加灵活,可维护性更高。 Builder设计模式通常由四个部分组成:产品、抽象构建器、具体构建器和指导者。

  • 产品:需要被构建的对象,它由多个部分组成,每个部分可以是简单的数据类型或复杂的对象。
  • 抽象构建器:定义了构建产品的接口和方法,具体构建器将实现这些方法。
  • 具体构建器:实现了抽象构建器中定义的方法,并负责构建产品的不同部分。
  • 指导者:负责指导具体构建器如何构建产品。

使用Builder设计模式,可以使得创建对象的过程更加灵活,可以按需添加或删除部分,而不需要改变整个构建过程。此外,

2.2 示例代码

比如构建一个用户对象,这里构建的较为简单。

public class Builder {
    User user = new User.UserBuilder()
            .withAge(12)
            .withEmail("XXX@qq.com")
            .withName("李华").build();
}
class User {
    private String name;
    private int age;
    private String email;

    private User(UserBuilder builder) {
        this.name = builder.name;
        this.age = builder.age;
        this.email = builder.email;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

    public String getEmail() {
        return email;
    }

    public static class UserBuilder {
        private String name;
        private int age;
        private String email;

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

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

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

        public User build() {
            return new User(this);
        }
    }
}

使用Builder设计模式构建对象相比直接使用对象的set方法构建具有以下区别:

  1. 链式调用:Builder模式可以使用链式调用,可以通过一行代码构建出一个复杂对象,而使用set方法则需要一步步设置每一个属性值。
  2. 不可变性:Builder模式可以通过将类的属性设置为final来确保对象在构建完成后不可变,避免了对象被篡改的风险。
  3. 可读性:Builder模式可以使用具有描述性的方法名来设置属性值,使得代码更具有可读性和可维护性。
  4. 安全性:Builder模式可以通过将构建过程封装起来,确保对象的构建过程是线程安全的,避免了多线程同时对同一对象进行修改导致的数据不一致问题。

在标准的Builder设计模式中,一旦通过Builder构建出对象,对象的状态就被固定下来,无法再进行修改。这是因为Builder在构建对象时,将对象的各个属性都赋值给了对象本身,而不是通过setter方法一个个地设置属性值,这种方式避免了对象状态的后续修改。如果需要修改对象状态,需要重新使用Builder构建一个新的对象。

当然,这并不是说Builder模式的实现都必须这样,开发人员可以根据实际需求来进行设计和实现。有些Builder模式实现可能允许在构建对象之后对对象的某些属性进行修改,这样做可能会牺牲一定的安全性,但是可以提高灵活性。

3 FactoryMethod工厂方法

概念

它定义了一个用于创建对象的接口,但让子类决定实例化哪个类。工厂方法模式让类把实例化推迟到子类。

该模式的主要思想是创建一个用于创建对象的工厂接口,该接口包含一个用于创建对象的方法。子类可以实现此方法以创建对象的不同实例。这种方法的好处是可以轻松地扩展应用程序,而无需修改现有的代码。此外,工厂方法模式还可以隐藏复杂的对象创建过程,并使客户端代码更加简单和易于维护。

示例代码

package com.cxq.creational;

public class FactoryMethod {
    public static void main(String[] args) {
        PizzaFactory pizzaFactory = new CheesePizzaFactory();
        PizzaFactory pizzaFactory2 = new VeggiePizzaFactory();
        CheesePizza cheesePizza = (CheesePizza) pizzaFactory.createPizza();
        VeggiePizza veggiePizza = (VeggiePizza) pizzaFactory2.createPizza();
    }
}
abstract class Pizza {
    protected String name;

    public abstract void prepare();
    public abstract void bake();
    public abstract void cut();
    public abstract void box();

    public String getName() {
        return name;
    }
}
class CheesePizza extends Pizza {
    public CheesePizza() {
        name = "Cheese Pizza";
    }

    @Override
    public void prepare() {
        System.out.println("Preparing cheese pizza...");
    }

    @Override
    public void bake() {
        System.out.println("Baking cheese pizza...");
    }

    @Override
    public void cut() {
        System.out.println("Cutting cheese pizza...");
    }

    @Override
    public void box() {
        System.out.println("Boxing cheese pizza...");
    }
}

class VeggiePizza extends Pizza {
    public VeggiePizza() {
        name = "Veggie Pizza";
    }

    @Override
    public void prepare() {
        System.out.println("Preparing veggie pizza...");
    }

    @Override
    public void bake() {
        System.out.println("Baking veggie pizza...");
    }

    @Override
    public void cut() {
        System.out.println("Cutting veggie pizza...");
    }

    @Override
    public void box() {
        System.out.println("Boxing veggie pizza...");
    }
}
//
interface PizzaFactory {
    Pizza createPizza();
}

//具体工厂
class CheesePizzaFactory implements PizzaFactory {
    @Override
    public Pizza createPizza() {
        return new CheesePizza();
    }
}

//具体工厂
class VeggiePizzaFactory implements PizzaFactory {
    @Override
    public Pizza createPizza() {
        return new VeggiePizza();

    }
}

抽象工厂和工厂方法的区别在于,工厂方法只能创建一个具体产品类的实例,而抽象工厂可以创建一系列的具体产品类的实例。

4 Singleton单例模式

单例模式即在系统运行中,一个类只存在一个实例,并且系统提供了一个全局访问该实例的方式。

单例模式通常用来创建系统资源、配置数据库连接等。避免创建多个实例造成的资源浪费或者同步问题。 单例模型的创建方式有两种。 双重检测锁方式

package com.cxq.creational;

public class Singleton {
    private static volatile Singleton singleton;
    private Singleton(){
        System.out.println("创建");
    }
    public static Singleton getInstance(){

        if(singleton== null){
            synchronized (singleton){
                if(singleton == null){
                    singleton = new Singleton();
                }
            }
        }
        return singleton;
    }
}

静态内部类方式

单例模式主要适用于以下场景:

  • 需要频繁使用的对象,例如数据库连接池、线程池等;
  • 需要控制和管理的对象,例如系统配置、日志文件等;
  • 需要避免多个实例间的冲突和资源竞争的对象,例如计数器、序列号生成器等。

在使用单例模式时,需要注意以下事项:

  • 需要考虑线程安全性,可以使用双重检查锁、静态内部类等方式来保证线程安全;
  • 需要注意内存泄漏的问题,可以使用弱引用、软引用等方式来避免内存泄漏;
  • 需要注意反射和序列化等情况可能会破坏单例模式,可以使用枚举类型等方式来避免这种情况。

5 Prototype原型模式

原型模式允许我们通过复制对象来创建一些复杂对象。 在使用原型模式时,我们首先需要创建一个原型对象,然后将其复制多份以创建新对象。在 Java 中,原型模式的实现需要实现 Cloneable 接口,并重写 clone() 方法。

下面我们来通过一个例子更好地理解原型模式的概念和用法。假设我们有一个简单的图形类 Shape,它有一个 draw() 方法用于绘制图形。我们需要创建一个 Circle 类,它是 Shape 类的子类,并且具有不同的属性和行为。

在传统的对象创建方式中,我们需要编写一个 Circle 类,定义其属性和行为,然后在程序中实例化这个类。而使用原型模式,则可以直接复制已有的 Circle 对象,从而避免了重复编写类的过程。

public abstract class Shape implements Cloneable {
   private String id;
   protected String type;
   
   public String getId() {
      return id;
   }
   
   public void setId(String id) {
      this.id = id;
   }
   
   public String getType() {
      return type;
   }
   
   public void setType(String type) {
      this.type = type;
   }
   
   public abstract void draw();
   
   public Object clone() {
      Object clone = null;
      
      try {
         clone = super.clone();
      } catch (CloneNotSupportedException e) {
         e.printStackTrace();
      }
      
      return clone;
   }
}

public class Circle extends Shape {
   public Circle() {
      type = "Circle";
   }
   
   @Override
   public void draw() {
      System.out.println("Drawing a circle");
   }
}

public class PrototypePatternDemo {
   public static void main(String[] args) {
      Circle circle = new Circle();
      circle.setId("1");
      
      Circle clonedCircle = (Circle) circle.clone();
      clonedCircle.setId("2");
      
      System.out.println("Original object: " + circle.getType() + ", ID: " + circle.getId());
      System.out.println("Cloned object: " + clonedCircle.getType() + ", ID: " + clonedCircle.getId());
   }
}

原型模式的优点在于它能够提高程序的效率,因为对象的创建通常比对象的复制更加耗费时间和资源。此外,原型模式还能够提高代码的灵活性,因为它允许我们动态地添加、删除和修改对象。