经典设计模式:单例模式、工厂模式

1. 单例模式

1.1 懒汉式:在真正需要使用对象时才去创建该单例类对象

懒汉式创建对象的方法是在程序使用对象前,先判断该对象是否已经实例化**(判空),**若已实例化直接返回该类对象。否则则先执行实例化操作。

java 复制代码
public class Singleton {
    
    private static Singleton singleton;
    
    private Singleton(){}
    
    public static Singleton getInstance() {
        if (singleton == null) {
            singleton = new Singleton();
        }
        return singleton;
    }
    
}

懒汉式如何保证只创建一个对象

最容易想到的解决方法就是在方法上加锁,或者是对类对象加锁,

这样就规避了两个线程同时创建Singleton对象的风险,但是引来另外一个问题:每次去获取对象都需要先获取锁,并发性能非常地差,极端情况下,可能会出现卡顿现象。

接下来要做的就是优化性能,目标是:如果没有实例化对象则加锁创建,如果已经实例化了,则不需要加锁,直接获取实例

所以直接在方法上加锁的方式就被废掉了,因为这种方式无论如何都需要先获取锁

java 复制代码
public static Singleton getInstance() {
    if (singleton == null) {  // 线程A和线程B同时看到singleton = null,如果不为null,则直接返回singleton
        synchronized(Singleton.class) { // 线程A或线程B获得该锁进行初始化
            if (singleton == null) { // 其中一个线程进入该分支,另外一个线程则不会进入该分支
                singleton = new Singleton();
            }
        }
    }
    return singleton;
}

因为需要两次判空,且对类对象加锁,该懒汉式写法也被称为:Double Check(双重校验) + Lock(加锁)

1.2 饿汉式:在类加载时已经创建好该单例对象,等待被程序使用

饿汉式在类加载 时已经创建好该对象,在程序调用时直接返回 该单例对象即可,即我们在编码时就已经指明了要马上创建这个对象,不需要等到被调用时再去创建

java 复制代码
public class Singleton{
    
    private static final Singleton singleton = new Singleton();
    
    private Singleton(){}
    
    public static Singleton getInstance() {
        return singleton;
    }
}
typescript 复制代码
// 饿汉式单例
class EagerSingleton {
    private static readonly singleton: EagerSingleton = new EagerSingleton();
    
    private constructor() {}
    
    public static getInstance(): EagerSingleton {
        return EagerSingleton.singleton;
    }
}

// 懒汉式单例
class LazySingleton {
    private static singleton: LazySingleton;
    
    private constructor() {}
    
    public static getInstance(): LazySingleton {
        if (!LazySingleton.singleton) {
            LazySingleton.singleton = new LazySingleton();
        }
        return LazySingleton.singleton;
    }
}

// 验证饿汉式单例
const eager1 = EagerSingleton.getInstance();
const eager2 = EagerSingleton.getInstance();
console.log("饿汉式单例是否为同一实例:", eager1 === eager2); // 输出 true

// 验证懒汉式单例
const lazy1 = LazySingleton.getInstance();
const lazy2 = LazySingleton.getInstance();
console.log("懒汉式单例是否为同一实例:", lazy1 === lazy2); // 输出 true

2. 工厂模式

什么是工厂模式:

​ 工厂模式将目的将创建对象的具体过程屏蔽隔离起来,从而达到更高的灵活性,工厂模式可以分为三类:

  • 简单工厂模式(Simple Factory)
  • 工厂方法模式(Factory Method)
  • 抽象工厂模式(Abstract Factory)

这三种模式从上到下逐步抽象,并且更具一般性。《设计模式》一书中将工厂模式分为两类:工厂方法模式与抽象工厂模式。将简单工厂模式看为工厂方法模式的一种特例,两者归为一类。

java 复制代码
public class BMW320 {
        public BMW320(){
                System.out.println("制造-->BMW320");
        }
}
 
public class BMW523 {
        public BMW523(){
                System.out.println("制造-->BMW523");
        }
}
 
