第一句子大全,网罗天下好句子,好文章尽在本站!

「Java设计模式」图文代码案例详解Java五大创建者模式 建造者 原型 (抽象)工厂 单例模式

时间:2015-06-03

就是为了提供代码结构的扩展性,屏蔽每个功能类中的具体实现逻辑

友情提示:本文共有 9011 个字,阅读大概需要 19 分钟。

文章预览:

一、工厂模式 1、介绍 2、实例 (1)、典型的工厂模式 (2)、多个工厂方法模式 (3)、静态工厂方法模式 3、总结 二、抽象工厂模式 1、介绍 2、实例 3、实例拓展 4、总结 三、建造者模式 1、介绍 2、实例 3、实例拓展 4、总结 四、原型模式 1、介绍 2、实例 3、实例拓展 4、总结 五、单例模式 1、介绍 2、实例 (1)、懒汉式(线程不安全版) (2)、懒汉式(线程安全版) (3)、饿汉式(线程安全) (4)、使用类的内部类(线程安全) (5)、双重锁校验(线程安全) (6)、CAS「AtomicReference」(线程安全) (7)、枚举单例(线程安全) 3、总结

一、工厂模式

1、介绍

这种设计模式也是 Java 开发中最常的种模式,又称工厂方法模式,简单说 在工厂类中提供个创建对象的法, 允许实际调用类决定实例化对象的类型。就是为了提供代码结构的扩展性,屏蔽每个功能类中的具体实现逻辑。让外部可以更加简单的只是知道调即可,同时,这也是去掉众多 ifelse 的式。当然这可能也有些缺点,如需要实现的类常多,如何去维护,怎样减低开发成本。但这些问题都可以在后续的设计模式结合使中,逐步降低。

2、实例

(1)、典型的工厂模式

以一个生产男鞋和女鞋的工厂为例:

工厂类和生产者接口:

男鞋和女鞋的实现类:

实际调用类:

输出结果:

生产男鞋生产女鞋进程已结束,退出代码为 0

(2)、多个工厂方法模式

与典型的工厂模式对比,Factory2将每个子类实例返回封装成单独的方法

这也使得在实际调用中通过调用单独不同的方法即可得到实例:这样做的好处是不必关心触发某个实例创建的逻辑,只需要调用创建某个实例的专有方法即可。

(3)、静态工厂方法模式

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

3、总结

看完你看会觉得,只是判断男鞋或女鞋从而觉得输出语句业务,仅此而已。但是随着业务逻辑的增加,不同业务类型决定各种各样的逻辑功能时,事情也没有那么简单。比如若工厂实现的实例越来越多,而且实例间的相关性大大降低,这就凸显了工厂模式的重要性,充分发挥出其避免创建者与具体的产品逻辑耦合 、 满单职责,每个业务逻辑实现都在所属的类中完成 、 满开闭原则,需更改使调就可以在程序中引新的产品类型的优点。

但这样也会带来些问题,如有常多的奖品类型,那么实现的类会极速扩张。因此也需要使其他的模式进优化,这些在后续的设计模式中会逐步涉及到。从案例看设计模式往往要看理论学的更加容易,因为案例是缩短理论到上的最佳式,如果你已经有所收获,定要去尝试实操。

二、抽象工厂模式

1、介绍

抽象工厂模式其实就是在工厂模式的基础上又添加了一层“工厂的工厂”。上述的工厂方法模式有一个问题就是,类的创建十分依赖工厂类,如果想要拓展程序,必须对工厂类进行修改,这违背了闭包原则,所以,从设计角度考虑,有一定的问题,如何解决?就用到抽象工厂模式,创建多个工厂类,这样一旦需要增加新的功能,直接增加新的工厂类就可以了,不需要修改之前的代码。

2、实例

仍然以男鞋女鞋工厂为例,与上面不同的是,该模式将“一个工厂生产两种鞋”的业务变成了两个工厂各自生产一种鞋。

两个产品:

两个工厂:

测试:

3、实例拓展

以搭建Redis集群A、B为例:

可以预的问题会有:

很多服务到了Redis需要起升级到集群。 需要兼容集群A和集群B,便于后续的灾备。 两套集群提供的接和法各有差异,需要做适配。 不能影响到前正常运的系统。

采代理类的式创建和获取抽象工厂,所被代理的类就是Redis操作法类,让这个类在不需要任何修改下,就可以实现调集群A和集群B的数据服务。

