Java设计模式---(创建型模式)工厂、单例、建造者、原型

目录

  • 前言
  • 一、工厂模式(Factory)
    • 1.1 工厂方法模式(Factory Method)
      • 1.1.1 普通工厂方法模式
      • 1.1.2 多个工厂方法模式
      • 1.1.3 静态工厂方法模式
    • 1.2 抽象工厂模式(Abstract Factory)
  • 二、单例模式(Singleton)
  • 三、建造者模式(Builder)
  • 四、原型模式(Prototype)


前言

创建型模式(4种):用于描述“怎样创建对象”,它的主要特点是“将对象的创建与使用分离”。

一、工厂模式(Factory)

1.1 工厂方法模式(Factory Method)

1.1.1 普通工厂方法模式

普通工厂方法模式,就是建立一个工厂类,对实现了同一接口的一些类进行实例的创建。首先看下关系图:
在这里插入图片描述
举一个发送邮件和短信的例子,首先,创建二者的共同接口:

public interface Sender {public void Send();
}

其次,创建实现类:

public class MailSender implements Sender {@Overridepublic void Send() {System.out.println("this is mailsender!");}
}
public class SmsSender implements Sender {@Overridepublic void Send() {System.out.println("this is sms sender!");}
}

最后,建工厂类:

public class SendFactory {public Sender produce(String type) {if ("mail".equals(type)) {return new MailSender();} else if ("sms".equals(type)) {return new SmsSender();} else {System.out.println("请输入正确的类型!");return null;}}
}

测试下:

public class FactoryTest {public static void main(String[] args) {SendFactory factory = new SendFactory();Sender sender = factory.produce("sms");sender.Send();}
}

输出:this is sms sender!

1.1.2 多个工厂方法模式

多个工厂方法模式,是对普通工厂方法模式的改进,在普通工厂方法模式中,如果传递的字符串出错,则不能正确创建对象,而多个工厂方法模式是提供多个工厂方法,分别创建对象。关系图:
在这里插入图片描述
将上面的代码做下修改,改动下SendFactory类就行,如下:

public class SendFactory {public Sender produceMail(){return new MailSender();}public Sender produceSms(){return new SmsSender();}
}

测试类如下:

public class FactoryTest {public static void main(String[] args) {SendFactory factory = new SendFactory();Sender sender = factory.produceMail();sender.Send();}
}

输出:this is mailsender!

1.1.3 静态工厂方法模式

静态工厂方法模式,将上面的多个工厂方法模式里的方法置为静态的,不需要创建实例,直接调用即可。

public class SendFactory {public static Sender produceMail(){return new MailSender();}public static Sender produceSms(){return new SmsSender();}
}
public class FactoryTest {public static void main(String[] args) {	Sender sender = SendFactory.produceMail();sender.Send();}
}

输出:this is mailsender!

工厂模式适合:凡是出现了大量的产品需要创建,并且具有共同的接口时,可以通过工厂方法模式进行创建。
在以上的三种模式中,第一种如果传入的字符串有误,不能正确创建对象;
第三种相对于第二种,不需要实例化工厂类;
所以,大多数情况下,我们会选用第三种——静态工厂方法模式。

1.2 抽象工厂模式(Abstract Factory)

工厂方法模式的问题是类的创建依赖工厂类,也就是说,如果想要拓展程序,必须对工厂类进行修改,这违背了闭包原则,所以,从设计角度考虑,有一定的问题,如何解决?就用到抽象工厂模式,创建多个工厂类,这样一旦需要增加新的功能,直接增加新的工厂类就可以了,不需要修改之前的代码。因为抽象工厂不太好理解,先看看图,然后就和代码,就比较容易理解。

在这里插入图片描述

public interface Sender {public void Send();
}

两个实现类:

public class MailSender implements Sender {@Overridepublic void Send() {System.out.println("this is mailsender!");}
}
public class SmsSender implements Sender {@Overridepublic void Send() {System.out.println("this is sms sender!");}
}

两个工厂类:

public class SendMailFactory implements Provider {@Overridepublic Sender produce(){return new MailSender();}
}
public class SendSmsFactory implements Provider{@Overridepublic Sender produce() {return new SmsSender();}
}

再提供一个接口:

public interface Provider {public Sender produce();
}

测试类:

public class Test {public static void main(String[] args) {Provider provider = new SendMailFactory();Sender sender = provider.produce();sender.Send();}
}

