先修知識
多態
- 接口與父類可作返回類型,引用聲明類型.
- 實現類與繼承類可作構造類型,反射強制轉換類型,反射(子類.class)獲取元類型
利用反射拓展工廠生產能力
反射一定是用在通用的場景,故利用反射機制時不應該出現具體類的名字,最多用到接口或抽象類.
- 定義輸入參數:public static Object getClass(Class<?extends Shape> clazz)//定義元類型Class的範圍,必須滿足是Shape的子類
ps:如何輸入(Class clazz)元類型?
通過類的元類型 Class clazz=Object.class 或對象的元類型 Class clazz.=obj.getClass().
- 獲取元類型對象對應的類型名:clazz.getName()
- 根據類名加載對應的類,運行其中的靜態方法:Class.forName(clazz.getName())
- 加載完後創建對應的實例:Object obj = Class.forNmae(clazz.getName()).getInstance();
- return obj;
應用反射工廠類時人們一般都寫好了具體類,人們是知道具體類名的
- 利用反射機制獲取對應類的類對象:Object obj = ShapeFactory.getClass(Square.class)
- 用具體類強制轉換:Square square = (Square) ShapeFactory.getClass(Square.class);
- 完美實現子類的拓展,且不用修改工廠類的源碼
工廠模式
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();
}
}
工廠模式總結
直接反射返回多態的子類實例
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;
}
}
反射缺點
- 反射也就拓展方便點
- 程序員想用反射必須用類名.class
- 嚴重違背迪米特法則
- 我都知道類名了.直接 Shape shape = new Circle();不好點?
- 最好是情況是不管真實類名是Circle還是CirShape,我只要輸入"circle"都能正確輸出對應的類的實例.
- 這纔是我們要的.枚舉類滿足這一切.
在上述基礎上,修改接口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();
}