由于集群A和集群B在部分法提供上是不同的,因此需要做个接适配(ICacheAdapter),这个适配类就相当于中的,工厂A类和工厂B类实现了该适配接口,并且实现其方法。于创建把不同的服务抽象为统的接做相同的业务。

4、总结

好处就是,如果你现在想增加一个功能:生产男鞋和女鞋以外的鞋,虽然这似乎不符合伦理道德,但实际业务可能就是需要实现。那么只需创建一个产品实现类,实现Product接口,同时创建一个工厂类,实现Producer接口就OK了,无需去改动现成的代码。那么这个设计模式满了:单职责、开闭原则、解耦等优点

但如果说随着业务的不断拓展,可能会造成类实现上的复杂度。但也可以说算不上缺点,因为可以随着其他设计式的引和代理类以及动成加载的式降低此项缺点。

三、建造者模式

1、介绍

建造者模式主要解决的问题是在软件系统中,有时候临着"个复杂对象"(鞋)的创建作,其通常由各个部分的对象定的过程构成;由于需求的变化,这个复杂对象的各个部分经常临着重的变化,但是将它们组合在起(Builder)的过程却相对稳定。

这我们会把构建的过程交给 创建者 类,创建者通过使我们的 构建具包 ,去构建出不同的生产方案。

2、实例

仍然是生产产品接口及其实现类:

建造者类发出命令分别生产3双男鞋和2双女鞋:

测试结果:

生产男鞋生产男鞋生产男鞋生产女鞋生产女鞋进程已结束,退出代码为 0

3、实例拓展

现在我们在一个选择装修套餐(豪华欧式、轻奢园、现代简约三种)的场景下,利用建造者结合各种物料以及物料品牌做出构建方案。

DecorationPackageMenu类通过实现IMenu接口,将不同的物料构建方法封装进来,对外开放的参数只需要Builder将物料及品牌扔进去,让DecorationPackageMenu处理后返回IMenu即可。

4、总结

建造者模式将很多功能集成到一个类里,这个类可以创造出比较复杂的东西。所以与工厂模式的区别就是:工厂模式关注的是创建单个产品,而建造者模式则关注创建符合对象,多个部分。通过上对建造者模式的使,已经可以摸索出点得。那就是什么时候会选择这样的设计模式,当: 些基本物料不会变,其组合经常变化的时候 ,就可以选择这样的设计模式来构建代码。此设计模式满了单职责原则以及可复的技术、建造者独、易扩展、便于控制细节险。

但同时当出现特别多的物料以及很多的组合后,类的不断扩展也会造成难以维护的问题。但这种设计结构模型可以把复的内容抽象到数据库中,按照需要配置。这样就可以减少代码中量的重复。

四、原型模式

1、介绍

该模式的思想就是将一个对象作为原型,对其进行复制、克隆,产生一个和原对象类似的新对象。本小结会通过对象的复制,进行讲解。

2、实例

在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;}

3、实例拓展

模拟出题方式,在这模拟了两个试卷题的类; ChoiceQuestion (选择题)、 AnswerQuestion (问答题)。如果是实际的业务场景开发中,会有更多的题类型,可以回忆下你的考试卷。

QuestionBank 克隆试卷对象处理类

QuestionBankController 初始化试卷数据类

这个类的内容就较简单了,主要提供对试卷内容的模式初始化操作(所有考试卷样,题顺序不致)。以及对外部提供创建试卷的法,在创建的过程中使的是克隆的式;(QuestionBank)questionBank.clone(),并最终返回试卷信息。

4、总结

以上的实际场景模拟了原型模式在开发中重构的作,但是原型模式的使频率确实不是很。如果有些特殊场景需要使到,也可以按照此设计模式进优化。另外原型设计模式的优点包括:便于通过克隆式创建复杂对象、也可以避免重复做初始化操作、不需要与类中所属的其他类耦合等。但也有些缺点如果对象中包括了循环引的克隆,以及类中深度使对象的克隆,都会使此模式变得异常麻烦。

五、单例模式

1、介绍

单例模式可以说是整个设计中最简单的模式之,且这种式即使在没有看设计模式相关资料也会常在编码开发中。因为在编程开发中经常会遇到这样种场景,那就是需要保证个类只有个实例哪怕多线程同时访问,并需要提供个全局访问此实例的点。综上以及我们平常的开发中,可以总结条经验,单例模式主要解决的是,个全局使的类频繁的创建和消费,从提升提升整体的代码的性能。

技术场景:

数据库的连接池不会反复创建 spring中个单例模式bean的成和使 在我们平常的代码中需要设置全局的的些属性保存

2、实例

