java23种设计模式(一)-- 工厂模式、抽象工厂模式和单例模式
一、工厂模式
1、定义统一的接口,并在接口中定义要实现的抽象方法。
2、创建接口的具体实现类,并实现抽象方法。
3、创建一个工厂类,根据传递的参数,生成具体的实现类对象,执行具体的方法。
优点: 1、一个调用者想创建一个对象,只要知道其名称就可以了。 2、扩展性高,如果想增加一个产品,只要扩展一个工厂类就可以。 3、屏蔽产品的具体实现,调用者只关心产品的接口。
缺点:每次增加一个产品时,都需要增加一个具体类和对象实现工厂,使得系统中类的个数成倍增加,在一定程度上增加了系统的复杂度,同时也增加了系统具体类的依赖。这并不是什么好事。
注意事项:作为一种创建类模式,在任何需要生成复杂对象的地方,都可以使用工厂方法模式。有一点需要注意的地方就是复杂对象适合使用工厂模式,而简单对象,特别是只需要通过 new 就可以完成创建的对象,无需使用工厂模式。如果使用工厂模式,就需要引入一个工厂类,会增加系统的复杂度。
二、抽象工厂模式
1、定义统一的接口,并在接口中定义要实现的抽象方法。
//为形状创建一个接口。
public interface Shape {
void draw();
}
//为颜色创建一个接口。
public interface Color {
void fill();
}
2、创建接口的具体实现,并实现抽象方法。
//创建形状的实现类。
public class Rectangle implements Shape { @Override
public void draw() {
System.out.println("Inside Rectangle::draw() method.");
}
}
//创建颜色的实现类
public class Red implements Color { @Override
public void fill() {
System.out.println("Inside Red::fill() method.");
}
}
3、创建一个抽象的工厂,用于根据不同的信息生成不同的具体生产对象的工厂。
//为 Color 和 Shape 对象创建抽象类来获取工厂。
public abstract class AbstractFactory {
public abstract Color getColor(String color);
public abstract Shape getShape(String shape) ;
}
4、每一个生产具体对象的工厂都继承上个步骤定义的抽象工厂,根据不同的信息生成不同的具体对象。
//创建扩展了 AbstractFactory 的工厂类,基于给定的信息生成实体类的对象。
public class ShapeFactory extends AbstractFactory { @Override
public Shape getShape(String shapeType){
if(shapeType == null){
return null;
}
if(shapeType.equalsIgnoreCase("CIRCLE")){
return new Circle();
} else if(shapeType.equalsIgnoreCase("RECTANGLE")){
return new Rectangle();
} else if(shapeType.equalsIgnoreCase("SQUARE")){
return new Square();
}
return null;
} @Override
public Color getColor(String color) {
return null;
}
}
public class ColorFactory extends AbstractFactory { @Override
public Shape getShape(String shapeType){
return null;
} @Override
public Color getColor(String color) {
if(color == null){
return null;
}
if(color.equalsIgnoreCase("RED")){
return new Red();
} else if(color.equalsIgnoreCase("GREEN")){
return new Green();
} else if(color.equalsIgnoreCase("BLUE")){
return new Blue();
}
return null;
}
}
5、创建一个工厂创建器,根据不同的信息创建不同的生成具体对象的工厂。
//创建一个工厂创造器/生成器类,通过传递形状或颜色信息来获取工厂。
public class FactoryProducer {
public static AbstractFactory getFactory(String choice){
if(choice.equalsIgnoreCase("SHAPE")){
return new ShapeFactory();
} else if(choice.equalsIgnoreCase("COLOR")){
return new ColorFactory();
}
return null;
}
}
6、调用的时候需要先获取生成具体对象的工厂,然后通过工厂生成具体的对象。
优点:当一个产品族中的多个对象被设计成一起工作时,它能保证客户端始终只使用同一个产品族中的对象。
缺点:产品族扩展非常困难,要增加一个系列的某一产品,既要在修改抽象工厂的代码,又要修改具体工厂的代码。
注意事项:产品族难扩展,产品等级易扩展。
三、单例模式
1、懒汉式,线程不安全。
这种方式是最基本的实现方式,这种实现最大的问题就是不支持多线程。因为没有加锁 synchronized,所以严格意义上它并不算单例模式。
这种方式 lazy loading 很明显,不要求线程安全,在多线程不能正常工作。
public class Singleton {
private static Singleton instance;
private Singleton (){} public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
2、懒汉式,线程安全。
这种方式具备很好的 lazy loading,能够在多线程中很好的工作,但是,效率很低,99% 情况下不需要同步。
优点:第一次调用才初始化,避免内存浪费。
缺点:必须加锁 synchronized 才能保证单例,但加锁会影响效率。
getInstance() 的性能对应用程序不是很关键(该方法使用不太频繁)。
public class Singleton {
private static Singleton instance;
private Singleton (){}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
3、饿汉式
这种方式比较常用,但容易产生垃圾对象。
优点:没有加锁,执行效率会提高。
缺点:类加载时就初始化,浪费内存。
它基于 classloader 机制避免了多线程的同步问题,不过,instance 在类装载时就实例化,虽然导致类装载的原因有很多种,在单例模式中大多数都是调用 getInstance 方法, 但是也不能确定有其他的方式(或者其他的静态方法)导致类装载,这时候初始化 instance 显然没有达到 lazy loading 的效果。
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton (){}
public static Singleton getInstance() {
return instance;
}
}
4、双检锁/双重校验锁(DCL,即 double-checked locking)
这种方式采用双锁机制,安全且在多线程情况下能保持高性能。
getInstance() 的性能对应用程序很关键。
public class Singleton {
private volatile static Singleton singleton;
private Singleton (){}
public static Singleton getSingleton() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
5、登记式/静态内部类
这种方式能达到双检锁方式一样的功效,但实现更简单。对静态域使用延迟初始化,应使用这种方式而不是双检锁方式。这种方式只适用于静态域的情况,双检锁方式可在实例域需要延迟初始化时使用。
这种方式同样利用了 classloader 机制来保证初始化 instance 时只有一个线程,它跟第 3 种方式不同的是:第 3 种方式只要 Singleton 类被装载了,那么 instance 就会被实例化(没有达到 lazy loading 效果),而这种方式是 Singleton 类被装载了,instance 不一定被初始化。因为 SingletonHolder 类没有被主动使用,只有通过显式调用 getInstance 方法时,才会显式装载 SingletonHolder 类,从而实例化 instance。想象一下,如果实例化 instance 很消耗资源,所以想让它延迟加载,另外一方面,又不希望在 Singleton 类加载时就实例化,因为不能确保 Singleton 类还可能在其他的地方被主动使用从而被加载,那么这个时候实例化 instance 显然是不合适的。这个时候,这种方式相比第 3 种方式就显得很合理。
public class Singleton {
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
private Singleton (){}
public static final Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
6、枚举
这种实现方式还没有被广泛采用,但这是实现单例模式的最佳方法。它更简洁,自动支持序列化机制,绝对防止多次实例化。
这种方式是 Effective Java 作者 Josh Bloch 提倡的方式,它不仅能避免多线程同步问题,而且还自动支持序列化机制,防止反序列化重新创建新的对象,绝对防止多次实例化。不过,由于 JDK1.5 之后才加入 enum 特性,用这种方式写不免让人感觉生疏,在实际工作中,也很少用。
不能通过 reflection attack 来调用私有构造方法。
public class EnumSingleton{
private EnumSingleton(){}
public static EnumSingleton getInstance(){
return Singleton.INSTANCE.getInstance();
} private static enum Singleton{
INSTANCE; private EnumSingleton singleton;
//JVM会保证此方法绝对只调用一次
private Singleton(){
singleton = new EnumSingleton();
}
public EnumSingleton getInstance(){
return singleton;
}
}
}
//调用
public static void main(String[] args) {
EnumSingleton obj1 = EnumSingleton.getInstance();
EnumSingleton obj2 = EnumSingleton.getInstance();
//输出结果:obj1==obj2?true
System.out.println("obj1==obj2?" + (obj1==obj2));
}
经验之谈:一般情况下,不建议使用第 1 种和第 2 种懒汉方式,建议使用第 3 种饿汉方式。只有在要明确实现 lazy loading 效果时,才会使用第 5 种登记方式。如果涉及到反序列化创建对象时,可以尝试使用第 6 种枚举方式。如果有其他特殊的需求,可以考虑使用第 4 种双检锁方式。
java23种设计模式(一)-- 工厂模式、抽象工厂模式和单例模式的更多相关文章
- java23种设计模式(二)抽象工厂模式
我们接着上一章的工厂方法模式继续学习一下抽象工厂模式. 抽象工厂模式:在工厂模式中,如果有多个产品,则就是抽象工厂模式. 例子: 有一个工厂开了两个子公司,专门用来生产电脑配件键盘和鼠标,一个是联想工 ...
- java23种设计模式之十:责任链模式
最近在学习netty中发现其中用到了责任链模式,然后结合自己在写代码中遇到了大量写if...else的情况,决定学习一下责任链模式. 一.什么样的场景下会选择用责任链模式 我们在进行业务逻辑判断时,需 ...
- java23种设计模式——三、工厂模式
源码在我的github和gitee中获取 工厂模式 工厂模式介绍 工厂模式是我们最常用的实例化对象模式了,是用工厂方法代替new操作的一种模式.著名的Jive论坛 ,就大量使用了工厂模式,工厂模式在J ...
- Java设计模式(三) 抽象工厂模式
原创文章,同步发自作者个人博客,转载请注明出处 http://www.jasongj.com/design_pattern/abstract_factory/ 抽象工厂模式解决的问题 上文<工厂 ...
- Java设计模式之-----工厂模式(简单工厂,抽象工厂)
一.工厂模式主要是为创建对象提供过渡接口,以便将创建对象的具体过程屏蔽隔离起来,达到提高灵活性的目的. 工厂模式在<Java与模式>中分为三类:1)简单工厂模式(Simple Factor ...
- 设计模式的征途—3.抽象工厂(Abstract Factory)模式
上一篇的工厂方法模式引入了工厂等级结构,解决了在原来简单工厂模式中工厂类职责太重的原则,但是由于工厂方法模式的每个工厂只生产一类产品,可能会导致系统中存在大量的工厂类,从而增加系统开销.那么,我们应该 ...
- 设计模式的征途—4.抽象工厂(Abstract Factory)模式
上一篇的工厂方法模式引入了工厂等级结构,解决了在原来简单工厂模式中工厂类职责太重的原则,但是由于工厂方法模式的每个工厂只生产一类产品,可能会导致系统中存在大量的工厂类,从而增加系统开销.那么,我们应该 ...
- Java设计模式—工厂方法模式&抽象工厂模式
工厂方法模式与抽象工厂模式都是设计模式中重要而且常见的模式. 工厂方法模式:定义一个用于创建对象的接口,让子类决定实例化哪一个类.工厂方法使一个类的实例化延迟到其子类. 通用类图如下: 在 ...
- PHP设计模式(三)抽象工厂模式(Abstract Factory)
一.什么是抽象工厂模式 抽象工厂模式的用意为:给客户端提供一个接口,可以创建多个产品族中的产品对象 ,而且使用抽象工厂模式还要满足以下条件: 系统中有多个产品族,而系统一次只可能消费其中一族产品. 同 ...
- Java设计模式学习笔记(四) 抽象工厂模式
前言 本篇是设计模式学习笔记的其中一篇文章,如对其他模式有兴趣,可从该地址查找设计模式学习笔记汇总地址 1. 抽象工厂模式概述 工厂方法模式通过引入工厂等级结构,解决了简单工厂模式中工厂类职责太重的问 ...
随机推荐
- Ubuntu18.04 安装 Idea 2018.2
https://blog.csdn.net/weixx3/article/details/81136822 Ubuntu18.04 安装 Idea 2018.2环境信息:OS:Ubuntu18.04J ...
- 更好的在 Git 项目中保存大文件(Git LFS 的使用)
珠玉在前, 大家可以参考 Git LFS的使用 - 简书 为什么要用 Git LFS 原有的 Git 是文本层面的版本控制, 为代码这种小文件设计的, 保存大文件会导致 repo 非常臃肿, push ...
- LDD快速参考
第二章 快速参考 本节中出现的条目会以它们在文中出现的顺序列出: insmod modprobe rmmod 用来装载模块到正运行的内核和移除模块的用户空间工具: #include <linux ...
- Android逆向之旅---SO(ELF)文件格式详解
第一.前言 从今天开始我们正式开始Android的逆向之旅,关于逆向的相关知识,想必大家都不陌生了,逆向领域是一个充满挑战和神秘的领域.作为一名Android开发者,每个人都想去探索这个领域,因为一旦 ...
- ROM、RAM、DRAM、SRAM、FLASH的区别?
在学习单片机的时候经常会被这些东西搞晕掉,什么ROM RAM FLASH EEPROM 等等......为了不被搞晕,做个笔记,不记得的时候过来看看. 下面是我在网上找的资料: ROM和RAM指的都是 ...
- 动态调试某个apk的smali代码,微信举例
本地环境: PC:windows 10,Intellij IDEA (android studio应该一样的) 手机:nexus5 8.1系统, 其他依赖:smalidea插件,xposed 插件 h ...
- 测开之路三十五:css引入
CSS是一种定义样式结构,如字体.颜色.位置等的语言,被用于描述网页上的信息格式化和现实的方式.CSS样式可以直接存储于HTML网页或者单独的样式单文件.无论哪一种方式,样式单包含将样式应用到指定类型 ...
- 如何保存android app日志
android 手机日志保存方法如下: 前置条件:已安装adb 1,手机usb连接电脑,打开USB调试模式(注意仅连接一台手机设备) 2,win+R输入cmd打开命令窗口,输入指令:adb devic ...
- 用原生js写小游戏--Flappy Bird
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- [ERR] 1114 - The table 'xxx' is full
异常原因: 磁盘空间不足 解决办法: 1. 新增磁盘 2. 删除无用数据 信息补充: df: df -h #查询磁盘空间命令 du: du|sort -nr|more #显示目录或者文件所占空间 du ...