Java23种设计模式理解----创建型模式

  • 工厂模式

        我要实现一个发送邮件的功能,一开始我想着是准备用QQ邮箱去发送,于是我在项目消息通知模块定义了一个类

package com.hwq.message;/*** @author huangwq* @date 2024/4/12* @description 描述*/
public class QQMailService {public boolean sendMail(String content){System.out.println("通过QQ邮箱发送邮件");System.out.println("内容:"+content);return true;}
}
package com.hwq.order;import com.hwq.message.QQMailService;/*** @author huangwq* @date 2024/4/12* @description 描述*/
public class OrderService {public void createOrder(){System.out.println("开始创建订单");System.out.println("订单创建成功");// 发邮件通知用户QQMailService mailService = new QQMailService();mailService.sendMail("您的订单XXX,已在仓库下单成功");}
}
package com.hwq.user;import com.hwq.message.QQMailService;/*** @author huangwq* @date 2024/4/12* @description 描述*/
public class UserService {public void registerUser(){System.out.println("开始注册用户");System.out.println("用户注册成功");QQMailService mailService = new QQMailService();mailService.sendMail("您好,XXX 恭喜您成功尊贵的VIP");}
}

        当然还可能有其他模块需要发邮件,这里只列举了两个,看起来这样用好像没啥问题,但是有一天QQ邮箱不让用了,要求换成网易邮箱。OK那这个时候,我们去新增一个WYMailService,然后把所有new QQMailService的地方,改成new WYMaillService。好我们作为一个受气包,那我去改。然后过了几天领导有说要改成雅虎邮箱。那这个时候不得改废了,这个时候我们就想到了先辈们的经验工厂模式,那我们就可以改成这样

package com.hwq.message;/*** @author huangwq* @date 2024/4/12* @description 描述*/
public interface MailFactory {boolean sendMail(String content);static MailFactory getFactory(){return mailService;}static MailFactory mailService = new WYMailService();
}
package com.hwq.order;import com.hwq.message.MailFactory;/*** @author huangwq* @date 2024/4/12* @description 描述*/
public class OrderService {public void createOrder(){System.out.println("开始创建订单");System.out.println("订单创建成功");// 发邮件通知用户//QQMailService mailService = new QQMailService();MailFactory mailService = MailFactory.getFactory();mailService.sendMail("您的订单XXX,已在仓库下单成功");}
}
package com.hwq.user;import com.hwq.message.MailFactory;/*** @author huangwq* @date 2024/4/12* @description 描述*/
public class UserService {public void registerUser(){System.out.println("开始注册用户");System.out.println("用户注册成功");//QQMailService mailService = new QQMailService();MailFactory mailService = MailFactory.getFactory();mailService.sendMail("您好,XXX 恭喜您成功尊贵的VIP");}
}

        终于好了一点,后面再变动邮箱我就只用改动MailFactory里面的getFactory方法,不用去改其他模块的代码,是不是要舒服点。

        那这么做除了能在一定程度上符合闭合原则(尽量不修改已有代码,而是新增扩展代码满足需求),其实还有一个功能,那就是new XXMailService是我可以控制的,我可以在一个地方给初始化一些参数进去,而其他用到的地方是不用关心的,这个地方可以参考Integer.valueOf的方法实现

    public static Integer valueOf(int i) {if (i >= IntegerCache.low && i <= IntegerCache.high)return IntegerCache.cache[i + (-IntegerCache.low)];return new Integer(i);}

        它可以帮你从缓存中取,缓存中取不到再new 一个,而这些使用的人是不需要关心的。

  •  抽象工厂模式

        先说一下抽象工厂模式和工厂模式有啥区别吧,工厂模式是提供了一个创建一个对象的接口,具体这个产品是由什么实现的这个是子类决定的,抽象工厂是为了创建一系列相关的对象的,抽象工厂没有具体的实现,他的产品都是抽象,挺绕的。简单点理解就是抽象工厂里面的方法也是抽象的,我需要有实际的工厂去实现、重写我们抽象工厂里面的抽象方法。

