一、工厂模式

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种设计模式(一)-- 工厂模式、抽象工厂模式和单例模式的更多相关文章

  1. java23种设计模式(二)抽象工厂模式

    我们接着上一章的工厂方法模式继续学习一下抽象工厂模式. 抽象工厂模式:在工厂模式中,如果有多个产品,则就是抽象工厂模式. 例子: 有一个工厂开了两个子公司,专门用来生产电脑配件键盘和鼠标,一个是联想工 ...

  2. java23种设计模式之十:责任链模式

    最近在学习netty中发现其中用到了责任链模式,然后结合自己在写代码中遇到了大量写if...else的情况,决定学习一下责任链模式. 一.什么样的场景下会选择用责任链模式 我们在进行业务逻辑判断时,需 ...

  3. java23种设计模式——三、工厂模式

    源码在我的github和gitee中获取 工厂模式 工厂模式介绍 工厂模式是我们最常用的实例化对象模式了,是用工厂方法代替new操作的一种模式.著名的Jive论坛 ,就大量使用了工厂模式,工厂模式在J ...

  4. Java设计模式(三) 抽象工厂模式

    原创文章,同步发自作者个人博客,转载请注明出处 http://www.jasongj.com/design_pattern/abstract_factory/ 抽象工厂模式解决的问题 上文<工厂 ...

  5. Java设计模式之-----工厂模式(简单工厂,抽象工厂)

    一.工厂模式主要是为创建对象提供过渡接口,以便将创建对象的具体过程屏蔽隔离起来,达到提高灵活性的目的. 工厂模式在<Java与模式>中分为三类:1)简单工厂模式(Simple Factor ...

  6. 设计模式的征途—3.抽象工厂(Abstract Factory)模式

    上一篇的工厂方法模式引入了工厂等级结构,解决了在原来简单工厂模式中工厂类职责太重的原则,但是由于工厂方法模式的每个工厂只生产一类产品,可能会导致系统中存在大量的工厂类,从而增加系统开销.那么,我们应该 ...

  7. 设计模式的征途—4.抽象工厂(Abstract Factory)模式

    上一篇的工厂方法模式引入了工厂等级结构,解决了在原来简单工厂模式中工厂类职责太重的原则,但是由于工厂方法模式的每个工厂只生产一类产品,可能会导致系统中存在大量的工厂类,从而增加系统开销.那么,我们应该 ...

  8. Java设计模式—工厂方法模式&抽象工厂模式

    工厂方法模式与抽象工厂模式都是设计模式中重要而且常见的模式.       工厂方法模式:定义一个用于创建对象的接口,让子类决定实例化哪一个类.工厂方法使一个类的实例化延迟到其子类. 通用类图如下: 在 ...

  9. PHP设计模式(三)抽象工厂模式(Abstract Factory)

    一.什么是抽象工厂模式 抽象工厂模式的用意为:给客户端提供一个接口,可以创建多个产品族中的产品对象 ,而且使用抽象工厂模式还要满足以下条件: 系统中有多个产品族,而系统一次只可能消费其中一族产品. 同 ...

  10. Java设计模式学习笔记(四) 抽象工厂模式

    前言 本篇是设计模式学习笔记的其中一篇文章,如对其他模式有兴趣,可从该地址查找设计模式学习笔记汇总地址 1. 抽象工厂模式概述 工厂方法模式通过引入工厂等级结构,解决了简单工厂模式中工厂类职责太重的问 ...

随机推荐

  1. Autoit脚本调用pscp上传小程序

    linux上传文件用pscp上传相对麻烦,如下写了个脚本方便上传 代码如下: $fileURL=@ScriptDir & "pscp.ini" If (FileExists ...

  2. Vue通信的10种方式

    1.10种通信方式 10种:https://juejin.im/post/5bd18c72e51d455e3f6e4334 2.除此之外,还有children和ref. 需要注意 $children  ...

  3. 【leetcode】892. Surface Area of 3D Shapes

    题目如下: 解题思路:对于v = grid[i][j],其表面积为s = 2 + v*4 .接下来只要在判断其相邻四个方向有没有放置立方体,有的话减去重合的面积即可. 代码如下: class Solu ...

  4. webpack.config.js配置入口出口文件

    目录结构: 新建webpack.config.js配置文件 const path = require('path') //导出 path是node内置的包 通过npm init初始化得到package ...

  5. list的过滤操作

    假设 l = ['abc', 'mn', 'aq', 'liuming'] 我要过滤出以a开头的元素,方法有以下两种 方法1: l = ['abc', 'mn', 'aq', 'liuming'] l ...

  6. EDA课设-交通灯-Verilog版----FPGA--004

    分得到析四个状态: S1: 主干道(绿灯亮) ,支干道(亮红灯):--40S S1: 主干道 (黄灯亮) ,支干道(亮红灯):--4S S1: 主干道 (亮红灯),支干道(绿灯亮):--20S S1: ...

  7. CodeForces - 990G (点分治+链表计数)

    题目:https://vjudge.net/contest/307753#problem/J 题意:一棵树,每个点都有个权值,现在问你,树上gcd每个不同的数有多少个 思路:点分治,首先范围只有 1e ...

  8. JS基础入门篇(四)—this的使用,模拟单选框,选项卡和复选框

    1.this的使用 this js中的关键字 js内部已经定义好了,可以不声明 直接使用 this的指向问题 1. 在函数外部使用 this指向的是window 2. 在函数内部使用 有名函数 直接调 ...

  9. linux记事工具:RedNotebook Lifeograph Kontact ThotKeeper

    Linux桌面有许多灵活而功能强大的日记工具,如支持标签.加密.多种日志模版和实时搜索.其中的优秀者包括: RedNotebook Lifeograph Kontact ThotKeeper

  10. 关于C++中nothrow的某某某

    前言 在学习C++中new的种种用法时,在operator new的其中一个重载版本中看一个参数nothrow,想弄清楚到底是什么意思?nothrow顾名思义,就是不抛出的意思嘛!不抛出啥,在C++中 ...