当前位置: 首页 > news >正文

手撕四种常用设计模式(工厂,策略,代理,单例)

工厂模式

一、工厂模式的总体好处

  1. 解耦:客户端与具体实现类解耦,符合“开闭原则”。
  2. 统一创建:对象创建交由工厂处理,便于集中控制。
  3. 增强可维护性:新增对象种类时不需要大改动调用代码。
  4. 便于扩展:易于管理产品族或产品等级结构。、

手写静态工厂模式,通过一个汽车静态工厂负责创建汽车

特点:
  • 工厂类通过一个静态方法来返回不同的对象。
  • 客户端通过传入参数决定创建哪个类。
✅ 优点:
  • 实现简单,结构清晰。
  • 对客户端隐藏了对象的具体创建过程。
⚠️ 缺点:
  • 不符合开闭原则(新增产品需修改 createCar() 方法)。
  • 工厂职责过重,产品一多代码臃肿。
public class StaticFactoryModel {public static void main(String[] args){car car=new CarFactory.createCar("Tesla");
}
interface car{void drive();
}
class Tesla implements car{@Overridepublic void drive(){System.out.println("drive Tesla");}
}
class toyota implements car{@Overridepublic void drive(){System.out.println("drive toyota");}
}class CarFactory{public static car createCar(String type){switch(type){case"Tesla": return new Tesla();case"toyota":return new toyota();default:throw new IllegalArgumentException("UnKnow Car");}}
}

手写工厂方法模式

特点:
  • 将创建对象的工作延迟到子类,通过不同工厂子类创建不同对象。
✅ 优点:
  • 满足开闭原则,新增产品只需新增对应工厂。
  • 结构清晰,职责单一,每个工厂只负责一种产品的创建。
⚠️ 缺点:
  • 类的数量变多,增加系统复杂度。
  • 只能生产单一类型的产品。
package com;public class FactoryMethod {public static void main(String[] args) {phoneFactory factory=new iPhoneFactory();phone myphone=factory.createPhone();myphone.call();}
}interface phone{public void call();
}
class iPhone implements phone{@Overridepublic void call(){System.out.println("iPhone call");}
}
class Huawei implements phone{@Overridepublic void call(){System.out.println("Huawei call");}
}
interface phoneFactory{public phone createPhone();
}
class iPhoneFactory implements phoneFactory{@Overridepublic phone createPhone(){return new iPhone();}
}
class HuaweiFactory implements phoneFactory{@Overridepublic phone createPhone(){return new Huawei();}
}

手写抽象工厂模式

✅ 特点:
  • 一个工厂可以生产多个相关的产品(如电脑 + 操作系统)。
✅ 优点:
  • 更强的扩展能力,可以生产“产品族”(多个相关产品)。
  • 高度封装了产品的创建细节,对客户端透明。
⚠️ 缺点:
  • 不易新增“新产品”(比如新加一个 Printer 接口)需修改所有工厂。
  • 抽象程度更高,理解成本稍大。
package com;public class AbstractFactory {public static void main(String[] args){ShowFactory factory=new WinFactory();Computee myCom=factory.createCom();Os myOs=factory.createOs();}
}interface Computee{public void use();
}
interface Os{public void call();
}class hp implements  Computee{@Overridepublic void use() {System.out.println("useing window");}
}
class AppleCom implements  Computee{@Overridepublic void use() {System.out.println("using apple");}
}class window implements Os{@Overridepublic void call() {System.out.println("calling window");}
}
class AppleOS implements Os{@Overridepublic void call() {System.out.println("calling apple");}
}
interface ShowFactory{Computee createCom();Os createOs();
}
class WinFactory implements ShowFactory{@Overridepublic Computee createCom() {return new hp();}@Overridepublic Os createOs() {return new window();}
}
//..另外一个工厂对应行为

策略模式

策略模式

上下文负责生成具体的策略类并且负责与客户端交互

抽象策略类为抽象角色,通常由一个接口或者抽象类实现,给出所有的具体策略类需要的接口

具体策略类:是实现接口,提供具体算法或者行为

策略模式优点:

  • 算法解耦:将行为或算法封装在独立策略类中,便于切换和扩展。
  • 避免多重判断:通过多态替代 if-elseswitch,结构更清晰。
  • 符合开闭原则:新增策略时无需改动已有代码,只需增加新策略类。
  • 可复用性高:不同上下文可复用同一个策略类,提升代码复用率
package com;public class AbstractFactory {public static void main(String[] args){ShowFactory factory=new WinFactory();Computee myCom=factory.createCom();Os myOs=factory.createOs();}
}interface Computee{public void use();
}
interface Os{public void call();
}class hp implements  Computee{@Overridepublic void use() {System.out.println("useing window");}
}
class AppleCom implements  Computee{@Overridepublic void use() {System.out.println("using apple");}
}class window implements Os{@Overridepublic void call() {System.out.println("calling window");}
}
class AppleOS implements Os{@Overridepublic void call() {System.out.println("calling apple");}
}
interface ShowFactory{Computee createCom();Os createOs();
}
class WinFactory implements ShowFactory{@Overridepublic Computee createCom() {return new hp();}@Overridepublic Os createOs() {return new window();}
}
//..另外一个工厂对应行为

代理模式

代理模式(Proxy Pattern)是结构型设计模式的一种,
定义如下:

为其他对象提供一种代理以控制对这个对象的访问。

这里使用jdk代理实现代理模式

优点:

  • 增强功能:在不修改原始对象的情况下增加额外逻辑(如权限校验、日志、事务等)。
  • 解耦结构:将业务逻辑与通用功能分离,代码更清晰、职责更单一。
  • 灵活控制:可以在调用前后做一些处理,比如安全控制、延迟加载、访问控制等。
  • 支持动态扩展:通过 JDK 动态代理可根据接口生成代理对象,运行时更灵活。
package com;import java.awt.*;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;public class ProxyModel {
}interface UserDao{public void add();public void delete();
}
class UserDaoImpl implements UserDao{@Overridepublic void add() {System.out.println("adding");}@Overridepublic void delete() {System.out.println("deleteling");}
}
class UserProxy implements InvocationHandler{Object object;public UserProxy(Object obb){object=obb;}public Object getProxy(){return Proxy.newProxyInstance(object.getClass().getClassLoader(),object.getClass().getInterfaces(),this);}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("代理加强前");Object invoke = method.invoke(object, args);System.out.println("代理加强后");return invoke;}
}

单例模式

定义:

单例模式就是一个类只有一个实例,并且还提供这个实例的全局访问点(避免一个全局使用的类频繁创建和销毁,耗费系统资源)

设计要素

  • 一个私有的构造函数(确保只能由单例类自己创建实例)
  • 一个私有的静态变量(确保只有一个实例)
  • 一个公有的静态函数(给调用者提供调用方法)

单例类的构造方法不让其他人修改和使用;并且单例类自己只创建一个实例,这个实例,其他人也无法修改和直接使用;然后单例类提供一个调用方法,想用这个实例,只能调用。这样就确保了全局只创建了一次实例。

六种实现方式

懒汉式(线程不安全)

先不创建实例,当第一次被调用的时候再创建实例,延迟了实例化,不需要使用该类就不会实例化,节省了系统资源

线程不安全,如果多个线程同时进入了lazyd==null,此时如果还没有实例化,多个线程就会进行实例化,导致实例化了多个实例

package com;public class LazyD {private static LazyD lazyd;private LazyD(){}public static LazyD getUniqueInstance(){if(lazyd==null){lazyd=new LazyD();}return lazyd;}
}

饿汉式不管使用还是不使用这个实例,直接实例化好实例即可,然后如果需要使用的时候,直接调用方法即可

优点:提前实例化了,避免了线程不安全的问题

缺点:直接实例花了这个实例,不会再延迟实例化,如果系统没有使用这个实例,就会导致操作系统的资源浪费

package com;public class HungryD {private static HungryD uniqueInstance=new HungryD();private HungryD(){};public static HungryD getUniqueInstance(){return uniqueInstance;}
}

懒汉式(线程安全)

和基本的懒汉式的区别就是在get方法上 加了一把 锁。如此一来,多个线程访问,每次只有拿到锁的的线程能够进入该方法,避免了多线程不安全问题的出现。

package com;public class Singletion {private static Singletion uniqueInstance;private Singletion(){};public static synchronized Singletion getUniqueInstance(){if(uniqueInstance==null){uniqueInstance=new Singletion();}return uniqueInstance;}
}

双重校验锁实现(线程安全)

双重检查锁定(Double-Check Locking)是一种对线程安全的懒汉式单例模式的优化。传统的线程安全懒汉式存在性能问题——即使单例已经创建,每次调用仍然需要获取锁,导致性能下降。

而双重检查锁定通过在加锁前先判断实例是否已存在,避免了不必要的锁开销:

  • 如果实例已创建,直接返回实例,不进入加锁代码块,提升了效率。
  • 如果实例未创建,多个线程同时进入时,由于加锁机制,只有一个线程能够进入锁内创建实例,保证线程安全。

因此,只有在首次实例化时会发生线程阻塞,之后的调用都不会再产生锁竞争,从而实现了高效且安全的延迟初始化。

