软件设计模式“单例模式”和“工厂模式”

  • 单例模式
    • 什么是单例模式
    • 单例模式的实现方式有哪些
    • 单例模式的优缺点
    • 单例模式的应用场景
    • 总结
  • 工厂模式
    • 什么是工厂模式
    • 工厂模式的实现方式有哪些
    • 简单工厂模式
    • 工厂方法模式
    • 抽象工厂模式

单例模式

什么是单例模式

单例(Singleton)模式的定义:指一个类只有一个实例,且该类能自行创建这个实例的一种模式。

例如:Windows中只能打开一个任务管理器,这样可以避免因打开多个任务管理器窗口而造成内存资源的浪费,或出现各个窗口显示内容的不一致等错误。

单例模式的实现方式有哪些

常见的实现方式有:懒汉模式、饥汉模式、双重校验锁、静态内部类、枚举等方式实现,那我们我们紧接着就具体的一个一个的来看看他们的实现
懒汉模式

/*** @author hz* @version 1.0*/
public class Singleton {private static Singleton instance = null;private Singleton(){}public static synchronized Singleton getInstance(){//如果还没有被实例化过,就实例化一个,然后返回if(instance == null){instance = new Singleton();}return instance;}
}

这里的关键字synchronized是用于确保getInstance方法线程安全,但是这种方式弊端特别大,因为所有线程到达该方法以后需要进行排队等候,所以对性能的损耗非常大,该处理解决了线程安全安全问题,并没有解决效率问题,如果要既要解决线程安全问题又要解决效率问题则需要我们后面的双重校验锁方式。

当 synchronized 作用于静态方法时,其锁就是当前类的 class
对象锁。由于静态成员不专属于任何一个实例对象,是类成员,因此通过 class 对象锁可以控制静态成员的并发操作。需要注意的是如果一个线程 A
调用一个实例对象的非 static synchronized 方法,而线程 B 需要调用这个实例对象所属类的静态
==synchronized ==方法,是允许的,不会发生互斥现象,因为访问静态 synchronized方法占用的锁是当前类的 class 对象,而访问非静态 ==synchronized ==方法占用的锁是当前实例对象锁,二者的锁并不一样,所以不冲突。

懒汉模式将实例化的时机放到了需要使用的时候(饿汉是类加载了就有实例),也就是“延迟加载”,相比饿汉,能避免了在加载的时候实例化有可能用不到的实例,但是问题也很明显,我们要花精力去解决线程安全的问题。
饿汉模式:

 /*** @author hz* @version 1.0*/
public class Singleton {//类加载的时候instance就已经指向了一个实例private static Singleton instance = new Singleton();private Singleton(){}public static Singleton getInstance(){return instance;}
}

饿汉模式相比懒汉模式,在类加载的时候就已经存在一个实例,举个例子,比如数据库连接吧,懒汉就是第一次访问数据库的时候我才去创建一个连接,而饿汉呢,是你程序启动了,类加载好了的时候,我已经有个连接了,你用不用不一定了,所以饿汉的缺点也就出来了:可能会产生很多无用的实例。

那么加载时机的问题我们已经说过了,接下来就是线程安全了,代码里我们并没有看见synchronized关键字,那么这种方式是如何确保线程安全的呢,这个就是JVM类加载的特性了,JVM在加载类的时候,是单线程的,所以可以保证只存在单一的实例
双重校验锁

/*** @author hz* @version 1.0*/
public class Singleton {private static Singleton instance = null;private Singleton(){}public static Singleton getInstance(){if(instance == null){synchronized (Singleton.class){if(instance == null){instance = new Singleton();}}}return instance;}
}

首先要说明的是,双重检验锁也是一种延迟加载,并且较好的解决了在确保线程安全的时候效率低下的问题,对比一下最原始的那种线程安全的方法(就是懒汉模式的第二种代码),那种方法将整个getInstance方法锁住,那么每次调用那个方法都要获得锁,释放锁,等待等等…而双重校验锁锁住了部分的代码。进入方法如果检查为空才进入同步代码块,这样很明显效率高了很多。
静态内部类

/*** @author hz* @version 1.0*/
public class Singleton {private static class SingletonHolder{private static Singleton instance = new Singleton();}private Singleton(){}public static Singleton getInstance(){return SingletonHolder.instance;}
}

懒汉模式需要考虑线程安全,所以我们多写了好多的代码,饿汉模式利用了类加载的特性为我们省去了线程安全的考虑,那么,既能享受类加载确保线程安全带来的便利,又能延迟加载的方式,就是静态内部类

Java静态内部类的特性是:加载的时候不会加载内部静态类,使用的时候才会进行加载。而使用到的时候类加载又是线程安全的,这就完美的达到了我们的预期效果。
枚举

/*** @author hz* @version 1.0*/
public enum Singleton {INSTANCE;
}

JDK1.5提供了一个新的数据类型:枚举

枚举的出现提供了一个较为优雅的方式取代以前大量的static final类型的变量。而这里,我们也利用枚举的特性,实现了单例模式,外部调用由原来的Singleton.getInstance变成了Singleton.INSTANCE了。

以上几种单例模式的实现各自有各自的优势,所以我们在实际使用中,需要根据自己的需求进行选择,而通过上述实现方式我们可以可以总结一下单例模式的特点

1).单例模式只有一个实例对象;

