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

工厂模式

一、工厂模式的总体好处

  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() {// 实际功能逻辑写在这里}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/81496.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

阿里通义万相 Wan2.1-VACE:开启视频创作新境界

2025 年 5 月 14 日,阿里巴巴为视频创作领域带来了重磅惊喜 —— 开源通义万相 Wan2.1-VACE。这一模型堪称视频生成与编辑领域的集大成者,凭借其全面且强大的功能,为广大创作者、开发者以及企业用户开辟了全新的视频创作天地。它打破了以往视…

自定义类、元组、字典和结构体对比——AutoCAD C# 开发中建立不同对象之间的联系

以下是对它们的详细分析和对比: 1. 自定义类(Class) 优势 封装性强:可以定义字段、属性、方法和事件,实现复杂的行为和逻辑。继承与多态:支持继承体系,可通过接口或抽象类实现多态。引用类型…

MVC架构模式

mvc架构是一种常见的开发模式,以下是三个核心部分 Model(模型):负责应用程序的数据和业务逻辑。它与数据库交互,处理数据的存储、检索和更新,是应用程序的核心业务所在。View(视图)&#xff1a…

Python实例题:Python百行制作登陆系统

目录 Python实例题 题目 python-login-systemPython 百行登录系统脚本 代码解释 用户数据库: 注册功能: 登录功能: 主程序: 运行思路 注意事项 Python实例题 题目 Python百行制作登陆系统 python-login-systemPython…

uniapp使用全局组件,

在 Uniapp 中,如果你的组件是应用层组件(例如全局悬浮按钮、全局通知栏等),并且希望它自动出现在所有页面而无需在每个页面模板中手动添加组件标签,可以通过以下两种方案实现: 方案一:通过 app.…

(8)python开发经验

文章目录 1 下载python2 pip安装依赖无法访问3 系统支持4 下载python文档5 设置虚拟环境6 编译安装python 更多精彩内容👉内容导航 👈👉Qt开发 👈👉python开发 👈 1 下载python 下载地址尽量不要下载最新版…

【原创】基于视觉大模型gemma-3-4b实现短视频自动识别内容并生成解说文案

📦 一、整体功能定位 这是一个用于从原始视频自动生成短视频解说内容的自动化工具,包含: 视频抽帧(可基于画面变化提取关键帧) 多模态图像识别(每帧图片理解) 文案生成(大模型生成…

每日算法刷题计划Day5 5.13:leetcode数组3道题,用时1h

11. 26. 删除有序数组中的重复项(简单,双指针) 26. 删除有序数组中的重复项 - 力扣(LeetCode) 思想: 1.我的思想: 双指针遍历集合储存已有元素 2.官方思想: 题目条件有序数组删除重复元素,所以重复元素都是连续存在…

Transformer 架构在目标检测中的应用:YOLO 系列模型解析

目录 Transformer 架构在目标检测中的应用:YOLO 系列模型解析 一、YOLO 模型概述 二、YOLO 模型的核心架构 (一)主干网络 (二)颈部结构 (三)头部结构 三、YOLO 模型的工作原理 &#xf…

一个完整的项目示例:taro开发微信小程序

前一周完成了一个项目,体测成绩转换的工具,没做记录,。这次计划开发一个地图应用小程序,记录一下。方便给使用的人。 一、申请微信小程序,填写相应的信息,取得开发者ID。这个要给腾讯地图使用的。 二、申…

动态规划-LCR 166.珠宝的最大价值-力扣(LeetCode)

一、题目解析 frame二维矩阵中每个值代表珠宝的价值,现在从左上角开始拿珠宝,只能向右或向下拿珠宝,到达右下角时停止拿珠宝,要求拿的珠宝价值最大。 二、算法解析 1.状态表示 我们想要知道的是到达[i,j]为位置时的最大价值&am…

安装nerdctl和buildkitd脚本命令

#!/bin/bash set -euo pipefail # 检查是否以root权限运行 if [ "$(id -u)" -ne 0 ]; then echo "错误:请使用root权限或sudo运行本脚本" >&2 exit 1 fi # 检测openEuler系统(兼容大小写) detect_distrib…

实现视频分片上传 OSS

访问 OSS 有两种方式,本文用到的是使用临时访问凭证上传到 OSS,不同语言版本的代码参考: 使用STS临时访问凭证访问OSS_对象存储(OSS)-阿里云帮助中心 1.安装并使用 首先我们要安装 OSS: npm install ali-oss --save 接着我们…

动态规划(3)学习方法论:构建思维模型

引言 动态规划是算法领域中一个强大而优雅的解题方法,但对于许多学习者来说,它也是最难以掌握的算法范式之一。与贪心算法或分治法等直观的算法相比,动态规划往往需要更抽象的思维和更系统的学习方法。在前两篇文章中,我们介绍了动态规划的基础概念、原理以及问题建模与状…

elementplus el-tree 二次封装支持配置删除后展示展开或折叠编辑复选框懒加载功能

本文介绍了基于 ElementPlus 的 el-tree 组件进行二次封装的 TreeView 组件,使用 Vue3 和 JavaScript 实现。TreeView 组件通过 props 接收树形数据、配置项等,支持懒加载、节点展开/收起、节点点击、删除、编辑等操作。组件内部通过 ref 管理树实例&…

2025年渗透测试面试题总结-安恒[实习]安全工程师(题目+回答)

网络安全领域各种资源,学习文档,以及工具分享、前沿信息分享、POC、EXP分享。不定期分享各种好玩的项目及好用的工具,欢迎关注。 目录 安恒[实习]安全工程师 一面 1. 自我介绍 2. 前两段实习做了些什么 3. 中等难度的算法题 4. Java的C…

网络编程中的直接内存与零拷贝

本篇文章会介绍 JDK 与 Linux 网络编程中的直接内存与零拷贝的相关知识,最后还会介绍一下 Linux 系统与 JDK 对网络通信的实现。 1、直接内存 所有的网络通信和应用程序中(任何语言),每个 TCP Socket 的内核中都有一个发送缓冲区…

TransmittableThreadLocal使用场景

🚀 为什么要用 TransmittableThreadLocal?一文读懂线程上下文传递问题 在 Java Web 开发中,我们经常用 ThreadLocal 来保存每个请求的用户信息,例如 userId。但当我们使用线程池或异步方法(如 Async)时&am…

Milvus(24):全文搜索、文本匹配

1 全文搜索 全文搜索是一种在文本数据集中检索包含特定术语或短语的文档,然后根据相关性对结果进行排序的功能。该功能克服了语义搜索的局限性(语义搜索可能会忽略精确的术语),确保您获得最准确且与上下文最相关的结果。此外&…

2000 元以下罕见的真三色光源投影仪:雷克赛恩Cyber Pro1重新定义入门级投影体验

当性价比遇上技术瓶颈 在 2000元以下的1080P投影仪,单LCD 技术长期主导。而三色光源的DLP和3LCD真1080P都在4000元以上。 单LCD投影为纯白光光源,依赖CF滤光膜导致光效低下,普遍存在" 色彩失真 " 等问题。数据显示,该价…