核心就是对比懒汉式的线程安全版本有性能提升

还有就是使用volatile关键字修饰uniqueInstance实例变量的原因如下

执行 uniqueInstance = new Singleton(); 时,实际上分为三步:

  1. 为 uniqueInstance 分配内存
  2. 初始化 uniqueInstance
  3. 将 uniqueInstance 指向分配的内存地址

虽然正常顺序是 1 → 2 → 3,但 JVM 可能因指令重排导致执行顺序变为 1 → 3 → 2。

在单线程环境中这不会有问题,但在多线程环境下可能导致安全隐患:例如线程 A 执行了步骤 1 和 3,还未完成初始化(步骤 2),线程 B 看到 uniqueInstance 非空后直接使用它,结果是使用了未初始化的实例。

为避免这种情况,使用 volatile 关键字修饰 uniqueInstance,可以禁止指令重排,确保多线程环境下实例的正确初始化和可见性,保证线程安全。

package com;public class Singletion {private volatile static Singletion uniqueInstance;private Singletion(){};public static Singletion getUniqueInstance(){if(uniqueInstance==null){synchronized (Singletion.class){if(uniqueInstance==null){uniqueInstance=new Singletion();}}}return uniqueInstance;}
}

静态内部类实现(线程安全)

  1. 延迟加载机制
    • 静态内部类 SingletonHolder 不会在类加载时初始化,只有在首次调用 getUniqueInstance() 方法并访问 SingletonHolder.INSTANCE 时才会被加载。
    • 此时 JVM 会保证 INSTANCE 的初始化过程是线程安全的,并且 仅执行一次。
  1. 线程安全保证
    • 由于类加载机制的特性,JVM 会通过 类初始化锁(Class Initialization Lock) 确保 INSTANCE 的唯一性,无需额外同步代码。
  1. 优势总结
    • 懒加载:实例仅在需要时创建,节省资源。
    • 线程安全:依赖 JVM 的类加载机制,无需双重检查锁(DCL)或 synchronized
    • 高性能:无锁竞争,访问效率高
package com;public class Singletion {private Singletion(){};private static class SingletionHolder{private static final Singletion INSTANCE=new Singletion()l}public static Singletion getUniqueInstance(){return SingletionHolder.INSTANCE;}
}

枚举类实现(线程安全)

枚举类的创建就是线程安全的,任何情况下都是单例的

枚举实现单例时,其实例的创建由 JVM 保证线程安全,且天然是单例。

优点

  • 写法简洁
  • 天然线程安全
  • 自动防止反射和反序列化攻击

关于反序列化问题

  • 序列化:将 Java 对象转换为字节序列
  • 反序列化:根据字节序列重建 Java 对象

常规单例模式在反序列化时可能会创建新的实例,破坏单例性。
为了避免这一问题,通常需要重写 readResolve() 方法来确保反序列化返回同一个实例:

package com;public enum Singletion {INSTANCE;// 添加业务逻辑方法public void using() {// 实际功能逻辑写在这里}
}

相关文章:

  • sudo apt update是什么意思呢?
  • STM32F10xx 参考手册
  • 从零开始理解Jetty:轻量级Java服务器的入门指南
  • JavaScript入门【2】语法基础
  • MATLAB学习笔记(六):MATLAB数学建模
  • Redis Sentinel如何实现高可用?
  • 机器学习——逻辑回归
  • C/C++之内存管理
  • 网络编程中的直接内存与零拷贝
  • 强化学习赋能医疗大模型:构建闭环检索-反馈-优化系统提升推理能力
  • chrome 浏览器插件 myTools, 日常小工具。
  • 【C++】string的使用【上】
  • spring -MVC-02
  • 相机Camera日志分析之十一:高通相机Camx hal预览1帧logcat日志process_capture_result详解
  • (C语言)超市管理系统 (正式版)(指针)(数据结构)(清屏操作)(文件读写)(网页版预告)(html)(js)(json)
  • Node.js 源码概览
  • 使用 Python 连接 Oracle 23ai 数据库完整指南
  • 黑马点评-用户登录
  • Java 类和对象
  • 模型量化AWQ和GPTQ哪种效果好?
  • 当“诈骗诱饵”盯上短剧
  • 英国6月初将公布对华关系的审计报告,外交部:望英方树立正确政策导向
  • 现场丨在胡适施蛰存等手札与文献间,再读百年光华
  • 小耳朵等来了春天:公益义诊筛查专家走进安徽安庆
  • 万科再获深铁集团借款,今年已累计获股东借款近120亿元
  • MSCI中国指数5月调整:新增5只A股、1只港股