2).该单例对象必须由单例类创建;

3).单例类对外提供一个访问该单例的全局访问点。

单例模式的优缺点

优点

单例模式可以保证内存里只有一个实例,减少了内存的开销。

可以避免对资源的多重占用。

单例模式设置全局访问点,可以优化和共享资源的访问。

缺点

单例模式一般没有接口,扩展困难。如果要扩展,则除了修改原来的代码,没有第二种途径,违背开闭原则。

在并发测试中,单例模式不利于代码调试。在调试过程中,如果单例中的代码没有执行完,也不能模拟生成一个新的对象。

单例模式的功能代码通常写在一个类中,如果功能设计不合理,则很容易违背单一职责原则。

单例模式的应用场景

对于 Java 来说,单例模式可以保证在一个 JVM 中只存在单一实例。

单例模式的应用场景主要有以下几个方面:

需要频繁创建的一些类,使用单例可以降低系统的内存压力,减少 GC。

某类只要求生成一个对象的时候,如一个班中的班长、每个人的身份证号等。

某些类创建实例时占用资源较多,或实例化耗时较长,且经常使用。

某类需要频繁实例化,而创建的对象又频繁被销毁的时候,如多线程的线程池、网络连接池等。

频繁访问数据库或文件的对象。

对于一些控制硬件级别的操作,或者从系统上来讲应当是单一控制逻辑的操作,如果有多个实例,则系统会完全乱套。

当对象需要被共享的场合。由于单例模式只允许创建一个对象,共享该对象可以节省内存,并加快对象访问速度。如 ==Web ==中的配置对象、数据库的连接池等。

总结

在单例模式各种设计的方法中,我们使用到了内部静态类的特性,使用了枚举的特性,所以基础非常重要,单例模式是设计模式之一,而设计模式其实是对语言特性不足的一面进一步的包装。吸纳基础,工作学习多加思考,设计模式也就自然而然的能够理解。

工厂模式

什么是工厂模式

定义一个创建产品对象的工厂接口,将产品对象的实际创建工作推迟到具体子工厂类当中。这满足创建型模式中所要求的“创建与使用相分离”的特点。所有我们在实际开发中,凡是需要生成复杂对象的地方,都可以尝试考虑使用工厂模式来代替。

工厂模式的实现方式有哪些

按实际业务场景划分,工厂模式有 3 种不同的实现方式,分别是简单工厂模式、工厂方法模式和抽象工厂模式。

所以紧接着我们来依次根据不同的场景看看这几种工厂模式。

简单工厂模式

我们把被创建的对象称为“产品”,把创建产品的对象称为“工厂”。如果要创建的产品不多,只要一个工厂类就可以完成,这种模式叫“简单工厂模式”。

在简单工厂模式中创建实例的方法通常为静态(static)方法

因此简单工厂模式(Simple Factory Pattern)又叫作静态工厂方法模式(Static Factory Method Pattern)。

首先我们来看看简单工厂模式的组成:

简单工厂(SimpleFactory):是简单工厂模式的核心,负责实现创建所有实例的内部逻辑。工厂类的创建产品类的方法可以被外界直接调用,创建所需的产品对象。
抽象产品(Product):是简单工厂创建的所有对象的父类,负责描述所有实例共有的公共接口。
具体产品(ConcreteProduct):是简单工厂模式的创建目标。

绘制结构图如下:

用代码表示:

/*** @author hz* @version 1.0*/
public class Client {//抽象产品public interface Product {void show();}//具体产品:ProductAstatic class ConcreteProduct1 implements Product {public void show() {System.out.println("具体产品1显示...");}}//具体产品:ProductBstatic class ConcreteProduct2 implements Product {public void show() {System.out.println("具体产品2显示...");}}final class Const {static final int PRODUCT_A = 0;static final int PRODUCT_B = 1;static final int PRODUCT_C = 2;}static class SimpleFactory {public static Product makeProduct(int kind) {switch (kind) {case Const.PRODUCT_A:return new ConcreteProduct1();case Const.PRODUCT_B:return new ConcreteProduct2();}return null;}}
}