其实这个模式的好处就是,如果你现在想增加一个功能:发及时信息,则只需做一个实现类,实现Sender接口,同时做一个工厂类,实现Provider接口,就OK了,无需去改动现成的代码。这样做,拓展性较好!


二、单例模式(Singleton)

保证被创建一次,节省系统开销

  • ​饿汉式:上来不管有没有对象,都要直接创建一个新的对象。
  • 懒汉式:首先判断有没有创建对象,如果创建对象了就使用原来创建的对象,没有创建的话新创建一个对象。

单例模式重点在于在整个系统上共享一些创建时较耗资源的对象。整个应用中只维护一个特定类实例,它被所有组件共同使用。Java.lang.Runtime是单例模式的经典例子。

public static Singleton getInstance() {if (instance == null) {synchronized (instance) {if (instance == null) {instance = new Singleton();}}}return instance;}

使用内部类来维护单例的实现,JVM内部的机制能够保证当一个类被加载的时候,这个类的加载过程是线程互斥的。这样当我们第一次调用getInstance的时候,JVM能够帮我们保证instance只被创建一次,并且会保证把赋值给instance的内存初始化完毕,一个完美的单例模式

public class Singleton {/* 私有构造方法,防止被实例化 */private Singleton() {}/* 此处使用一个内部类来维护单例 */private static class SingletonFactory {private static Singleton instance = new Singleton();}/* 获取实例 */public static Singleton getInstance() {return SingletonFactory.instance;}/* 如果该对象被用于序列化,可以保证对象在序列化前后保持一致 */public Object readResolve() {return getInstance();}
}

三、建造者模式(Builder)

工厂类模式提供的是创建单个类的模式,而建造者模式则是将各种产品集中起来进行管理,用来创建复合对象,所谓复合对象就是指某个类具有不同的属性,其实建造者模式就是前面抽象工厂模式和最后的Test结合起来得到的。

还和前面一样,一个Sender接口,两个实现类MailSender和SmsSender。最后,建造者类如下:

public class Builder {private List<Sender> list = new ArrayList<Sender>();public void produceMailSender(int count){for(int i=0; i<count; i++){list.add(new MailSender());}}public void produceSmsSender(int count){for(int i=0; i<count; i++){list.add(new SmsSender());}}
}

测试类:

public class Test {public static void main(String[] args) {Builder builder = new Builder();builder.produceMailSender(10);}
}

建造者模式将很多功能集成到一个类里,这个类可以创造出比较复杂的东西。所以与工程模式的区别就是:

  • 工厂模式关注的是创建单个产品,
  • 而建造者模式则关注创建符合对象,多个部分。
    因此,是选择工厂模式还是建造者模式,依实际情况而定。

四、原型模式(Prototype)

原型模式虽然是创建型的模式,但是与工程模式没有关系,该模式的思想就是将一个对象作为原型,对其进行复制、克隆,产生一个和原对象类似的新对象。在Java中,复制对象是通过clone()实现的,先创建一个原型类:

public class Prototype implements Cloneable {public Object clone() throws CloneNotSupportedException {Prototype proto = (Prototype) super.clone();return proto;}
}

一个原型类,只需要实现Cloneable接口,覆写clone方法,此处clone方法可以改成任意的名称,因为Cloneable接口是个空接口,你可以任意定义实现类的方法名,如cloneA或者cloneB,因为此处的重点是super.clone()这句话,super.clone()调用的是Object的clone()方法。

将结合对象的浅复制和深复制来说一下,首先需要了解对象深、浅复制的概念:

  • 浅复制:将一个对象复制后,基本数据类型的变量都会重新创建,而引用类型,指向的还是原对象所指向的。
  • 深复制:将一个对象复制后,不论是基本数据类型还有引用类型,都是重新创建的。简单来说,就是深复制进行了完全彻底的复制,而浅复制不彻底。

写一个深浅复制的例子:

public class Prototype implements Cloneable, Serializable {private static final long serialVersionUID = 1L;private String string;private SerializableObject obj;/* 浅复制 */public Object clone() throws CloneNotSupportedException {Prototype proto = (Prototype) super.clone();return proto;}/* 深复制 */public Object deepClone() throws IOException, ClassNotFoundException {/* 写入当前对象的二进制流 */ByteArrayOutputStream bos = new ByteArrayOutputStream();ObjectOutputStream oos = new ObjectOutputStream(bos);oos.writeObject(this);/* 读出二进制流产生的新对象 */ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());ObjectInputStream ois = new ObjectInputStream(bis);return ois.readObject();}public String getString() {return string;}public void setString(String string) {this.string = string;}public SerializableObject getObj() {return obj;}public void setObj(SerializableObject obj) {this.obj = obj;}}class SerializableObject implements Serializable {private static final long serialVersionUID = 1L;
}

