创建型模式

建造者

将复杂对象的构建与表示相分离,同样的构建过程可以创建不同的表示

为了应对在对象创建过程中,构造函数参数过多、参数间有依赖关系或约束条件、以及对象希望不可变等复杂场景。通过Builder模式,能够将对象的创建逻辑集中在一个单独的Builder类中,避免了构造函数参数过长、校验逻辑难以安放、以及对象处于无效状态等问题

classDiagram    class Builder {        +buildPart()    }    class ConcreteBuilder {        +buildPart()        +getResult()    }    Builder <|-- ConcreteBuilder    ConcreteBuilder --> Product    class Director {        +construct()    }    Director o--> Builder
interface Builder{    Builder process1();    Builder process2();    Builder process3();    Product build();}class ConcreteBuilder implements Builder{    // 方法实现...}class ProductDirector{    public Product constructProduct(Builder builder){        builder.process1();        builder.process2();        builder.process3();        return builder.build();    }}// 使用ProductDirector director = new ProductDirector();Product product = director.constructProduct(new ConcreteBuilder());

工厂模式

简单工厂

客户无需知道具体产品的名称,只需要知道产品类所对应的参数即可

class Factory{    public Product get(int condition){        switch(condition){            case 1:                return new Product1();            case 2:                return new Product2();        }        return null;    }}

但是工厂的职责过重,而且当类型过多时不利于系统的扩展维护

工厂方法

定义一个接口,让子类创建该接口的实例,也就是将实例化延迟到工厂的子类

classDiagram    ConcreteProduct --|> Product    class ConcreteCreator {        +factoryMethod() ConcreteProduct    }    class Creator {        +factoryMethod() Product        +operation()    }    ConcreteCreator --|> Creator    ConcreteCreator ..> ConcreteProduct

模板方式和工厂模式的核心思想非常类似, 都是把一些操作留给子类去实现。模板方法经常使用工厂方法作为其算法的一部分

abstract class AbstractCreator{    abstract Product get();    public void doSomething(){        // do something        Product product = get();        // do something    }}class Creator1 extends AbstractCreator{    Product get(){...}}class Creator2 extends AbstractCreator{    Product get(){...}}// 使用Factory factory = new Creator2();Product product = factory.doSomething();

抽象工厂

提供一个创建一系列相关或相互依赖对象的接口,而无需指定具体类

classDiagram    class AbstractFactory {        +createProductA()        +createProductB()    }    class ConcreteFactory1 {        +createProductA()        +createProductB()    }    class ConcreteFactory2 {        +createProductA()        +createProductB()    }    ConcreteFactory1 --|> AbstractFactory    ConcreteFactory2 --|> AbstractFactory        ProductA1 --|> AbstractProductA    ProductA2 --|> AbstractProductA        ProductB1 --|> AbstractProductB    ProductB2 --|> AbstractProductB    client ..> AbstractFactory    client ..> AbstractProductA    client ..> AbstractProductB
abstract class Factory{    abstract Product get(int condition);}class ProductAFactory extends Factory{    ProductA get(int condition){...}}class ProductBFactory extends Factory{    ProductB get(int condition){...}}// 使用Factory factory = new ProductAFactory();Product product = factory.get(condition);

在实践中,每个工厂一般都会是单例。工厂内部可使用原型模式来实现

原型

通过一个原型对象创建新的对象

为了应对创建成本较高的对象,尤其是当同类对象之间差异不大时。原型模式通过复制(或拷贝)现有对象来创建新对象,从而节省时间和资源

classDiagram    class Prototype {        +clone()    }    class ConcretePrototype1 {        +clone()    }    class ConcretePrototype2 {        +clone()    }    Prototype <|-- ConcretePrototype1    Prototype <|-- ConcretePrototype2    Client --> Prototype
class Product {    Part1 part1;    @Override    protected Object clone() throws CloneNotSupportedException {        Product product = (Product) super.clone();        product.part1 = (Part1)part1.clone();        return product;    }}

单例

一个类仅有一个实例,并只拥有一个全局访问点

问题:谁来销毁单例对象?什么时候销毁?

饿汉式

public class Singleton {    private static final Singleton SINGLETON = new Singleton();    private Singleton() { }        public static Singleton getInstance(){        return SINGLETON;    }}

懒汉式

public class Singleton {    private static Singleton SINGLETON ;    private Singleton() { }    // 线程不安全    public static Singleton getInstance(){        if (SINGLETON == null){            SINGLETON = new Singleton();        }        return SINGLETON;    }}

静态内部类方式

public class Singleton {        private Singleton() { }    private static class SingletonClass{        public static final Singleton SINGLETON = new Singleton();    }        public static Singleton getInstance(){        return SingletonClass.SINGLETON;    }}

枚举单例

public class Singleton {    private Singleton() { }    private enum  SingletonEnum{        INSTANCE;        private Singleton singleton;        SingletonEnum() {            singleton = new Singleton();        }        public Singleton getSingleton() {            return singleton;        }    }    public static Singleton getInstance(){        return SingletonEnum.INSTANCE.getSingleton();    }}

双重检测加锁

public class Singleton {    private static volatile Singleton SINGLETON; // 如果没有volatile JVM的指令重排序很有可能导致实例化多个对象    private Singleton() { }    public static Singleton getInstance(){        if (SINGLETON == null){            synchronized (Singleton.class){                if (SINGLETON == null){                    SINGLETON = new Singleton();                }            }        }        return SINGLETON;    }}
// SINGLETON = new Singleton() 可以分解为以下三个步骤1 memory=allocate();// 分配内存 相当于c的malloc2 ctorInstanc(memory) //初始化对象3 s=memory //设置s指向刚分配的地址// 上述三个步骤可能会被重排序为 1-3-2,也就是:1 memory=allocate();// 分配内存 相当于c的malloc3 s=memory //设置s指向刚分配的地址2 ctorInstanc(memory) //初始化对象

单例的问题

  1. 对 OOP 特性的支持不友好:单例模式对面向对象编程(OOP)的抽象、继承和多态特性支持不足。它违背了“基于接口而非实现”的设计原则,导致代码在需求变化时需要大范围修改,并且单例类的继承和多态实现也会使代码的可读性和扩展性降低。
  2. 隐藏类之间的依赖关系:单例模式直接调用类方法,而不需要通过构造函数或参数传递依赖关系。这种隐式依赖使得代码难以阅读和理解,尤其在代码复杂时,难以清晰地识别类与类之间的依赖关系。
  3. 对代码的扩展性不友好:单例模式只允许一个实例对象,当系统需求变化需要多个实例时,单例模式难以适应,可能导致大范围的代码修改
  4. 对代码的可测试性不友好:单例模式的硬编码方式难以进行依赖注入或替换,导致代码难以进行单元测试。同时,单例类如果持有可变全局变量,还会导致测试用例之间的相互影响。
  5. 不支持有参数的构造函数:单例模式通常不支持传递参数来创建实例,限制了其灵活性。虽然可以通过 init() 方法或其他方式来绕过这一限制,但这些方法可能引发新的问题,如参数无效而未提示用户的问题