(1)、懒汉式(线程不安全版)

“你要的时候我才new,而且只new一次”

public class Singleton_01 { private static Singleton_01 instance; private Singleton_01() { } public static Singleton_01 getInstance(){ if (null != instance){ //只能有一个实例 return instance; }esle{ instance = new Singleton_01(); return instance; } }}

前此种式的单例确实满了懒加载,但是如果有多个访问者同时去获取对象实例你可以想象成堆在抢厕所,就会造成多个同样的实例并存,从没有达到单例的要求。

(2)、懒汉式(线程安全版)

public class Singleton_02 { private static Singleton_02 instance; private Singleton_02() { } public static synchronized Singleton_01 getInstance(){ //设置同步锁 if (null != instance){ //只能有一个实例 return instance; }esle{ instance = new Singleton_01(); return instance; } }}

此种模式虽然是线程安全的,但由于把锁加到法上后,所有的访问都因需要锁占导致资源的浪费。如果不是特殊情况下,不建议此种式实现单例模式。

(3)、饿汉式(线程安全)

"不管你要不要,我先new一个,而且只new一次"

public class Singleton_03 { private static Singleton_03 instance = new Singleton_03(); private Singleton_03() { } public static Singleton_03 getInstance() { return instance; }}

解释:

由于instance为静态实例,随着类的加载而加载,调用静态方法getInstance时,返回的一直是这个一开始就被加载到内存里的instance实例,从而达到了单例的效果。

此种式并不是懒加载,也就是说论你程序中是否到这样的类都会在程序启动之初进创建。那么这种式导致的问题就像你下载个游戏软件,可能你游戏地图还没有打开呢,但是程序已经将这些地图全部实例化。到你机上最明显体验就开游戏内存满了,机卡了,需要换了。

(4)、使用类的内部类(线程安全)

此种式是常推荐使的种单例模式。

使类的静态内部类实现的单例模式,既保证了线程安全有保证了懒加载,即使用的时候只要调用getInstance方法,内部类SingletonHolder才会被加载从而实现了懒加载,同时不会因为加锁的式耗费性能。这主要是因为JVM虚拟机可以保证多线程并发访问的正确性,也就是个类的构造法在多线程环境下可以被正确的加载。

public class Singleton_04 { private Singleton_04() { } private static class SingletonHolder { private static Singleton_04 instance = new Singleton_04(); } public static Singleton_04 getInstance() { return SingletonHolder.instance; }}

(5)、双重锁校验(线程安全)

public class Singleton_05 { private static Singleton_05 instance; private Singleton_05() { } public static Singleton_05 getInstance(){ if(instance != null){ return instance; } synchronized (Singleton_05.class){ //保证线程安全 if (instance == null){ instance = new Singleton_05(); } } return instance; }}

双重锁的式是法级锁的优化,其实就是懒汉式线程安全版的性能优化,因为懒汉式加了方法锁,每次调用getInstance()时,都要对对象上锁,实际上,只有第一次创建对象的时候才需要加锁,所以可以改成如上的形式。减少了部分获取实例的耗时。同时这种式也满了懒加载

(6)、CAS「AtomicReference」(线程安全)

public class Singleton_06 { private static final AtomicReference ar = new AtomicReference(); private static Singleton_06 instance; private Singleton_06() { } public static final Singleton_06 getInstance() { for (; ; ) { Singleton_06 instance = ar.get(); if (null != instance) return instance; ar.compareAndSet(null, new Singleton_06()); return ar.get(); } } public static void main(String[] args) { System.out.println(Singleton_06.getInstance()); //org.itstack.demo.design.Singleton_06@2b193f2d System.out.println(Singleton_06.getInstance()); //org.itstack.demo.design.Singleton_06@2b193f2d }}

java并发库提供了很多原类来持并发访问的数据安全性:AtomicInteger 、 AtomicBoolean 、 AtomicLong 、 AtomicReference 。

AtomicReference 可以封装引个V实例,持并发访问如上的单例式就是使了这样的个特点。

使CAS的好处就是不需要使传统的加锁式保证线程安全,是依赖于CAS的忙等算法,依赖于底层硬件的实现,来保证线程安全。相对于其他锁的实现没有线程的切换和阻塞也就没有了额外的开销,并且可以持较的并发性。当然CAS也有个缺点就是忙等,如果直没有获取到将会处于死循环中

(7)、枚举单例(线程安全)

public enum Singleton_07 { INSTANCE; public void test(){ System.out.println("hi~"); }}

@Testpublic void test() { Singleton_07.INSTANCE.test();}

Effective Java 作者推荐使枚举的式解决单例模式,此种式可能是平时最少到的。这种式解决了最主要的:线程安全、由串化、单实例。偿地提供了串化机制,绝对防对此实例化,即使是在对复杂的串化或者反射攻击的时候。虽然这种法还没有泛采,但是单元素的枚举类型已经成为实现Singleton的最佳法。但也要知道此种式在存在继承场景下是不可的。

3、总结

虽然只是个很平常的单例模式,但在各种的实现上真的可以看到java的基本功的体现,这包括了:懒汉、饿汉、线程是否安全、静态类、内部类、加锁、串化等等。

在平时的开发中如果可以确保此类是全局可不需要做懒加载,那么直接创建并给外部调即可。但如果是很多的类,有些需要在户触发定的条件后才显示,那么定要懒加载。线程的安全上可以按需选择。

,https://blog.csdn.net/weixin_45296116/article/details/121055884

本文如果对你有帮助,请点赞收藏《「Java设计模式」图文代码案例详解Java五大创建者模式 建造者 原型 (抽象)工厂 单例模式》,同时在此感谢原作者。

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。
相关阅读
Java语言编程基础100题期末复习

Java语言编程基础100题期末复习

...模式是:A.ASCII B.十六进制 C.Unicode D.八进制7. Java源代码文件可以下列哪个扩展名储存?A.java B.javac C.javax D.src8. 以下哪个是关于Java的正确说法A.具有编译器 B.具有解释程序C.具有编译器和解释程序 D.具有...

2024-01-15 #经典句子

Java代码的finally语句块中 这个语句要慎用

Java代码的finally语句块中 这个语句要慎用

...用的。是什么语句呢,容我卖个关子。大家看看下面这段代码会输出什么结果呢。想一想,会输出什么内容。不要着急说出来。再看看下面这段代码会输出什么结果呢。和第一段代码相比,就是在try块中去掉了异常的抛出。这次...

2023-01-19 #经典句子

Java中的3个双引号是什么语法?Java15刷新你的认知

Java中的3个双引号是什么语法?Java15刷新你的认知

...编译器自动增加换行符可以用来增强用非 Java 语言编写的代码的字符串的可读性,比如我们经常要拼接 HTML、XML、SQL 语言代码等,代码非常难看。就等同于:"line 1nline 2nline 3n"或者:"line 1n" +"line 2n" +"line 3n"如果最后一行不需要换...

2017-07-21 #经典句子

跟我学java编程—Java跳转语句—break语句

跟我学java编程—Java跳转语句—break语句

...a”文件。用记事本打开“BreakSample1.java”文件,输入以下代码:代码结构分析程序功能主要是演示break语句的使用。该循环在变量i的值等于5时,满足条件,然后执行break语句,结束整个循环,循环语句后面没有其它语句可执行,...

2023-11-01 #经典句子

浅析 Java 程序语言的运行机制

浅析 Java 程序语言的运行机制

...释型语言(Interpreted language)这种类型的编程语言,会将代码一句一句直接运行,不需要像编译语言(Compiled language)一样,经过编译器先行编译为机器代码,之后再运行。这种编程语言需要利用解释器,在运行期,动态将代码...

2009-06-06 #经典句子

自学Java8(保姆级教学)——流程控制语句

自学Java8(保姆级教学)——流程控制语句

...,我们下班回家的流程:下班——回家——吃晚饭——敲代码?刷视频?玩游戏?也就是说,当我们回家吃晚饭之后,我们剩下的时间,我们可以去做自己的事,比如可以继续深造敲代码,可以放松一下刷刷视频或者玩玩游戏,...

2022-12-28 #经典句子

跟我学java编程—Java跳转语句—continue语句

跟我学java编程—Java跳转语句—continue语句

...文件。用记事本打开“ContinueSample.java”文件,输入以下代码:代码结构分析程序功能主要是演示continue语句的使用方法。程序检查i除以10的余数是否等于3,如果是,则意味该数的个位数是3,这种情况下,将使用continue语句跳过...

2023-09-30 #经典句子

学java编程一定要会英语吗?关于这个我给你说句实话

学java编程一定要会英语吗?关于这个我给你说句实话

...扰,因为我们可能都知道,学习编程都是用英文在写相关代码。那么我的英语恰好学得不是那么的好,甚至很差,那我到底能不能学习编程呢?那今天我的这篇文章就来分析一下这个问题。英语不好到底能不能学习Java呢?以我...

2023-10-31 #经典句子