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

1 单例模式

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

1.1饿汉+线程安全

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!"); } }

1.2懒汉+线程安全

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; } }

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

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; } }

1.4饿汉+线程安全+枚举

以上都是通过私有构造器来限制外部来创建对象,然而用户是可以通过反射机制来调用私有构造器的,从而一定程度上破坏了单例,同理反序列化时通过私有无参构造函数构建对象,也会出现一个非单例对象,也破坏了单例。当然可以通过添加构造函数权限效验以及自定义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、全局共享数据

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 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;

/**
 * 产品消费者(抽象工厂思想核心)
 * 

* 通过组合抽象工厂,在子类中使用抽象工厂来产生对应产品族 * * @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 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、产品类非常复杂,或者产品类中的调用顺序不同产生了不同的效能;

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)this.arrayList.clone();

 

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


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

你可能感兴趣的:(设计模式)