【设计模式】一、创建型模式:单例,工厂,建造者,原型

1 单例模式

定义:确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例

1.1饿汉+线程安全

package com.simple.gof.singleton;

/**
 * 饿汉式线程安全
 * </p>
 * 优点:没有加锁,执行效率会提高。
 * </p>
 * 缺点:类加载时就初始化,浪费内存。
 * </p>
 * 
 * @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!");
    }
}

1.2懒汉+线程安全

package com.simple.gof.singleton;

/**
 * 懒汉式线程安全
 * </p>
 * 这种方式采用双锁机制,安全且在多线程情况下能保持高性能。
 * 
 * @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;
    }
}

1.3懒汉+线程安全+内部类

package com.simple.gof.singleton;

/**
 * 懒汉+线程安全+静态内部类
 * </p>
 * 利用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;
    }
}

1.4饿汉+线程安全+枚举

package com.simple.gof.singleton;
/**
 * 饿汉+线程安全+枚举
 * </p>
 * 简洁,自动支持序列化机制,绝对防止多次实例化
 * @author chaozai
 * @date 2018年8月20日
 *
 */
public enum EnumLazySingleton {  
    INSTANCE;  
    public void whateverMethod() {  
    }  
}

总结:建议使用第1 种饿汉方式。只有在要明确实现 lazy loading 效果时,才会使用第 3种内部类方式。如果涉及到反序列化创建对象时,可以尝试使用第 4 种枚举方式。如果有其他特殊的需求,可以考虑使用第 2种双检锁方式。

使用场景:1、唯一序列号,机器ID等;2、某些和配置关联工具类(一套配置一个单例);3、全局共享数据

2工厂模式

2.1简单工厂

定义:在工厂类中通过不同类型产生不同对象,一个模块仅需要一个工厂类

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 extends IProduct> T getProduct(Class type) {
	IProduct product = null;
	try {
	    product = (IProduct) Class.forName(type.getName()).newInstance();
	} catch (Exception e) {
	}
	return (T) product;
    }
}

2.2工厂方法

定义:定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。

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;

2.3抽象工厂

定义:为创建一组相关或相互依赖的对象提供一个接口,而且无须指定它们的具体类。并通过组合方式被其他对象集成使用。

package com.simple.gof.factory.customer;

import com.simple.gof.factory.AbstractFactory;

/**
 * 产品消费者(抽象工厂思想核心)
 * </p>
 * 通过组合抽象工厂,在子类中使用抽象工厂来产生对应产品族
 * 
 * @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、特定场景约束

3建造者模式

定义:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。

说明:一个复杂对象可能由多个简单对象构成,构成顺序不同或者简单对象实现不同,所获得的复杂对象结果也不同。

建造者:

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<BaseItem> items = new ArrayList<BaseItem>();    
    /**
     * 添加奶茶基础项
     * @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、产品类非常复杂,或者产品类中的调用顺序不同产生了不同的效能;

4原型模式

定义:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。原型模式实际上就是实现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<String>)this.arrayList.clone();

样例源码地址:https://github.com/qqchaozai/gof.git


爱家人,爱生活,爱设计,爱编程,拥抱精彩人生!

猜你喜欢

转载自blog.csdn.net/qqchaozai/article/details/81868013