1. 程式人生 > >反射與工廠模式之間不可告人的祕密

反射與工廠模式之間不可告人的祕密

認識反射機制

思維導圖

初識反射
反射是指物件的反向操作,既然是反向操作處理。那先來觀察一下正向的操作。

import java.util.Date;

public static void main (String[] args){
    Date date = new Date();
    }
}

以上便是我們一般關於物件的處理流程:根據包名.類名 找到類;
所謂“反”指的是根據物件取得來取得物件的來源資訊,而這個“反”的操作核心的處理就在於Object類的一個方法:取得Class物件:

public final native Class<?> getClass
();

該方法返回的是一個Class類的物件,這個Class描述的就是類。
如:呼叫getClass()方法

package reflect;

import java.util.Date;

public class Test {

    public static void main(String[] args) {
            Date date = new Date();
            System.out.println(date.getClass());
        }
}

//+++++++++++++++++++++++++++++++++++
class java.util.Date

此時便是通過物件去的了物件的來源,這就是“反”的本質。
在反射的世界裡面,看重的不再是一個物件,而是物件身後的組成(類、構造、成員等)

Class類物件的三種例項化模式
Class類是描述整個類的概念,也是整個反射的操作源頭,在使用Class類的時候魚藥關注的依然是這個類的物件。而這個類的物件的產生模式一共有三種:

  • 任何類的例項化物件都可以通過Object類中的getClass()方法取得Class類物件。
  • “類.class”:直接根據某個具體的類來取得Class類的例項化物件。
  • 使用Class類提供的方法:public static Class< ?>forName(String className) throws ClassNotFoundException

使用Class.forName()方法:

package reflect;

public class Test {
    public static void main(String[] args) throws ClassNotFoundException {
        Class<?> cls = Class.forName("java.util.Date");
        System.out.println(cls.getName());
    }
}

//++++++++++++++++++++++++++++++++++++++++++++
java.util.Date

在以上給出的三個方法中可以發現,除了第一種方法會產生Date類物件的例項化物件之外,其他的兩種都不會產生Date類的例項化物件。於是取得了Class類物件有一個最直接的好處:可以通過反射例項化物件,在Class類中定義有如下方法:

public T newInstance()throws InstantiationException,IllegalAccessException

如:

public class Test {
    public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
        Class<?> cls = Class.forName("java.util.Date");
        Object obj = cls.newInstance();//結果和下句結果相同
//      Object obj = new java.util.Date();
        System.out.println(obj);
    }
}
//+++++++++++++++++++++
Fri Jun 01 15:59:46 GMT+08:00 2018

所以除了new之外,物件的例項化模式有了新的方式。
取得lClass物件就意味著取得了一些指定類的操作許可權。

反射與工廠設計模式

工廠設計模式的原則:如果是自己編寫的介面,想要取得本介面的例項化物件,最好使用工廠類來設計。單是也需要知道傳統設計模式所帶來的問題:

interface Ifruit {//介面
    public void eat();
}
class Apple implements Ifruit{//實現

    @Override
    public void eat() {
        System.out.println("i like apple");
    }
}
class Factory {//工廠模式,代替生產物件
    private Factory() {};
    public static Ifruit getInstance(String ClassName) {
        if ("apple".equals(ClassName)) {
            return new Apple();
        }
        return null;
    }
}

public class Test{
    public static void main(String[] args) {
        Ifruit ifruit = Factory.getInstance("apple");
        ifruit.eat();
    }
}

//+++++++++++++++++++++++++++++++++++
i like apple

傳統工廠類在實際開發中根本用不到。因為如果每增加一個介面就需要修改工廠類
如果想要解決關鍵字new帶來的問題,最好的做法就是通過反射來完成處理,因為Class類可以使用newInstant()例項化物件,同時Class.forName()能夠接收類名稱。
修改:

interface Ifruit {//
    public void eat();
}
class Apple implements Ifruit{

    @Override
    public void eat() {
        System.out.println("i like apple");
    }
}
class Banana implements Ifruit{

    @Override
    public void eat() {
        System.out.println("i like banana");
    }

}
class Factory {
    private Factory() {};
    public static Ifruit getInstance(String ClassName) throws InstantiationException, IllegalAccessException, ClassNotFoundException {
        Ifruit ifruit = (Ifruit)Class.forName(ClassName).newInstance();//用newInstance產生例項化物件
        return ifruit;
    }
}

public class Test{
    public static void main(String[] args) throws InstantiationException, IllegalAccessException, ClassNotFoundException {
        Ifruit ifruit = Factory.getInstance("reflect.Apple");//傳入完整的類名
        ifruit.eat();
    }
}

//+++++++++++++++++++++++++++++++++++
i like apple

引入反射後,每當新增介面子類,無需修改工廠類帶就可以很方便的進行介面子類擴充套件,相當於動態的在new傳入的物件,以上就是簡單工廠模式