架构师内功心法:设计模式(二)创建型模式(二)
写在前面:
- 你好,欢迎关注!
- 我热爱技术,热爱分享,热爱生活, 我始终相信:技术是开源的,知识是共享的!
- 博客里面的内容大部分均为原创,是自己日常的学习记录和总结,便于自己在后面的时间里回顾,当然也是希望可以分享 自己的知识。如果你觉得还可以的话不妨关注一下,我们共同进步!
- 个人除了分享博客之外,也喜欢看书,写一点日常杂文和心情分享,如果你感兴趣,也可以关注关注!
- 公众号:傲骄鹿先生
目录
四、 建造者模式(Build Pattern)
-
Director: 指挥者类,用于统一组装流程
-
Builder:抽象Builder类,规范产品的组建,一般是由子类实现。
-
ConcreteBulider: 抽象Builder类的实现类,实现抽象Builder类定义的所有方法,并且返回一个组建好的对象
-
Product: 产品类
public class Computer {
private String mCpu;
private String mMainboard;
private String mRam;
public void setmCpu(String mCpu) {
this.mCpu = mCpu;
}
public void setmMainboard(String mMainboard) {
this.mMainboard = mMainboard;
}
public void setmRam(String mRam) {
this.mRam = mRam;
}
}
public abstract class Builder {
public abstract void buildCpu(String cpu);
public abstract void buildMainboard(String mainboard);
public abstract void buildRam(String ram);
public abstract Computer create();
}
(3)具体Builder类
商家实现了抽象的Builder类,MoonComputerBuilder类用于组装电脑:
public class MoonComputerBuilder extends Builder {
private Computer mComputer = new Computer();
@Override public void buildCpu(String cpu) {
mComputer.setmCpu(cpu);
}
@Override
public void buildMainboard(String mainboard) {
mComputer.setmMainboard(mainboard);
}
@Override
public void buildRam(String ram) {
mComputer.setmRam(ram);
}
@Override
public Computer create() {
return mComputer;
}
}
(4)具体指挥者(Director)类
public class Direcror {
Builder mBuild=null;
public Direcror(Builder build){
this.mBuild=build;
}
public Computer CreateComputer(String cpu,String mainboard,String ram){
//规范建造流程
this.mBuild.buildMainboard(mainboard);
this.mBuild.buildCpu(cpu);
this.mBuild.buildRam(ram);
return mBuild.create();
}
}
(5)客户端调用指挥者(Director)类
public class CreatComputer {
public static void main(String[]args){
Builder mBuilder=new MoonComputerBuilder();
Direcror mDirecror=new Direcror(mBuilder);
//组装电脑
mDirecror.CreateComputer("i7-6700","华擎玩家至尊","三星DDR4");
}
}
3、优点和缺点
(1)优点
-
使用建造者模式可以使客户端不必知道产品内部组成的细节。
-
具体的建造者类之间是相互独立的,容易扩展。
-
由于具体的建造者是独立的,因此可以对建造过程逐步细化,而不对其他的模块产生任何影响。
(2)缺点
-
产生多余的Build对象以及Dirextor类。
4、适用场景
-
当创建复杂对象的算法应该独立于该对象的组成部分以及它们的装配方式时。
-
相同的方法,不同的执行顺序,产生不同的事件结果时。
-
多个部件或零件,都可以装配到一个对象中,但是产生的运行结果又不相同时。
-
产品类非常复杂,或者产品类中的调用顺序不同产生了不同的效能。
-
创建一些复杂的对象时,这些对象的内部组成构件间的建造顺序是稳定的,但是对象的内部组成构件面临着复杂的变化。
五、 单例模式(Singleton Pattern)
-
构造函数不对外开放---Private
-
通过一个静态方法或者枚举返回单利对象
-
确保单例类的对象有且只有一个,尤其是在多线程环境下。
-
确保单例类对象在反序列化时不会重新构建对象。
-
JVM在类的初始化阶段(即 在Class被加载后、被线程使用前),会执行类的初始化
-
在执行类的初始化期间,JVM会去获取一个锁。这个锁可以同步多个线程对同一个类的初始化
public class HungrySingleton {
private static final HungrySingleton mSingleton= new HungrySingleton();
//私有构造函数
private HungrySingleton(){
}
//公有的静态函数,对外暴露获取单例对象的接口
public static HungrySingleton getSingleton(){
return mSingleton;
}
}
(2)枚举类型
public enum Singleton{
//定义1个枚举的元素,即为单例类的1个实例
INSTANCE;
// 隐藏了1个空的、私有的 构造方法
// private Singleton () {}
}
// 获取单例的方式:
Singleton singleton = Singleton.INSTANCE;
class Singleton {
// 1. 类加载时,先不自动创建单例
// 即,将单例的引用先赋值为 Null
private static Singleton ourInstance = null;
// 2. 构造函数 设置为 私有权限
// 原因:禁止他人创建实例
private Singleton() {
}
// 3. 需要时才手动调用 newInstance() 创建 单例
public static Singleton newInstance() {
// 先判断单例是否为空,以避免重复创建
if( ourInstance == null){
ourInstance = new Singleton();
}
return ourInstance;
}
}
基础实现的懒汉式是线程不安全的,具体原因如下:
-
即,getInstance()方法块只能运行在1个线程中
-
若该段代码已在1个线程中运行,另外1个线程试图运行该块代码,则 会被阻塞而一直等待
-
而在这个线程安全的方法里我们实现了单例的创建,保证了多线程模式下 单例对象的唯一性
// 写法1
class Singleton {
// 1. 类加载时,先不自动创建单例
// 即,将单例的引用先赋值为 Null
private static Singleton ourInstance = null;
// 2. 构造函数 设置为 私有权限
// 原因:禁止他人创建实例
private Singleton() {
}
// 3. 加入同步锁
public static synchronized Singleton getInstance(){
// 先判断单例是否为空,以避免重复创建
if ( ourInstance == null )
ourInstance = new Singleton();
return ourInstance;
}
}
// 写法2// 该写法的作用与上述写法作用相同,只是写法有所区别
class Singleton{
private static Singleton instance = null;
private Singleton(){
}
public static Singleton getInstance(){
// 加入同步锁
synchronized(Singleton.class) {
if (instance == null)
instance = new Singleton();
}
return instance;
}
}
(3)懒汉式——双重锁优化
class Singleton {
private static Singleton ourInstance = null;
private Singleton() {}
public static Singleton newInstance() {
// 加入双重校验锁
// 校验锁1:第1个if
if( ourInstance == null){ // ①
synchronized (Singleton.class){ // ②
// 校验锁2:第2个 if
if( ourInstance == null){
ourInstance = new Singleton();
}
}
}
return ourInstance;
}
}
检验锁1: 第一个if
-
作用:若单例已创建,则直接返回已创建的单例,无需再执行加锁操作。即直接跳到执行 return ourInstance
-
作用:防止多次创建单例问题
-
原理:
-
线程A调用newInstance(),当运行到②位置时,此时线程B也调用了newInstance()
-
因线程A并没有执行instance = new Singleton();,此时instance仍为空,因此线程B能突破第1层if 判断,运行到①位置等待synchronized中的A线程执行完毕
-
当线程A释放同步锁时,单例已创建,即instance已非空
-
此时线程B 从①开始执行到位置②。此时第2层if判断 = 为空(单例已创建),因此也不会创建多余的实例
-
public class StaticInnerSingleton {
private StaticInnerSingleton(){}
public static StaticInnerSingleton getInstance(){
return InstanceHolder .mStaticInnerSingleton;
}
//静态内部类
private static class InstanceHolder {
private static StaticInnerSingleton mStaticInnerSingleton=new StaticInnerSingleton();
}
}
JVM在类的初始化阶段(即在Class被加载后,且被线程使用之前),会执行类的初始化。在执行类的初始化期间,JVM会去获取一个锁。这个锁可以同步多个线程对同一个类的初始化。
public class SingletonManager {
private static Map<String, Object> objectMap = new HashMap<>();
private SingletonManager() {
}
public static void registerService(String key, Object instance) {
if (!objectMap.containsKey(key)) {
objectMap.put(key, instance);
}
}
public static Object getService(String key) {
return objectMap.get(key);
}
}
在程序的初始,将多种单例类型注入到一个统一的管理类中,在使用时根据key获取对象对应类型的对象。
public class SerializableSingleton implements Serializable {
private static final long serialVersionUID=0L;
private static final SerializableSingleton INSTANCE =new SerializableSingleton();
private SerializableSingleton(){}
public static SerializableSingleton getSerializableSingleton(){
return INSTANCE;
}
private Object readResolve() throws ObjectStreamException{
return INSTANCE;
}
}
readResolve方法中将单例对象返回,而不是重新生成一个新的对象。而对于枚举则不存在这个问题。
六、原型模式( Prototype Pattern)
public interface Prototype{
/**
* 克隆自身的方法
* @return 一个从自身克隆出来的对象
*/
public Object clone();
}
具体原型:
public class ConcretePrototype1 implements Prototype {
public Prototype clone(){
//最简单的克隆,新建一个自身对象,由于没有属性就不再复制值了
Prototype prototype = new ConcretePrototype1();
return prototype;
}
}
public class ConcretePrototype2 implements Prototype {
public Prototype clone(){
//最简单的克隆,新建一个自身对象,由于没有属性就不再复制值了
Prototype prototype = new ConcretePrototype2();
return prototype;
}
}
客户端角色:
public class Client {
//持有需要使用的原型接口对象
private Prototype prototype;
//构造方法,传入需要使用的原型接口对象
public Client(Prototype prototype){
this.prototype = prototype;
}
public void operation(Prototype example){
//需要创建原型接口的对象
Prototype copyPrototype = prototype.clone();
}
}
2、登记形式的原型模式
作为原型模式的第二种形式,它多了一个原型管理器(PrototypeManager)角色,该角色的作用是:创建具体原型类的对象,并记录每一个被创建的对象。
(2)简单实现
抽象原型角色:
public interface Prototype{
public Prototype clone();
public String getName();
public void setName(String name);
}
具体原型角色:
public class ConcretePrototype1 implements Prototype {
private String name;
public Prototype clone(){
ConcretePrototype1 prototype = new ConcretePrototype1();
prototype.setName(this.name);
return prototype;
}
public String toString(){
return "Now in Prototype1 , name = " + this.name;
}
@Override
public String getName() {
return name;
}
@Override
public void setName(String name) {
this.name = name;
}
}
public class ConcretePrototype2 implements Prototype {
private String name;
public Prototype clone(){
ConcretePrototype2 prototype = new ConcretePrototype2();
prototype.setName(this.name);
return prototype;
}
public String toString(){
return "Now in Prototype2 , name = " + this.name;
}
@Override
public String getName() {
return name;
}
@Override
public void setName(String name) {
this.name = name;
}
}
原型管理器角色保持一个聚集,作为对所有原型对象的登记,这个角色提供必要的方法,供外界增加新的原型对象和取得已经登记过的原型对象。
public class PrototypeManager {
/**
* 用来记录原型的编号和原型实例的对应关系
*/
private static Map<String,Prototype> map = new HashMap<String,Prototype>();
/**
* 私有化构造方法,避免外部创建实例
*/
private PrototypeManager(){}
/**
* 向原型管理器里面添加或是修改某个原型注册
* @param prototypeId 原型编号
* @param prototype 原型实例
*/
public synchronized static void setPrototype(String prototypeId , Prototype prototype){
map.put(prototypeId, prototype);
}
/**
* 从原型管理器里面删除某个原型注册
* @param prototypeId 原型编号
*/
public synchronized static void removePrototype(String prototypeId){
map.remove(prototypeId);
}
/**
* 获取某个原型编号对应的原型实例
* @param prototypeId 原型编号
* @return 原型编号对应的原型实例
* @throws Exception 如果原型编号对应的实例不存在,则抛出异常
*/
public synchronized static Prototype getPrototype(String prototypeId) throws Exception{
Prototype prototype = map.get(prototypeId);
if(prototype == null){
throw new Exception("您希望获取的原型还没有注册或已被销毁");
}
return prototype;
}
}
客户端角色:
public class Client {
public static void main(String[]args){
try{
Prototype p1 = new ConcretePrototype1();
PrototypeManager.setPrototype("p1", p1);
//获取原型来创建对象
Prototype p3 = PrototypeManager.getPrototype("p1").clone();
p3.setName("张三");
System.out.println("第一个实例:" + p3);
//有人动态的切换了实现
Prototype p2 = new ConcretePrototype2();
PrototypeManager.setPrototype("p1", p2);
//重新获取原型来创建对象
Prototype p4 = PrototypeManager.getPrototype("p1").clone();
p4.setName("李四");
System.out.println("第二个实例:" + p4);
//有人注销了这个原型
PrototypeManager.removePrototype("p1");
//再次获取原型来创建对象
Prototype p5 = PrototypeManager.getPrototype("p1").clone();
p5.setName("王五");
System.out.println("第三个实例:" + p5);
}catch(Exception e){
e.printStackTrace();
}
}
}
3、优点和缺点