该模式的优缺点有哪些呢
优点

工厂类包含必要的逻辑判断,可以决定在什么时候创建哪一个产品的实例。客户端可以免除直接创建产品对象的职责,很方便的创建出相应的产品。工厂和产品的职责区分明确。
客户端无需知道所创建具体产品的类名,只需知道参数即可。
也可以引入配置文件,在不修改客户端代码的情况下更换和添加新的具体产品类。

缺点

简单工厂模式的工厂类单一,负责所有产品的创建,职责过重,一旦异常,整个系统将受影响。且工厂类代码会非常臃肿,违背高聚合原则。
使用简单工厂模式会增加系统中类的个数(引入新的工厂类),增加系统的复杂度和理解难度
系统扩展困难,一旦增加新产品不得不修改工厂逻辑,在产品类型较多时,可能造成逻辑过于复杂
简单工厂模式使用了 static 工厂方法,造成工厂角色无法形成基于继承的等级结构。

但是简单工厂模式每增加一个产品就要增加一个具体产品类和一个对应的具体工厂类,这增加了系统的复杂度,违背了“开闭原则”,因此我们进行进一步对简单工厂进行抽象,这个也是我们后面马上将给大家说的工厂模式的第二个实现场景,工厂方法模式

工厂方法模式

前面我们说了简单工厂模式违背了开闭原则,而“工厂方法模式”是对简单工厂模式的进一步抽象化,其好处是可以使系统在不修改原来代码的情况下引进新的产品,即满足开闭原则。

工厂方法模式的主要角色包含:

抽象工厂(Abstract Factory):提供了创建产品的接口,调用者通过它访问具体工厂的工厂方法 newProduct()
来创建产品。
具体工厂(ConcreteFactory):主要是实现抽象工厂中的抽象方法,完成具体产品的创建。
抽象产品(Product):定义了产品的规范,描述了产品的主要特性和功能。
具体产品(ConcreteProduct):实现了抽象产品角色所定义的接口,由具体工厂来创建,它同具体工厂之间一一对应。

绘制结构图如下:


对比简单工厂模式,工厂方法模式在之间简单的工厂的基础之上将工厂进行拆分成抽象工厂和具体工厂两部分内容,这样灵活性得到大量提升。
用代码表示:

/*** @author hz* @version 1.0*/
public class AbstractFactoryTest {public static void main(String[] args) {try {Product a;AbstractFactory af;af = (AbstractFactory) ReadXML.getObject();//抽象工厂内容放入到外部配置文件xml/properties等文件中,通过I/O流加载从而创建出抽象工厂a = af.newProduct();a.show();} catch (Exception e) {System.out.println(e.getMessage());}}
}
//抽象产品:提供了产品的接口
interface Product {public void show();
}
//具体产品1:实现抽象产品中的抽象方法
class ConcreteProduct1 implements Product {public void show() {System.out.println("具体产品1显示...");}
}
//具体产品2:实现抽象产品中的抽象方法
class ConcreteProduct2 implements Product {public void show() {System.out.println("具体产品2显示...");}
}
//抽象工厂:提供了厂品的生成方法
interface AbstractFactory {public Product newProduct();
}
//具体工厂1:实现了厂品的生成方法
class ConcreteFactory1 implements AbstractFactory {public Product newProduct() {System.out.println("具体工厂1生成-->具体产品1...");return new ConcreteProduct1();}
}
//具体工厂2:实现了厂品的生成方法
class ConcreteFactory2 implements AbstractFactory {public Product newProduct() {System.out.println("具体工厂2生成-->具体产品2...");return new ConcreteProduct2();}
}
import javax.xml.parsers.*;
import org.w3c.dom.*;
import java.io.*;
/*** @author hz* @version 1.0*/
public class ReadXML {//该方法用于从XML配置文件中提取具体类类名,并返回一个实例对象public static Object getObject() {try {//创建文档对象DocumentBuilderFactory dFactory = DocumentBuilderFactory.newInstance();DocumentBuilder builder = dFactory.newDocumentBuilder();Document doc;doc = builder.parse(new File("src/FactoryMethod/config1.xml"));//获取包含类名的文本节点NodeList nl = doc.getElementsByTagName("className");Node classNode = nl.item(0).getFirstChild();String cName = "FactoryMethod." + classNode.getNodeValue();//System.out.println("新类名:"+cName);//通过类名生成实例对象并将其返回Class<?> c = Class.forName(cName);Object obj = c.newInstance();return obj;} catch (Exception e) {e.printStackTrace();return null;}}}

那么该模式有哪些优缺点呢?
优点

用户只需要知道具体工厂的名称就可得到所要的产品,无须知道产品的具体创建过程。
灵活性增强,对于新产品的创建,只需多写一个相应的工厂类。
典型的解耦框架。高层模块只需要知道产品的抽象类,无须关心其他实现类,满足迪米特法则、依赖倒置原则和里氏替换原则。

缺点

类的个数容易过多,增加复杂度
增加了系统的抽象性和理解难度
抽象产品只能生产一种产品,此弊端可使用抽象工厂模式解决。

抽象工厂模式

抽象工厂(AbstractFactory)模式的定义:是一种为访问类提供一个创建一组相关或相互依赖对象的接口,且访问类无须指定所要产品的具体类就能得到同族的不同等级的产品的模式结构,这里便出现了两个概念:同族和同等级。

我们用电器生产工厂表示如下:

抽象工厂(Abstract Factory):提供了创建产品的接口,它包含多个创建产品的方法 newProduct(),可以创建多个不同等级的产品。
具体工厂(Concrete Factory):主要是实现抽象工厂中的多个抽象方法,完成具体产品的创建。
抽象产品(Product):定义了产品的规范,描述了产品的主要特性和功能,抽象工厂模式有多个抽象产品。
具体产品(ConcreteProduct):实现了抽象产品角色所定义的接口,由具体工厂来创建,它同具体工厂之间是多对一的关系。

绘制结构图如下:

用代码表示:

/*** @author  hz* @version 1.0*/
public class FarmTest {public static void main(String[] args) {try {Farm f;Animal a;Plant p;//读取相应的配置信息,用于生产工厂f = (Farm) ReadXML.getObject();a = f.newAnimal();p = f.newPlant();a.show();p.show();} catch (Exception e) {System.out.println(e.getMessage());}}
}
//抽象产品:动物类
interface Animal {public void show();
}
//具体产品:马类
class Horse implements Animal {public Horse() {System.out.println("具体马类的生成");}public void show() {System.out.println("执行马类的相应操作");}
}
//具体产品:牛类
class Cattle implements Animal {public Cattle() {//具体牛类的生成System.out.println("具体牛类的生成");}public void show() {System.out.println("执行马类的相应操作");}
}
//抽象产品:植物类
interface Plant {public void show();
}
//具体产品:水果类
class Fruitage implements Plant {public Fruitage() {System.out.println("具体水果类生成");}public void show() {System.out.println("执行水果类的相应操作");}
}
//具体产品:蔬菜类
class Vegetables implements Plant {public Vegetables() {System.out.println("具体蔬菜类生成");}public void show() {System.out.println("执行蔬菜类的相应操作");}
}
//抽象工厂:农场类
interface Farm {public Animal newAnimal();public Plant newPlant();
}
//具体工厂:农场类1
class SGfarm implements Farm {public Animal newAnimal() {System.out.println("新牛出生!");return new Cattle();}public Plant newPlant() {System.out.println("蔬菜长成!");return new Vegetables();}
}
//具体工厂:农场类2
class SRfarm implements Farm {public Animal newAnimal() {System.out.println("新马出生!");return new Horse();}public Plant newPlant() {System.out.println("水果长成!");return new Fruitage();}
}

那么该模式有什么优缺点呢?
优点:

1.可以在类的内部对产品族中相关联的多等级产品共同管理,而不必专门引入多个新的类来进行管理。
2.当需要产品族时,抽象工厂可以保证客户端始终只使用同一个产品的产品组。
3.抽象工厂增强了程序的可扩展性,当增加一个新的产品族时,不需要修改原代码,满足开闭原则。

缺点

当产品族中需要增加一个新的产品时,所有的工厂类都需要进行修改。增加了系统的抽象性和理解难度。

参考文章:
1.设计模式-单例模式
2.JAVA 设计模式系列——工厂模式
3.设计模式系列——抽象工厂模式

软件设计模式“单例模式”和“工厂模式”相关推荐

  1. 设计模式——单例模式、工厂模式

    设计模式--单例模式.工厂模式 一.六大设计原则 开闭原则(Open Close Principle) 开闭原则就是说对扩展开放,对修改关闭.在程序需要进行拓展的时候,不能去修改原有的代码,实现一个热 ...

  2. 软件设计模式之路-----工厂模式抽象工厂模式

    系统开发和现实生活是一样的.在现实生活中一个复杂的商品都是通过一个工厂产生,具体工厂怎么产生的我们不需在乎.在软件设计上也可如此,如果一个对象的创建过程比较复杂,便可考虑使用工厂模式. 简单工厂模式 ...