public class Customer {
        public static void main(String[] args) {
                BMW320 bmw320 = new BMW320();
                BMW523 bmw523 = new BMW523();
        }
}

2.1 简单工厂模式

简单工厂模式的核心是定义一个创建对象的接口,将对象的创建和本身的业务逻辑分离,降低系统的耦合度,使得两个修改起来相对容易些,当以后实现改变时,只需要修改工厂类即可。

代码实现

产品类:

java 复制代码
abstract class BMW {
        public BMW(){}
}
 
public class BMW320 extends BMW {
        public BMW320() {
                System.out.println("制造-->BMW320");
        }
}
public class BMW523 extends BMW{
        public BMW523(){
                System.out.println("制造-->BMW523");
        }
}

工厂类:

java 复制代码
public class Factory {
        public BMW createBMW(int type) {
                switch (type) {
                
                case 320:
                        return new BMW320();
 
                case 523:
                        return new BMW523();
 
                default:
                        break;
                }
                return null;
        }
}

用户类:

java 复制代码
public class Customer {
        public static void main(String[] args) {
                Factory factory = new Factory();
                BMW bmw320 = factory.createBMW(320);
                BMW bmw523 = factory.createBMW(523);
        }
}

优缺点

简单工厂模式提供专门的工厂类用于创建对象,实现了对象创建和使用的职责分离,客户端不需知道所创建的具体产品类的类名以及创建过程,只需知道具体产品类所对应的参数即可,通过引入配置文件,可以在不修改任何客户端代码的情况下更换和增加新的具体产品类,在一定程度上提高了系统的灵活性。

但缺点在于不符合"开闭原则",每次添加新产品就需要修改工厂类。在产品类型较多时,有可能造成工厂逻辑过于复杂,不利于系统的扩展维护,并且工厂类集中了所有产品创建逻辑,一旦不能正常工作,整个系统都要受到影响。

为了解决简单工厂模式的问题,出现了工厂方法模式。

2.2 工厂方法模式

优缺点

工厂方法模式将工厂抽象化 ,并定义一个创建对象的接口 。每增加新产品,只需增加该产品以及对应的具体实现工厂类,由具体工厂类决定要实例化的产品是哪个,将对象的创建与实例化延迟到子类,这样工厂的设计就符合"开闭原则"了,扩展时不必去修改原来的代码。在使用时,用于只需知道产品对应的具体工厂,关注具体的创建过程,甚至不需要知道具体产品类的类名,当我们选择哪个具体工厂时,就已经决定了实际创建的产品是哪个了。

但缺点在于,每增加一个产品都需要增加一个具体产品类和实现工厂类,使得系统中类的个数成倍增加,在一定程度上增加了系统的复杂度,同时也增加了系统具体类的依赖。

代码实现

产品类:

同简单工厂模式产品类

工厂类:

java 复制代码
interface FactoryBMW {
        BMW createBMW();
}
 
public class FactoryBMW320 implements FactoryBMW{
 
        @Override
        public BMW320 createBMW() {
                return new BMW320();
        }
 
}
public class FactoryBMW523 implements FactoryBMW {
        @Override
        public BMW523 createBMW() {
                return new BMW523();
        }
}

客户类:

java 复制代码
public class Customer {
        public static void main(String[] args) {
                FactoryBMW320 factoryBMW320 = new FactoryBMW320();
                BMW320 bmw320 = factoryBMW320.createBMW();
 
                FactoryBMW523 factoryBMW523 = new FactoryBMW523();
                BMW523 bmw523 = factoryBMW523.createBMW();
        }
}

2.3 抽象工厂模式

优缺点

抽象工厂模式主要用于创建相关对象的家族。当一个产品族中需要被设计在一起工作时,通过抽象工厂模式,能够保证客户端始终只使用同一个产品族中的对象;并且通过隔离具体类的生成,使得客户端不需要明确指定具体生成类;所有的具体工厂都实现了抽象工厂中定义的公共接口,因此只需要改变具体工厂的实例,就可以在某种程度上改变整个软件系统的行为。

