設計模式之原型模式

原型模式

1.引出原型模式的思路

  • Java中的Object類是全部類的根類,Object類提供了一個clone()方法,該方法能夠將一個java對象複製一份,須要實現clone的類要實現Cloneable接口,該接口表示該類可以複製並且具備複製的能力

2.原型模式(Prototype)的介紹

  • 用原型實例指定建立對象的種類,而且經過拷貝這些原型來建立對象
  • 原型模式容許一個對象建立另外一個可定製的對象,無需知道建立的細節
  • 工做原理:經過將一個原型對象傳給要發動建立的對象,這個要發動建立的對象經過請求原型模型的拷貝來完成對象的建立,即原型模式.clone()

3.原理結構圖UML圖

  • Prototype:原型類,聲明一個克隆本身的接口
  • ConcretePrototype:具體的圓形類,實現一個克隆本身的操做
  • client:發動建立的對象,讓一個原型對象克隆本身,從而建立一個新的對象(屬性同樣)

4.代碼示例

  • 實現cloneable接口,重寫clone方法;注意,不重寫的話也能夠獲取到對象,可是獲取到的和發動建立的對象是同一個對象,即只是克隆了引用,hashCode是同樣的java

    public class Sheep implements Cloneable{
    	private String name;
    	private int age;
    //getter、setter、構造器
        ..................
    	
    	//重寫克隆方法:調用的是父類默認的clone方法來拷貝sheep類
    	@Override
    	protected Object clone(){
    		Object sheep = null;
    		try {
    			sheep = super.clone();
    		} catch (CloneNotSupportedException e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		}
    		// TODO Auto-generated method stub
    		return sheep;
    	}	
    }
  • 發動克隆spring

    public class client {
       public static void main(String[] args) {
    	Sheep sheep = new Sheep("Tom", 2);
    	Sheep sheep2 = (Sheep)sheep.clone();
    	
    	System.out.println(sheep.hashCode());   //366712642
    	System.out.println(sheep2.hashCode());   //1829164700
    	
    }

5.原型模式在Spring中的使用

  • Spring中配置bean的時候,scope屬性能夠配置一個prototype值,該值指定該bean的建立是使用原型模式數組

  • 在建立ioc容器後,經過getBean()獲取bean對象時,往裏追能夠發如今核心方法處spring對bean的scope屬性進行了判斷,配置了prototype時,進入了原型模式的使用安全

    建立ioc的時候建立了一個這樣的容器實例
    ide

    該類中對getBean()方法的定義以下ui

    public Object getBean(String name) throws BeansException {
        this.assertBeanFactoryActive();
        //咱們先進入getBeanFactory()方法,看看獲得的是哪一個BeanFactory,再尋找他的getBean()方法
        return this.getBeanFactory().getBean(name);
    }
    
    //追蹤發現這是一個抽象方法,應該是由AbstractApplicationContext的子類實現
    public abstract ConfigurableListableBeanFactory getBeanFactory() throws IllegalStateException;

    往下找getBeanFactory()方法的實現this

    發如今AbstractRefreshableApplicationContext中實現了這個方法,返回的是一個DefaultListableBeanFactory,也就是調用了DefaultListableBeanFactory的getBean()方法prototype

    private DefaultListableBeanFactory beanFactory;
    //實現了getBeanFactory方法,確保了線程安全的前提下返回了一個DefaultListableBeanFactory
    public final ConfigurableListableBeanFactory getBeanFactory() {
        synchronized(this.beanFactoryMonitor) {
            if (this.beanFactory == null) {
                throw new IllegalStateException("BeanFactory not initialized or already closed - call 'refresh' before accessing beans via the ApplicationContext");
            } else {
                return this.beanFactory;
            }
        }
    }

    去到DefaultListableBeanFactory中沒有找到getBean()方法,因而往他的父類去找線程

    從這個bean的父類的父類AbstractBeanFactory能夠看到這個getBean(),同時調用了核心方法doGetBean();3d

    public Object getBean(String name) throws BeansException {
        return this.doGetBean(name, (Class)null, (Object[])null, false);
    }

    進入到doGetBean()方法能夠發現,spring對參數進行了判斷,對應建立了原型模式的對象

    protected <T> T doGetBean(String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly) throws BeansException {
        String beanName = this.transformedBeanName(name);
        Object sharedInstance = this.getSingleton(beanName);
       			.................
    
                if (mbd.isSingleton()) {
                    sharedInstance = this.getSingleton(beanName, () -> {
                        try {
                            return this.createBean(beanName, mbd, args);
                        } catch (BeansException var5) {
                            this.destroySingleton(beanName);
                            throw var5;
                        }
                    });
                    bean = this.getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
                //判斷了是否設置了原型模式
                } else if (mbd.isPrototype()) {
                    var11 = null;
    
                    Object prototypeInstance;
                    try {
                        this.beforePrototypeCreation(beanName);
                	    //進入了原型模式的對象建立
                        prototypeInstance = this.createBean(beanName, mbd, args);
                    } finally {
                        this.afterPrototypeCreation(beanName);
                    }
    
                    bean = this.getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
                } else {
                    ...................

    同時能夠發現AbstractBeanFactory也是BeanFactory的實現類

6.關於原型模式的淺拷貝與深拷貝

1.淺拷貝

  • 對於數據類型是基本數據類型的成員變量,淺拷貝會直接進行值傳遞,也就是將該屬性值複製一份給新的對象屬性

  • 對於數據類型是引用類型的成員變量(對象、數組等),淺拷貝會進行引用傳遞,即將該成員變量實例的引用地址複製到新的對象屬性中,新對象屬性中的成員變量所指向的仍是和原型成員變量所指向的是同一個實例,在一個對象中修改爲員變量會影響到其它對象中的成員變量

  • 淺拷貝就是使用Object默認的clone()來實現的

2.深拷貝

  • 複製全部基本類型的成員變量值

  • 爲全部引用數據類型的變量申請存儲空間,並複製每一個引用數據類型成員變量所引用的對象,也就是說,對象進行深拷貝要對整個對象進行拷貝

  • 實現方式

    • 重寫clone方法來實現深拷貝

      public class DeepCloneableTarget implements Serializable,Cloneable{
      	
      	
      	private static final long serialVersionUID= 1L;
      	
      	private String cloneName;
      	private String cloneClass;
       .......................   
      }
      
      
      public class DeepPrototype implements Cloneable{
      	
      	private String name;
      	private DeepCloneableTarget deepCloneableTarget;//引用類型
      	public DeepPrototype() {
      		// TODO Auto-generated constructor stub
      		super();
      	}
      //深拷貝:方式一:重寫clone()
      	@Override
      	protected Object clone() throws CloneNotSupportedException {
      		// TODO Auto-generated method stub
      		Object deep = null;
      		DeepPrototype deepPrototype = (DeepPrototype)deep;
      		deepPrototype.deepCloneableTarget = (DeepCloneableTarget)deepCloneableTarget.clone();
      		
      		return deepPrototype;
      	}

      這個方法不推薦,當有多個引用數據類型時,代碼量特別多,並且複雜

    • 經過對象序列化l實現深拷貝(拷貝的類繼承Serializable,還有內部成員變量時對象的類也要繼承Serializable

      //深拷貝:方式二:經過對象的序列化實現
      	public Object deepClone() {
      		//建立流
      		ByteArrayOutputStream bos = null;
      		ObjectOutputStream oos = null;
      		ByteArrayInputStream bis =null;
      		ObjectInputStream ois = null;
      		
      		try {
      			//序列化
      			bos = new ByteArrayOutputStream();
      			oos = new ObjectOutputStream(bos);
      			oos.writeObject(this);//當前的對象以流的方式輸出
      			
      			//反序列化
      			bis = new ByteArrayInputStream(bos.toByteArray());
      			ois = new ObjectInputStream(bis);
      			DeepPrototype deepPrototype = (DeepPrototype)ois.readObject();
      			
      			return deepPrototype;
      			
      		} catch (Exception e) {
      			// TODO: handle exception
      			e.printStackTrace();
      			return null;
      		}finally {
      			try {
      				bos.close();
      				oos.close();
      				bis.close();
      				ois.close();
      			} catch (Exception e2) {
      				// TODO: handle exception
      			}
      		}
      		
      	}

      推薦使用該方法,由於該方法直接將整個對象進行流操做,將整個對象序列輸出,再反序列輸入,就會自動拷貝一個對象,缺陷在於流操做效率較低,並且基本數據類型也要進行流操做。

小結

  • 建立新的對象比較複雜時,能夠利用原型模式簡化對象的建立過程,同時提升效率
  • 不是初始化對象,而是能夠動態的克隆當前對象以及他的狀態
  • 若是原始對象發生改變,其餘克隆對象的也會發生相應的變化,無需修改代碼
  • 在實現深克隆的時候可能須要比較複雜的代碼
  • 缺陷:須要爲每個類配備一個克隆方法,對於已經存在的類來講得修改源代碼,這違背了ocp原則
相關文章
相關標籤/搜索