  3. 详解常用的设计模式——单例模式、工厂模式、代理模式

    设计模式遵循的原则有6个: 开闭原则(Open Close Principle) 对扩展开放,对修改关闭. 里氏代换原则(Liskov Substitution Principle) 只有当衍生类可以 ...

  4. 软件设计模式—单例模式

    前篇--软件设计模式-基础 前篇--软件设计模式-三种工厂模式 前篇--软件设计模式-装饰者模式 单例模式是创建型模式 目录 1.定义及理解 1.1 定义 1.2 特点 1.3 类图 1.3.1结构说 ...

  5. 设计模式----创建型设计模式(单例模式、工厂方法模式、构建者模式)

    创建型设计模式 单例模式(Singleton Pattern) 单例模式介绍 代码演示 饿汉式(静态常量) 饿汉式(静态代码块) 懒汉式(线程不安全) 懒汉式(线程安全,同步方法) 懒汉式(线程安全, ...

  6. 23种设计模式之单例模式、工厂模式、原型模式、建造者模式

    系列文章目录 第一章:程序设计原则-单一职责.接口隔离.依赖倒置.里式替换 第二章:程序设计原则-开闭原则.迪米特法则.合成复用原则 文章目录 系列文章目录 一.设计模式简单介绍 1.1.什么是设计模 ...

  7. 一口气讲完设计模式(单例模式、工厂模式、原型模式、建造者模式、适配器、桥梁模式)

    设计模式 使用设计模式,可以让我们的代码具有更好的可读性.可扩展性.可读性.重用性.符合高内聚低耦合的特点.作为程序员,是我们经常听到的概念,也是我们程序员必须深入学习,了解的知识. 设计模式种类 该 ...

  8. getinstance方法详解_二、设计模式总览及工厂模式详解

    二.架构师内功心法之设计模式 2.架构师内功心法之设计模式 2.1.课程目标 1.通过对本章内容的学习,了解设计模式的由来. 2.介绍设计模式能帮我们解决哪些问题. 3.剖析工厂模式的历史由来及应用场 ...

  9. C#设计模式(2)——简单工厂模式

    一.引言 这个系列也是自己对设计模式的一些学习笔记,希望对一些初学设计模式的人有所帮助的,在上一个专题中介绍了单例模式,在这个专题中继续为大家介绍一个比较容易理解的模式--简单工厂模式. 二.简单工厂 ...

最新文章

  1. bitmap对海量无重复的整数排序--转
  2. Spring Cloud Alibaba 生产实践:Sentinel 进阶应用场景
  3. 多线程的使用——模拟线程池的实现(2015-12-02 00:14:59)
  4. 若所有的参数皆需要类型转换——请为此采用non-member函数
  5. 想爱容易,相处难:当ASP.NET MVC爱上IoC
  6. 高盛发布区块链报告:从理论到实践(中文版)
  7. 节省3500万的背后,运维如何兼顾成本与效率?
  8. 如何使用批处理添加网络打印机
  9. 26岁,发25篇SCI,当上211教授、博导。
  10. java编程double相乘_浅谈Java double 相乘的结果偏差小问题
  11. 【bzoj1604】[Usaco2008 Open]Cow Neighborhoods 奶牛的邻居 旋转坐标系+并查集+Treap/STL-set...
  12. 三菱gxworks3安装失败_三菱gx works3下载 Gx Works3(三菱PLC编程软件) v1.031h 中文安装版(附安装教程+序列号) 下载-脚本之家...
  13. 面试经验|华为二面分享 真难ε=(´ο`*)))唉
  14. Bootstrap下拉单选框,下拉搜索多选框Bootstrap-select
  15. MongoDB 文档的高级查询操作
  16. java 音频 合成_java 音频 合成
  17. 蓝桥杯试题 基础练习 数列特征python
  18. 使用微信小程序新的订阅消息接口,主动推送。
  19. 一天一篇latex刘海洋代码解析:1.2.3填写正文
  20. memory check error at 0x03D70F16 = 0x00, should be 0xFD.

热门文章

  1. strstr函数用法小结
  2. Android 第三方应用接入微信平台(2)
  3. Java SE基础(十六)集合
  4. JQuery jQuey Ajax- POST请求
  5. 零基础转行,你要不要去编程培训班?
  6. 用户满意您的产品吗?20个用户体验调查问题给您答案
  7. 监控摄像头的测试方法
  8. 多目标规划问题的解法
  9. javascript-qrcode.js教程
  10. UltraEdit常用小技巧