        上代码吧,我现在要实现一个云存储,市面上有两家供应商一家上传下载快,但是数据安全性稍低,另一家上传下载一般,但是数据安全性很高,我可能要根据用户设置的偏好来决定,他是需要把数据上传到第一家还是第二家

package com.hwq.afactory;/*** @author huangwq* @date 2024/4/12* @description 描述*/
public interface AbstractFactory {ImageService createImageService();VideoService createVideoService();static AbstractFactory getFactory(String name){if("fast".equals(name)){return new FastFactory();} else if("safety".equals(name)){return new SafetyFactory();} else {throw new IllegalArgumentException("未知的类型");}}
}---------------------------------------------------------package com.hwq.afactory;/*** @author huangwq* @date 2024/4/12* @description 描述*/
public interface ImageService {void createImage();
}---------------------------------------------------------package com.hwq.afactory;/*** @author huangwq* @date 2024/4/12* @description 描述*/
public interface VideoService {void createVideo();
}---------------------------------------------------------package com.hwq.afactory;/*** @author huangwq* @date 2024/4/12* @description 描述*/
public class FastImagetServiceImpl implements ImageService {@Overridepublic void createImage() {System.out.println("速度快的上传图片方法");}
}---------------------------------------------------------package com.hwq.afactory;/*** @author huangwq* @date 2024/4/12* @description 描述*/
public class FastVideoServiceImpl implements VideoService {@Overridepublic void createVideo() {System.out.println("速度快的上传视频的方法");}
}---------------------------------------------------------package com.hwq.afactory;/*** @author huangwq* @date 2024/4/12* @description 描述*/
public class FastFactory implements AbstractFactory {@Overridepublic ImageService createImageService() {return new FastImagetServiceImpl();}@Overridepublic VideoService createVideoService() {return new FastVideoServiceImpl();}
}---------------------------------------------------------package com.hwq.afactory;/*** @author huangwq* @date 2024/4/12* @description 描述*/
public class SafetyImageService implements ImageService {@Overridepublic void createImage() {System.out.println("安全性高的上传图片的方法");}
}---------------------------------------------------------package com.hwq.afactory;/*** @author huangwq* @date 2024/4/12* @description 描述*/
public class SafetyVideoService implements VideoService {@Overridepublic void createVideo() {System.out.println("安全性高的上传视频方法");}
}---------------------------------------------------------package com.hwq.afactory;/*** @author huangwq* @date 2024/4/12* @description 描述*/
public class SafetyFactory implements AbstractFactory {@Overridepublic ImageService createImageService() {return new SafetyImageService();}@Overridepublic VideoService createVideoService() {return new SafetyVideoService();}
}

        抽象工厂是为了让创建工厂和一组产品与使用分离,并可以随时切换到另一个实际工厂和另一组实际产品,抽象工厂只定义我什么样的一个工厂,我工厂需要什么样的产品,具体如何实现工厂你可以用FastFactory也可以用SafetyFactory去实现,那产品具体怎么实现你可以用FastImageServiceImpl也可以用SafetyImageServiceImpl,其他模块在用的时候我只用使用抽象工厂和使用抽象工厂里面的抽象产品即可。

  • 生成器模式

         生成器模式是为了解决在创建一个对象的,要装上很多别的零部件以实现一些扩展的功能或者多带一些参数,下面实现一个简单加密的方法。

package com.hwq.builder;import java.nio.charset.StandardCharsets;
import java.util.Base64;/*** @author huangwq* @date 2024/4/12* @description 描述*/
public class Signature {public static class Builder{private String method;private String queryString;private String body;public Builder addMethod(String m){method = m;return this;}public Builder addQueryString(String q){queryString = q;return this;}public Builder addBody(String b){body = b;return this;}public String build(){String baseStr = new String(Base64.getEncoder().encode((this.method + this.queryString + this.body).getBytes(StandardCharsets.UTF_8)));return baseStr;}}}package com.hwq.builder;/*** @author huangwq* @date 2024/4/12* @description 描述*/
public class Main {public static void main(String[] args) {String str = new Signature.Builder().addMethod("GET").addBody("").addQueryString("state=1").build();}
}

        这种链式调用大家肯定很熟悉StringBuilder.append不就是这样的吗,其实那些给对象set属性的方式也是生成器的一种,只不过如果set的方法里面返回this,就可以链式调用。

  • 原型模式

