一.单例模式
常见的实现方式有:懒汉模式、饥汉模式、双重校验锁、静态内部类、枚举等方式实现,那我们我们紧接着就具体的一个一个的来看看他们的实现:
懒汉模式:
/**
* @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方法线程安全,但是这种方式弊端特别大,因为所有线程到达该方法以后需要进行排队等候,所以对性能的损耗非常大,该处理解决了线程安全安全问题,并没有解决效率问题,如果要既要解决线程安全问题又要解决效率问题则需要我们后面的双重校验锁方式。
懒汉模式将实例化的时机放到了需要使用的时候(饿汉是类加载了就有实例),也就是“延迟加载”,相比饿汉,能避免了在加载的时候实例化有可能用不到的实例,但是问题也很明显,我们要花精力去解决线程安全的问题。
饿汉模式:
/**
* @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;
}
单例模式的优缺点
转载自:https://www.jianshu.com/p/67ede26212be
主要优点;
1.提供了对唯一实例的受控访问;
2;由于系统中内存只存在一个对象,因此可以节约系统的的资源,对于一些频繁的创建和销毁的对象单例模式无意可以提供系统的性能,
3单例模式可以允许可变的数目的实例,使用单利模式进行扩展,使用控制单利对象相似的方法获取指定个数的实例,及解决了单利对象,共享过多,而有损性能的问题;
主要缺点;
1;由于单例模式,不是抽象的所以可扩展性比较差;
2;单例类,职责过重,在一定程度上;违背了单一职责‘
3;滥用单例将带来一些负面的问题,如为了节省资源将数据库连接池对象设计为单例模式,可能会导致共享连接池对象的程序过多未出而出现的连接池溢出,如果实例化对象长时间不用系统就会被认为垃圾对象被回收,这将导致对象状态丢失。
二.工厂模式:
1.简单工厂模式
我们把被创建的对象称为“产品”,把创建产品的对象称为“工厂”。如果要创建的产品不多,只要一个工厂类就可以完成,这种模式叫“简单工厂模式”。
在简单工厂模式中创建实例的方法通常为静态(static)方法
因此简单工厂模式(Simple Factory Pattern)又叫作静态工厂方法模式(Static Factory Method Pattern)。
简单工厂模式包含如下角色:
Factory:工厂角色
Product:抽象产品角色
ConcreteProduct:具体产品角色
工厂角色(Creator):是简单工厂模式的核心,它负责实现创建所有具体产品类的实例。工厂类可以被外界直接调用,创建所需的产品对象。
抽象产品角色(Product):是所有具体产品角色的父类,它负责描述所有实例所共有的公共接口。
具体产品角色(Concrete Product):继承自抽象产品角色,一般为多个,是简单工厂模式的创建目标。工厂类返回的都是该角色的某一具体产品。
用代码实现:
/**
* @author hz
* @version 1.0
*/
public class Client {
//抽象产品
public interface Product {
void show();
}
//具体产品:ProductA
static class ConcreteProduct1 implements Product {
public void show() {
System.out.println("具体产品1显示...");
}
}
//具体产品:ProductB
static 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;
}
}
}
简单工厂模式的优缺点
优点:
工厂类是整个模式的关键.包含了必要的逻辑判断,根据外界给定的信息,决定究竟应该创建哪个具体类的对象.
通过使用工厂类,外界可以从直接创建具体产品对象的尴尬局面摆脱出来,仅仅需要负责“消费”对象就可以了。
而不必管这些对象究竟如何创建及如何组织的.明确了各自的职责和权利,有利于整个软件体系结构的优化。
缺点:
由于工厂类集中了所有实例的创建逻辑,违反了开闭原则,将全部创建逻辑集中到了一个工厂类中;
它所能创建的类只能是事先考虑到的,如果需要添加新的类,则就需要改变工厂类了。
当系统中的具体产品类不断增多时候,可能会出现要求工厂类根据不同条件创建不同实例的需求.
这种对条件的判断和对具体产品类型的判断交错在一起,很难避免模块功能的蔓延,对系统的维护和扩展非常不利;
开闭原则定义:一个软件实体如类、模块和函数应该对扩展开放,对修改关闭。
开放-封闭原则的意思就是说,你设计的时候,时刻要考虑,尽量让这个类是足够好,写好了就不要去修改了,如果新需求来,我们增加一些类就完事了,原来的代码能不动则不动。这个原则有两个特性,一个是说“对于扩展是开放的”,另一个是说“对于更改是封闭的”。面对需求,对程序的改动是通过增加新代码进行的,而不是更改现有的代码。这就是“开放-封闭原则”的精神所在。
2.工厂方法模式
工厂方法模式的主要角色包含:
抽象工厂(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;
}
}
}
工厂方法模式的优缺点
优点:
用户只需要知道具体工厂的名称就可得到所要的产品,无须知道产品的具体创建过程。
灵活性增强,对于新产品的创建,只需多写一个相应的工厂类。
典型的解耦框架。高层模块只需要知道产品的抽象类,无须关心其他实现类,满足迪米特法则、依赖倒置原则和里氏替换原则。
缺点:
类的个数容易过多,增加复杂度
增加了系统的抽象性和理解难度
抽象产品只能生产一种产品,此弊端可使用抽象工厂模式解决。
3.抽象工厂模式
抽象工厂(AbstractFactory)模式的定义:是一种为访问类提供一个创建一组相关或相互依赖对象的接口,且访问类无须指定所要产品的具体类就能得到同族的不同等级的产品的模式结构。
而使用抽象工厂模式需要满足以下条件:
1.系统中有多个产品族,每个具体工厂创建同一族但属于不同等级结构的产品。
2.系统一次只可能消费其中某一族产品,即同族的产品一起使用。
而作为工厂方法模式的升级版,抽象工厂模式的组成同工厂方法模式一样,也是由抽象工厂、具体工厂、抽象产品和具体产品等 四个要素构成,但抽象工厂中方法个数不同,抽象产品的个数也不同。
抽象工厂(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();
}
}
import org.w3c.dom.*;
import javax.xml.parsers.*;
import java.io.File;
/**
* @author hz
* @version 1.0
*/
public class ReadXML2 {
public static Object getObject() {
try {
DocumentBuilderFactory dFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = dFactory.newDocumentBuilder();
Document doc;
doc = builder.parse(new File("src/AbstractFactory/config.xml"));
NodeList nl = doc.getElementsByTagName("className");
Node classNode = nl.item(0).getFirstChild();
String cName = "AbstractFactory." + classNode.getNodeValue();
System.out.println("新类名:" + cName);
Class<?> c = Class.forName(cName);
Object obj = c.newInstance();
return obj;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
抽象工厂模式的优缺点
抽象工厂模式的优点:
抽象工厂模式除了具有工厂方法模式的优点外,最主要的优点就是可以在类的内部对产品族进行约束。所谓的产品族,一般或多或少的都存在一定的关联,抽象工厂模式就可以在类内部对产品族的关联关系进行定义和描述,而不必专门引入一个新的类来进行管理。
抽象工厂模式的缺点:
产品族的扩展将是一件十分费力的事情,假如产品族中需要增加一个新的产品,则几乎所有的工厂类都需要进行修改。所以使用抽象工厂模式时,对产品等级结构的划分是非常重要的。