但该模式的缺点在于添加新的行为时比较麻烦,如果需要添加一个新产品族对象时,需要更改接口及其下所有子类,这必然会带来很大的麻烦。

代码实现

产品类:

java 复制代码
//发动机以及型号  
public interface Engine {}  
 
public class EngineA implements Engine{  
    public EngineA(){  
        System.out.println("制造-->EngineA");  
    }  
}  
public class EngineB implements Engine{  
    public EngineB(){  
        System.out.println("制造-->EngineB");  
    }  
}  
 
 
//空调以及型号  
public interface Aircondition {} 
 
public class AirconditionA implements Aircondition{  
    public AirconditionA(){  
        System.out.println("制造-->AirconditionA");  
    }  
}  
public class AirconditionB implements Aircondition{  
    public AirconditionB(){  
        System.out.println("制造-->AirconditionB");  
    }  
} 

工厂类:

java 复制代码
//创建工厂的接口  
public interface AbstractFactory {  
    //制造发动机
    public Engine createEngine();
    //制造空调 
    public Aircondition createAircondition(); 
}  
 
//为宝马320系列生产配件  
public class FactoryBMW320 implements AbstractFactory{     
    @Override  
    public Engine createEngine() {    
        return new EngineA();  
    }  
    @Override  
    public Aircondition createAircondition() {  
        return new AirconditionA();  
    }  
}  
//宝马523系列
public class FactoryBMW523 implements AbstractFactory {  
     @Override  
    public Engine createEngine() {    
        return new EngineB();  
    }  
    @Override  
    public Aircondition createAircondition() {  
        return new AirconditionB();  
    }  
} 

客户类:

java 复制代码
//创建工厂的接口  
public interface AbstractFactory {  
    //制造发动机
    public Engine createEngine();
    //制造空调 
    public Aircondition createAircondition(); 
}  
 
//为宝马320系列生产配件  
public class FactoryBMW320 implements AbstractFactory{     
    @Override  
    public Engine createEngine() {    
        return new EngineA();  
    }  
    @Override  
    public Aircondition createAircondition() {  
        return new AirconditionA();  
    }  
}  
//宝马523系列
public class FactoryBMW523 implements AbstractFactory {  
     @Override  
    public Engine createEngine() {    
        return new EngineB();  
    }  
    @Override  
    public Aircondition createAircondition() {  
        return new AirconditionB();  
    }  
} 

参考:

我给面试官讲解了单例模式后,他对我竖起了大拇指!

Java设计模式之创建型:工厂模式详解(简单工厂+工厂方法+抽象工厂)

相关推荐
Scott9999HH1 小时前
【IIoT流量实战】蒸汽管道阀门全关却仍有流量?用 Python 实现涡街信号 FFT 频谱分析与温压全补偿积算网关,深度拆解靠谱的涡街流量计厂家硬核技术标准
开发语言·python
腻害兔1 小时前
【若依项目-产品经理视角】深度拆解 RuoYi-Vue-Pro 商城模块:从商品管理到交易引擎,50 张表撑起一整套电商系统
java·大数据·vue.js·产品经理·ai编程
码智社2 小时前
AES加密原理详解及Java实现加解密实战
java·开发语言
AI云海2 小时前
python 列表、元组、集合和字典
开发语言·python
萧瑟余晖3 小时前
JDK 26 新特性详解
java·开发语言
马优晨4 小时前
Freemarker 完整讲解(后端 Java 模板引擎)
java·开发语言·freemarker·freemarker 完整讲解·freemarker模板引擎
人邮异步社区5 小时前
怎么把C语言学到精通?
c语言·开发语言
心平气和量大福大6 小时前
C#-WPF-控件-TextBox 数据绑定
开发语言·c#·wpf
ttwuai6 小时前
Cursor 生成 CRUD 后,Go 后台接口别只测 200:JWT、RBAC 和 tenant_id 怎么验
开发语言·后端·golang
维天说6 小时前
CLI-Switch 2026年3月版历史设计:Hook、TTY 隔离与 JSON 状态
java·服务器·json