         个人感觉用得比较少,从字面意思来理解就是以某个对象为原型,创建一个新的对象,其实就是实现一个clone的方法,然后做一次深拷贝。

package com.hwq.prototype;/*** @author huangwq* @date 2024/4/12* @description 描述*/
public class User {private String name;private int age;public User(String name,int age){this.name = name;this.age = age;}public User clone(){User user = new User(this.name,this.age);return user;}
}

        顺便看一下Arrays.copyOf的源码

    public static int[] copyOf(int[] original, int newLength) {int[] copy = new int[newLength];System.arraycopy(original, 0, copy, 0,Math.min(original.length, newLength));return copy;}
  •  单例

         单例模式还是用的比较多,使用单例模式我们要处理单例的类里面不要记录一些状态数据,比如我想记录类里面某个方法的调用次数,我在类中定义了int counter = 0,然后方法末尾执行counter++。单线程下这个是没问题的,多线程的时候这个counter的值可能就不准了,因为每个线程调用这个方法的时候都会复制一次这个变量的值,两个线程同时调用的时候他们可能都复制到了counter=2,然后线程1做了counter++,在把值3设置回去,线程2也用读到的counter=2做了一次counter++,也把值3设置回去了,我们会认为这个方法只调用了3次,实际确调用了4次。

