設計模式之反射機制下的工廠模式,寫完具體類馬上就能用

先修知識

多態

  1. 接口與父類可作返回類型,引用聲明類型.
  2. 實現類與繼承類可作構造類型,反射強制轉換類型,反射(子類.class)獲取元類型

利用反射拓展工廠生產能力

反射一定是用在通用的場景,故利用反射機制時不應該出現具體類的名字,最多用到接口或抽象類.

  1. 定義輸入參數:public static Object getClass(Class<?extends Shape> clazz)//定義元類型Class的範圍,必須滿足是Shape的子類

ps:如何輸入(Class clazz)元類型?
通過類的元類型 Class clazz=Object.class 或對象的元類型 Class clazz.=obj.getClass().

  1. 獲取元類型對象對應的類型名:clazz.getName()
  2. 根據類名加載對應的類,運行其中的靜態方法:Class.forName(clazz.getName())
  3. 加載完後創建對應的實例:Object obj = Class.forNmae(clazz.getName()).getInstance();
  4. return obj;

應用反射工廠類時人們一般都寫好了具體類,人們是知道具體類名的

  1. 利用反射機制獲取對應類的類對象:Object obj = ShapeFactory.getClass(Square.class)
  2. 用具體類強制轉換:Square square = (Square) ShapeFactory.getClass(Square.class);
  3. 完美實現子類的拓展,且不用修改工廠類的源碼

工廠模式

Factory創建對象,用於不同條件創建不同實例時.比如用三大協議連接服務器.

優點:

想創建一個對象只要知道名稱即可.想增加一個產品線,拓展一個工廠類和具體類即可.無需知道具體實現

缺點:

每增加一個產品線,必須新增一個具體類(實驗室)和對象實現工廠.

創建接口

public interface Shape{
    void draw;
}

接口的具體類

public class Rectangle implements Shape{
    @Override
    public void draw(){
        System.out.println("InsideRectangle::draw() method.");
    }
}
Square
Circle

單一工廠

依據輸入的信息,生產對應的產品

public class ShapeFactory{
    public Shape getShape(String shapeType){
        if(shapeType == null){
            return null;
        }
        if(shapeType.equalsIgnoreCase("CIRCLE")){
            return new Circle();
        }else if(shapeType.equalsIgnoreCase("Square")){
            return new Square();
        }else if(shapeType.equalsIgnoreCase("Rectangle")){
            return new Rectangle();
        }
        return null;//若有輸入但信息錯誤,則返回null

    }
}

使用工廠類,生成對應產品,並訪問對應的功能

public class FactoryPatternDemo{
    public static void main(String[] args){
        ShapeFactory shapeFactory = new ShapeFactory();
        Shape shape1 = shapeFactory.getShape("Circle");
        shape1.draw();
        Shape shape2 = shapeFactory.getShape("Rectangle");
        shape2.draw();
        Shape shape3 = shapeFactory.getShape("Square");
        shape3.draw();
    }
}

採用反射機制重構工廠類

擺脫必須在源碼中 if(具體類) return new 具體類的尷尬

尷尬是因爲源碼中寫了多少就只能用多少,想用新產品必須修改源碼,非常麻煩

public class ShapeFactory{
    public static Object getClass(Class<? extends Shape> clazz){//限制必須是Shape的子類
       Object obj = null;//萬能多態對象
       try{
           obj = Class.forName(clazz.getNmae()).newInstance();
       }catch(ClassNotFoundException e){
           e.printStackTrace();
       }
       return obj;
    }
}

利用JDK9特性的改進版

Class.forName(clazz.getName())等價於clazz.getConstructor

使用時強制轉換一下即可

public class FactoryPatternDemo{
    public static void main(String[] args){
        Rectangle rect = (Rectangle)ShapeFactory.getClass(Rectangle.class);
        rect.draw();
        Square square = ShapeFactory.getClass(Rectangle.class);
        square.draw();
    }
}

工廠模式總結

實現功能方法
在工廠的創建方法中被用來new實例
輸入\類名.class\到工廠類中的創建方法
產品接口
具體產品類
工廠類
Demo類

直接反射返回多態的子類實例

public class ShapeFactory{
    public static Object getShape(Class<? extends Shape> clazz){
       try{
            return (IShape) clazz.getConstructor().newInstance();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (SecurityException e) {
            e.printStackTrace();
        }
        return null;
    }
}

反射缺點

  1. 反射也就拓展方便點
  2. 程序員想用反射必須用類名.class
  3. 嚴重違背迪米特法則
  4. 我都知道類名了.直接 Shape shape = new Circle();不好點?
  5. 最好是情況是不管真實類名是Circle還是CirShape,我只要輸入"circle"都能正確輸出對應的類的實例.
  6. 這纔是我們要的.枚舉類滿足這一切.
    在上述基礎上,修改接口Shape
public interface Shape{
    static String SHAPR_SQUARE = Square.class;/Square類的全限定名
    static String SHAPR_REACTANGLE = Rectangle.class;/Rectangle類的全限定名
    static String SHAPR_Circle = Circle.class;/Circle類的全限定名
    
    void draw();
}

應用時

public class FactoryPatternDemo{
    
    Shape shape_1 = Factory.getShape(Shape.SHAPE_CIRCLE);
    shape_1.draw();
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章