深复制,需要采用流的形式读入当前对象的二进制输入,再写出二进制数据对应的对象。

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

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

相关文章

浅析Kafka-Stream消息流式处理流程及原理

以下结合案例&#xff1a;统计消息中单词出现次数&#xff0c;来测试并说明kafka消息流式处理的执行流程 Maven依赖 <dependencies><dependency><groupId>org.apache.kafka</groupId><artifactId>kafka-streams</artifactId><exclusio…

【密码学】大整数分解问题和离散对数问题

公钥密码体制的主要思想是通过一种非对称性&#xff0c;即正向计算简单&#xff0c;逆向计算复杂的加密算法设计&#xff0c;来解决安全通信。本文介绍两种在密码学领域内最为人所熟知、应用最为广泛的数学难题——大整数分解问题与离散对数问题 一、大整数分解问题 &#xf…

thinkphp 生成邀请推广二维码,保存到服务器并接口返回给前端

根据每个人生成自己的二维码图片,接口返回二维码图片地址 生成在服务器的二维码图片 控制器 public function createUserQRcode(){$uid = input(uid);if

传言称 iPhone 16 Pro 将支持 40W 快速充电和 20W MagSafe

目前&#xff0c;iPhone 15 和 iPhone 15 Pro 机型使用合适的 USB-C 电源适配器可实现高达 27W 的峰值充电速度&#xff0c;而 Apple 和授权第三方的官方 MagSafe 充电器可以高达 15W 的功率为 iPhone 15 机型进行无线充电。所有四款 iPhone 15 机型均可使用 20W 或更高功率的电…

FPGA学习笔记(一) FPGA最小系统

文章目录 前言一、FPGA最小系统总结 前言 今天学习下FPGA的最小系统一、FPGA最小系统 FPGA最小系统与STM32最小系统类似&#xff0c;由供电电源&#xff0c;时钟电路晶振&#xff0c;复位和调试接口JTAG以及FLASH配置芯片组成&#xff0c;其与STM32最大的不同之处就是必须要有…

Appium自动化测试系列: 2. 使用Appium启动APP(真机)

历史文章&#xff1a;Appium自动化测试系列: 1. Mac安装配置Appium_mac安装appium-CSDN博客 一、准备工作 1. 安卓测试机打开调试模式&#xff0c;然后使用可以传输数据的数据线连接上你的电脑。注意&#xff1a;你的数据线一定要支持传输数据&#xff0c;有的数据线只支持充…

《数据结构:C语言实现顺序表》

文章目录 一、顺序表1、静态顺序表2、动态顺序表 二、动态顺序表实现1、创建自定义类型2、完成顺序表的创建&#xff0c;测试功能需求3、完成顺序表的初始化和销毁功能4、顺序表插入数据和打印数据5、删除数据 三、顺序表完成最终的代码test.c文件中的代码&#xff1a;用来测试…

新手教学系列——MongoDB聚合查询的进阶用法

引言 MongoDB的聚合查询是其最强大的功能之一。无论是汇总、平均值、计数等标准操作,还是处理复杂的数据集合,MongoDB的聚合框架都能提供高效且灵活的解决方案。本文将通过几个实例,详细讲解如何在实际项目中使用MongoDB进行聚合查询。 标准应用:汇总、平均值、计数等 在…

k8s集群部署mysql8主备

一、搜索mysql8版本 # helm search repo mysql# helm pull bitnami/mysql --version:11.1.2# tar -zxf mysql-11.1.2.tgz# cd mysql 二、修改value.ysqml文件 动态存储类自己提前搭建。 # helm install mysql8 -n mysql-cluster ./ -f values.yaml NAME: mysql8 LAST DEPLOYED…

Neo4j安装

下载地址&#xff1a;Neo4j Deployment Center - Graph Database & Analytics 1.安装jdk&#xff0c;Neo4j 3.0需要jdk8&#xff0c;2.3.0之前的版本建议jdk7。Neo4j最新版本5.21.2&#xff0c;对应jdk版本17 2.将下载的zip文件解压到合适路径。 3.设置环境变量NEO4J_H…

【机器学习】朴素贝叶斯算法详解与实战扩展

欢迎来到 破晓的历程的 博客 ⛺️不负时光&#xff0c;不负己✈️ 引言 朴素贝叶斯算法是一种基于概率统计的分类方法&#xff0c;它利用贝叶斯定理和特征条件独立假设来预测样本的类别。尽管其假设特征之间相互独立在现实中往往不成立&#xff0c;但朴素贝叶斯分类器因其计算…

卤味江湖中,周黑鸭究竟该抓住什么赛点?

近年来&#xff0c;卤味江湖的决斗从未停止。 随着休闲卤味、佐餐卤味等细分赛道逐渐形成&#xff0c;“卤味三巨头”&#xff08;周黑鸭、绝味食品、煌上煌&#xff09;的牌桌上有了更多新对手&#xff0c;赛道变挤了&#xff0c;“周黑鸭们”也到了转型关键期。 这个夏天&a…

linux系统操作/基本命令/vim/权限修改/用户建立

Linux的目录结构&#xff1a; 一&#xff1a;在Linux系统中&#xff0c;路径之间的层级关系&#xff0c;使用:/来表示 注意:1、开头的/表示根目录 2、后面的/表示层级关系 二&#xff1a;在windows系统中&#xff0c;路径之间的层级关系&#xff0c;使用:\来表示 注意:1、D:表示…

【web前端HTML+CSS+JS】--- JS学习笔记03

一、JS介绍 可以在前端页面上进行逻辑处理&#xff0c;来解决表单的验证等问题&#xff0c;提升效率&#xff0c;直接在前端提示问题&#xff0c;减少服务器压力 应用1&#xff1a;可以做静态验证和动态验证&#xff08;进行异步请求&#xff09; 应用2&#xff1a;可以解析后…

Postgresql - 用户权限数据库

1、综述 在实际的软件项目开发过程中&#xff0c;用户权限控制可以说是所有运营系统中必不可少的一个重点功能&#xff0c;根据业务的复杂度&#xff0c;设计的时候可深可浅&#xff0c;但无论怎么变化&#xff0c;设计的思路基本都是围绕着用户、部门、角色、菜单这几个部分展…

电脑数据恢复篇:如何从电脑中恢复已删除的照片

按下 Shift Delete 后后悔了&#xff1f;想要恢复已删除的照片&#xff1f;好吧&#xff0c;如果是这样的话&#xff0c;你来对地方了。在本文中&#xff0c;我们将讨论如何从 PC 中恢复已删除的文件。 自从摄影的概念被提出以来&#xff0c;人们就对它着迷。以前&#xff0c…

【SQL】DML、DDL、ROLLBACK 、COMMIT详解

DML DML&#xff08;Data Manipulation Language&#xff09;数据操作语言&#xff0c;是用于对数据库中的数据进行基本操作的一种编程语言。DML是数据库管理系统&#xff08;DBMS&#xff09;中的一个重要部分&#xff0c;它允许用户或应用程序对数据库中的数据进行增、删、改…

【鸿蒙学习笔记】文件管理

官方文档&#xff1a;Core File Kit简介 目录标题 文件分类什么是应用沙箱&#xff1f; 文件分类 应用文件&#xff0c;比如应用的安装包&#xff0c;自己的资源文件等。用户文件&#xff0c;比如用户自己的照片&#xff0c;录制的音视频等。 什么是应用沙箱&#xff1f; 应…

Socks5代理为何比HTTP代理快?

在网络世界中&#xff0c;代理服务器扮演着重要的角色&#xff0c;它们能够帮助我们访问被限制的网站、提高网络安全性以及优化网络性能。其中&#xff0c;Socks5代理和HTTP代理是两种常见的代理类型。然而&#xff0c;很多用户发现&#xff0c;相较于HTTP代理&#xff0c;Sock…

ctfshow-web入门-文件上传(web164、web165)图片二次渲染绕过

web164 和 web165 的利用点都是二次渲染&#xff0c;一个是 png&#xff0c;一个是 jpg 目录 1、web164 2、web165 二次渲染&#xff1a; 网站服务器会对上传的图片进行二次处理&#xff0c;对文件内容进行替换更新&#xff0c;根据原有图片生成一个新的图片&#xff0c;这样…