定义:确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例
package com.simple.gof.singleton;
/**
* 饿汉式线程安全
*
* 优点:没有加锁,执行效率会提高。
*
* 缺点:类加载时就初始化,浪费内存。
*
*
* @author chaozai
* @date 2018年8月20日
*
*/
public class Singleton {
// 创建 Singleton 的一个对象
private static Singleton instance = new Singleton();
// 让构造函数为 private,这样该类就不会被实例化
private Singleton() {
}
// 获取唯一可用的对象
public static Singleton getInstance() {
return instance;
}
public void showMessage() {
System.out.println("Hello World!");
}
}
package com.simple.gof.singleton;
/**
* 懒汉式线程安全
*
* 这种方式采用双锁机制,安全且在多线程情况下能保持高性能。
*
* @author chaozai
* @date 2018年8月20日
*
*/
public class LazySafeSingleton {
private volatile static LazySafeSingleton singleton;
private LazySafeSingleton() {
}
public static LazySafeSingleton getSingleton() {
if (singleton == null) {
synchronized (LazySafeSingleton.class) {
if (singleton == null) {
singleton = new LazySafeSingleton();
}
}
}
return singleton;
}
}
package com.simple.gof.singleton;
/**
* 懒汉+线程安全+静态内部类
*
* 利用classloader机制,只有通过显式调用 getInstance 方法时,才会显式装载 SingletonHolder类,从而实例化 instance
*
* @author chaozai
* @date 2018年8月20日
*
*/
public class InnerClassLazySingleton {
private static class SingletonHolder {
private static final InnerClassLazySingleton INSTANCE = new InnerClassLazySingleton();
}
private InnerClassLazySingleton() {
}
public static final InnerClassLazySingleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
以上都是通过私有构造器来限制外部来创建对象,然而用户是可以通过反射机制来调用私有构造器的,从而一定程度上破坏了单例,同理反序列化时通过私有无参构造函数构建对象,也会出现一个非单例对象,也破坏了单例。当然可以通过添加构造函数权限效验以及自定义readResolve方法(返回单例对象)来解决类似问题。
枚举类本质上是一个实现了Enum静态类的对象,Enum会对调用构造函数的调用方进行判断,是否是枚举类本身在调用,如果不是则抛出异常,同时它也解决了反序列化问题。
package com.simple.gof.singleton;
/**
* 饿汉+线程安全+枚举
*
* 简洁,自动支持序列化机制,绝对防止多次实例化
* @author chaozai
* @date 2018年8月20日
*
*/
public enum EnumLazySingleton {
INSTANCE;
public void whateverMethod() {
}
}
总结:无序列化要求,且不care外部反射破坏的,可以使用第1 种饿汉方式。在需要且可以节约内存资源时,可以使用第 3种内部类方式。如果涉及到反序列化创建对象时,建议使用第 4 种枚举方式。如果有其他特殊的需求,可以考虑使用第 2种双检锁方式。
使用场景:1、唯一序列号,机器ID等;2、某些和配置关联工具类(一套配置一个单例);3、全局共享数据
定义:在工厂类中通过不同类型产生不同对象,一个模块仅需要一个工厂类
package com.simple.gof.factory;
import com.simple.gof.factory.product.IProduct;
import com.simple.gof.factory.product.ProductA;
import com.simple.gof.factory.product.ProductB;
/**
* 简单工厂:在工厂类中通过不同类型产生不同对象
*
* @author chaozai
* @date 2018年8月20日
*
*/
public class SimpleFactory {
/**
*
* @param type
* @return
*/
public IProduct getProduct(String type){
if("A".equals(type)){
return new ProductA();
}else if("B".equals(type)){
return new ProductB();
}
return null;
}
/**
*
* @param type
* @return
*/
public T getProduct(Class type) {
IProduct product = null;
try {
product = (IProduct) Class.forName(type.getName()).newInstance();
} catch (Exception e) {
}
return (T) product;
}
}
定义:定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。
package com.simple.gof.factory;
import com.simple.gof.factory.product.IProductExt;
import com.simple.gof.factory.product.IProduct;
/**
* 抽象工厂类,由子工厂类决定产生产品方式
* @author chaozai
* @date 2018年8月20日
*
*/
public abstract class AbstractFactory {
abstract IProduct getProduct(String type);
abstract IProductExt getProductExt(String type);
}
package com.simple.gof.factory;
import com.simple.gof.factory.product.IProductExt;
import com.simple.gof.factory.product.IProduct;
/**
* 创建product相关联的一系列产品族(A)
* @author chaozai
* @date 2018年8月20日
*
*/
public class ProductAFactory extends AbstractFactory{
@Override
IProduct getProduct(String type) {
return null;
}
@Override
IProductExt getProductExt(String type) {
return null;
}
}
问:怎么一个工厂里面还创建多个对象实例呢?这不是抽象工厂模式么?
答:通过在工厂子类中做不同类型产品的实例化,就是工厂方法模式的核心思想。抽象工厂使用了该思想,但这并不是抽象工厂模式,不是说有能产生多个关联产品族就代表这是抽象工厂模式(和很多博文所说相悖哦,但这就是我的理解)。
使用场景:1、数据库连接JDBC;2、不同缓存方式redis,bdb;
定义:为创建一组相关或相互依赖的对象提供一个接口,而且无须指定它们的具体类。并通过组合方式被其他对象集成使用。
package com.simple.gof.factory.customer;
import com.simple.gof.factory.AbstractFactory;
/**
* 产品消费者(抽象工厂思想核心)
*
* 通过组合抽象工厂,在子类中使用抽象工厂来产生对应产品族
*
* @author chaozai
* @date 2018年8月20日
*
*/
public abstract class ProductCustomer {
/**
* 抽象工厂
*/
AbstractFactory factory;
public ProductCustomer(AbstractFactory factory) {
this.factory = factory;
}
/**
* 子类通过factory使用对应产品族
*/
abstract void doSomethingWithFactory();
}
使用场景:1、不同操作系统;2、特定场景约束
定义:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
说明:一个复杂对象可能由多个简单对象构成,构成顺序不同或者简单对象实现不同,所获得的复杂对象结果也不同。
建造者:
package com.simple.gof.builder;
import com.simple.gof.builder.item.MilkItem;
import com.simple.gof.builder.item.PearlItem;
import com.simple.gof.builder.item.TeaItem;
/**
* 奶茶建造者
*
* @author chaozai
* @date 2018年8月20日
*
*/
public class MilkTeaBuilder {
/**
* 创建纯茶
* @return
*/
public MilkTea prepareTea() {
MilkTea milkTea = new MilkTea();
milkTea.addItem(new TeaItem());
return milkTea;
}
/**
* 创建奶茶
* @return
*/
public MilkTea prepareMilkTea() {
MilkTea milkTea = new MilkTea();
milkTea.addItem(new MilkItem());
milkTea.addItem(new TeaItem());
return milkTea;
}
/**
* 创建珍珠奶茶
* @return
*/
public MilkTea preparePearlMilkTea() {
MilkTea milkTea = new MilkTea();
milkTea.addItem(new MilkItem());
milkTea.addItem(new TeaItem());
milkTea.addItem(new PearlItem());
return milkTea;
}
}
被建造对象:
package com.simple.gof.builder;
import java.util.ArrayList;
import java.util.List;
import com.simple.gof.builder.item.BaseItem;
/**
* 奶茶
* @author chaozai
* @date 2018年8月20日
*
*/
public class MilkTea {
private List items = new ArrayList();
/**
* 添加奶茶基础项
* @param item
*/
public void addItem(BaseItem item){
items.add(item);
}
/**
* 获取总价格
* @return
*/
public float getCost(){
float cost = 0.0f;
for (BaseItem item : items) {
cost += item.price();
}
return cost;
}
/**
* 展示奶茶包含内容
*/
public void showItems(){
for (BaseItem item : items) {
System.out.print("Item : "+item.name());
System.out.println(", Price : "+item.price());
}
}
}
总结:可以通过抽象工厂来创建各个简单产品项;通过装饰者模式也可以创建类似复杂对象
使用场景:1、产品类非常复杂,或者产品类中的调用顺序不同产生了不同的效能;
定义:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。原型模式实际上就是实现Cloneable接口,重写clone()方法,或者自定义拷贝对象方法,如将对象序列化后再反序列化输出。
package com.simple.gof.prototype;
/**
* 实现Cloneable接口
*
* @author chaozai
* @date 2018年8月20日
*
*/
public class ProtoType implements Cloneable {
private static ProtoType protoType = new ProtoType();
/**
* 获取克隆对象
* @return
*/
public static ProtoType getCloneProtoType() {
ProtoType cloneProtoType = (ProtoType) protoType.clone();
return cloneProtoType;
}
@Override
public ProtoType clone() {
ProtoType clone = null;
try {
clone = (ProtoType) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return clone;
}
}
总结:原型模式是内存二进制流的拷贝,要比直接new一个对象性能好很多,特别是要在一个循环体内产生大量的对象时使用,性能更好。
使用场景:
● 资源优化场景
类初始化需要消化非常多的资源,这个资源包括数据、硬件资源等。
● 性能和安全要求的场景
通过new产生一个对象需要非常繁琐的数据准备或访问权限,则可以使用原型模式。
● 一个对象多个修改者的场景
一个对象需要提供给其他对象访问,而且各个调用者可能都需要修改其值时,可以考虑使用原型模式拷贝多个对象供调用者使用。
浅拷贝和深拷贝:
都是对象拷贝,非引用拷贝(拷贝引用指向同一内存地址),会创建新的对象。
浅拷贝:Object类提供的方法clone只是拷贝本对象,其对象内部的数组、引用对象等都不拷贝,还是指向原生对象的内部元素地址,这些引用对象会被拷贝对象和原对象共享,其他的原始类型比如int、long、char、string(当做是原始类型)等都会被拷贝。
注意:使用原型模式时,引用的成员变量必须满足两个条件才不会被拷贝:一是类的成员变量,而不是方法内变量;二是必须是一个可变的引用对象,而不是一个原始类型或不可变对象。
深拷贝:对私有的类变量进行独立的拷贝,类中所有对象全拷贝
如:thing.arrayList = (ArrayList
样例源码地址:https://github.com/qqchaozai/gof.git
爱家人,爱生活,爱设计,爱编程,拥抱精彩人生!