        实现单例的方式有几种,我就列举四种吧,饿汉式、懒汉式、静态代码块、枚举

package com.hwq.singleton;/*** @author huangwq* @date 2024/4/12* @description 描述*/
public class SingleService {// 饿汉式private SingleService(){}private static SingleService singleService = new SingleService();public static SingleService getInstance(){return singleService;}
}

饿汉式直接在类中定义并初始化了一个静态变量,那么这个变量会随类加载而初始化,并且JVM为我们保证了类加载的过程是单线程的。但是这个还会有一个问题,我们还是可以通过反射去调用这个private的构造函数,像序列化反序列化的时候代码如下:

    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {Constructor<SingleService> declaredConstructor = SingleService.class.getDeclaredConstructor();declaredConstructor.setAccessible(true);SingleService ss1 = declaredConstructor.newInstance();SingleService ss2 = SingleService.getInstance();SingleService ss3 = SingleService.getInstance();System.out.println(System.identityHashCode(ss1)); // 1975012498System.out.println(System.identityHashCode(ss2)); // 1808253012System.out.println(System.identityHashCode(ss3)); // 1808253012}

ss1 就是我们没有通过单例模式创建出来的,ss2\ss3是通过单例模式创建出来的。

public class SingleService {// 懒汉式初级版private SingleService(){}private static SingleService singleService = null;public static SingleService getInstance(){if(singleService == null){singleService = new SingleService();}return singleService;}
}public class SingleService {// 懒汉式加锁版private SingleService(){}private static SingleService singleService = null;public static synchronized SingleService getInstance(){if(singleService == null){singleService = new SingleService();}return singleService;}
}public class SingleService {// 懒汉式加锁小粒度版(也叫双重判断)private SingleService(){}private static SingleService singleService = null;public static SingleService getInstance(){if(singleService == null){synchronized (SingleService.class) {if(singleService == null) {singleService = new SingleService();}}}return singleService;}
}

懒汉式这三中方式的好处是只有在这个类被用到的时候才去创建对象,但是会有线程安全问题,所有后面两种方式加上了锁,但是加了又会造成性能问题,所以我们一般不用这种方式,当然这种方式依然存在反射调用私有构造函数的问题。

public class SingleService {// 静态代码块private SingleService(){}private static SingleService singleService = null;static {singleService = new SingleService();}public static SingleService getInstance(){return singleService;}
}

静态代码块其实就和饿汉式差不多,都是用类加载的机制保证线程安全,静态代码块依然存在反射调用私有构造函数的问题。

public enum ESingleService {INSTANCE;private String name = "test";public String getName(){return name;}
}public static void main(String[] args) {ESingleService.INSTANCE.getName();}

 枚举是唯一可以避免私有构造反射调用的方式,所以一般推荐大家用枚举。

总结一下其实我们日常开发过程中已经很少去自己实现单例了,更多的是用Spring框架创建一个Singleton的bean,而且Spring默认bean为Singleton模式。

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

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

相关文章

UE5+GIS技术应用场景介绍

随着科技的不断发展,虚幻引擎(Unreal Engine)和地理信息系统(Geographic Information System,GIS)技术在众多领域得到了广泛的应用。虚幻引擎5(UE5)作为一款功能强大的实时3D创作工具,与GIS技术的结合,为各行各业带来了前所未有的创新。 一、UE5与GIS的结合方式主要…

基于微信公众号,搭建一套简单的电商支付环境(下)-- 微信公众号的对接

一、接着上文 上文把部署情况介绍了&#xff0c;侧重于网络及代理&#xff0c;本文选择把微信公众号的对接实现介绍一下。 还是那句话&#xff0c;微信官方的文档已非常详细&#xff0c;这里先摘抄一些重要的概念。 其次&#xff0c;待对接微信公众号的接口众多&#xff0c;…

Qt | 视频播放器(multimedia、multimediawidgets)

QT +=multimedia 通俗解释: 此代码行告诉编译器在构建应用程序时包含多媒体库。这意味着您的应用程序将能够播放和显示音频和视频文件。 使用分步说明构建模型: 创建一个新的 Qt 项目。 在 .pro 文件中添加以下行: QT += multimedia 导入必要的多媒体头文件: #include &l…

普乐蛙VR航天体验馆设备VR太空飞船VR元宇宙展厅

三天小长假就要来啦&#xff01;五一假期也即将到来。老板们想捉住人流量这个财富密码吗&#xff1f;那快快行动起来&#xff01;开启VR体验项目&#xff0c;假期赚翻天&#xff01;小编亲测&#xff01;&#xff01;这款设备刺激好玩&#xff0c;想必会吸引各位家长小孩、学生…

java使用ShutdownHook优雅地停止服务

在Java程序中可以通过添加关闭钩子&#xff0c;实现在程序退出时关闭资源、平滑退出的功能。 使用Runtime.addShutdownHook(Thread hook)方法&#xff0c;可以注册一个JVM关闭的钩子&#xff0c;这个钩子可 这通常用于确保在应用程序退出时能够执行一些清理工作&#xff0c;比…

openssl3.2 - exp - zlib

文章目录 openssl3.2 - exp - zlib概述笔记命令行实现程序实现备注 - 压缩时无法base64压缩时无法带口令压缩实现 - 对buffer进行压缩和解压缩测试效果工程实现main.cppCOsslZlibBuffer.hCOsslZlibBuffer.cpp总结备注 - 解压可以替代完整性校验备注 - 多次压缩没作用备注 - 和7…

Lambda表达式保姆级教程详解(二)

本系列文章简介&#xff1a; Lambda表达式在现代编程语言中越来越流行&#xff0c;如Java、Python等。它是一种简洁、灵活的编码方式&#xff0c;能够使代码更易读、更易编写&#xff0c;同时提高代码的可维护性和扩展性。 Lambda表达式的引入使得函数式编程思想在传统的面向对…

海外媒体发稿:探究7个旅游业媒体套餐背后的秘密-华媒舍

旅游业媒体套餐对于旅游行业来说扮演着重要的角色&#xff0c;帮助企业在竞争激烈的市场中宣传推广&#xff0c;吸引更多的游客。在这篇文章中&#xff0c;我们将深入探究7个旅游业媒体套餐背后的秘密&#xff0c;为您揭示其真正的价值和影响。 1. 平台选择的关键 在选择旅游业…

博士找教职--经验贴

目录 一、如何寻找教职&#xff1f; 二、海投还是多投&#xff1f; 三、博士什么时候找工作最好&#xff1f; 为什么9月份开始最好&#xff1f; 四、论文没有出来可以9月找工作吗&#xff1f; 五、其他的一些嘱咐 一、如何寻找教职&#xff1f; 上必应键入关键词&#xf…

leetcode最长有效括号

. - 力扣&#xff08;LeetCode&#xff09; 32. 最长有效括号 困难 相关标签 相关企业 给你一个只包含 ( 和 ) 的字符串&#xff0c;找出最长有效&#xff08;格式正确且连续&#xff09;括号 子串 的长度。 示例 1&#xff1a; 输入&#xff1a;s "(()" 输…

spring boot学习第十七篇:OAuth2概述及使用GitHub登录第三方网站

0. 导言 我们在浏览器上可以访问成百上千个网站&#xff0c;使用每个网站的服务一般都要先注册账号&#xff0c;那么我们为了更好地记忆&#xff0c;一般都会在多个网站使用相同的账号和密码进行注册。那么问题就来了&#xff0c;如果在你注册的网站中有某些个网站的系统设计不…

CentOS7里ifcfg-eth0文件不存在解决方案/Centos7修改网络IP解决方案

Centos7网络IP地址手动设置 1、centos7没有ifcfg-eth0&#xff0c;我的centos7也没有其他博客说的什么ifcfg-ens33、ifcfg-ens32&#xff0c;然后我打开了我这里的ifcfg-eno***&#xff0c;结果发现就是centos6里的ifcfg-eth0里的网络配置。2、vim ifcfg-eno***&#xff08;按t…

202203青少年软件编程(Scratch图形化) 等级考试试卷(四级)

第1题&#xff1a;【 单选题】 由1,2,3,4,5,0这六个数字经过排列组合能够组成多少个六位数偶数&#xff1f; 注意&#xff1a; 每一位都不相同&#xff0c; 最高位不能为0。&#xff08; &#xff09; A:720 B:360 C:312 D:88 【正确答案】: C 【试题解析】 : 逻辑知识…

10分钟带你学会配置DNS服务正反向解析

正向解析 服务端IP客户端IP网址192.168.160.134192.168.160.135www.openlab.com 一、首先做准备工作&#xff1a; 关闭安全软件&#xff0c;关闭防火墙&#xff0c;下载bind软件 [rootserver ~]# setenforce 0 [rootserver ~]# systemctl stop firewalld [rootserver ~]# y…

【C++程序员的自我修炼】拷贝构造函数

心存希冀 追光而遇目有繁星 沐光而行 目录 拷贝构造函数概念 拷贝构造的特征 无穷递归的解释 浅拷贝 总结&#xff1a; 深拷贝 拷贝构造函数典型调用场景 总结 契子✨ 在生活中总有很多琐事&#xff0c;不做不行做了又怕麻烦&#xff0c;有时候想要是有个和自己一模一样的人就…

springboot中常见的设计模式

Spring Boot&#xff0c;作为基于Spring框架的扩展&#xff0c;广泛应用了多种设计模式来简化企业级应用开发。以下是在Spring Boot中常见的一些设计模式&#xff1a; 1. 单例模式&#xff08;Singleton Pattern&#xff09; 这是Spring框架中最常用的模式之一。Spring容器默…

每日一题 第八十九期 洛谷 [NOIP2017 提高组] 奶酪

[NOIP2017 提高组] 奶酪 题目背景 NOIP2017 提高组 D2T1 题目描述 现有一块大奶酪&#xff0c;它的高度为 h h h&#xff0c;它的长度和宽度我们可以认为是无限大的&#xff0c;奶酪中间有许多半径相同的球形空洞。我们可以在这块奶酪中建立空间坐标系&#xff0c;在坐标系…

Go——面向对象

一. 匿名字段 go支持只提供类型而不写字段名的方式&#xff0c;也就是匿名字段&#xff0c;也称为嵌入字段。 同名字段的情况 所以自定义类型和内置类型都可以作为匿名字段使用 指针类型匿名字段 二.接口 接口定义了一个对象的行为规范&#xff0c;但是定义规范不实现&#xff…

搞懂docker一篇就够了

在数字化浪潮汹涌的今日,Docker以其独特的魅力,引领着容器技术的风潮,成为众多开发者、运维人员乃至架构师们心中的宠儿。今日就带领大家一同探寻Docker的奥秘,感受其带来的便捷与高效。 Docker,这个看似简单的词汇,却蕴含着无尽的智慧与力量。它就像一位高超的魔术师,…

FreeSWITCH 1.10.10 简单图形化界面17 - ubuntu22.04或者debian12 安装FreeSWITCH

FreeSWITCH 1.10.10 简单图形化界面17 - ubuntu22.04或者debian12 安装FreeSWITCH 界面预览00、先看使用手册0、安装操作系统1、下载脚本2、开始安装3、登录网页FreeSWITCH界面安装参考:https://blog.csdn.net/jia198810/article/details/132479324 界面预览 http://myfs.f3…