工廠模式(Factory Pattern)是 Java 中最常用的設計模式之一。這種型別的設計模式屬於建立型模式,它提供了一種建立物件的最佳方式。我們熟悉的Spring 的 bean 工廠等。
直接上demo.先程式碼,後介紹。
1. 編寫介面Shape
Shape .java
/**
* 一個介面:關於形狀
* Created by Fant.J.
*/
public interface Shape {
/**
* 描述方法
*/
void describe();
}
複製程式碼
2. 編寫實現類
Circle .java
/**
* Created by Fant.J.
*/
public class Circle implements Shape {
/**
* 描述方法
*/
@Override
public void describe() {
System.out.println("我是個圓形");
}
}
複製程式碼
Rectangle .java
/**
* Created by Fant.J.
*/
public class Rectangle implements Shape {
/**
* 描述方法
*/
@Override
public void describe() {
System.out.println("我是一個長方形");
}
}
複製程式碼
3. 編寫工廠類ShapeFactory
ShapeFactory .java
/**
* 形狀工廠(你可以通過我獲取 相應的例項化物件)
* Created by Fant.J.
*/
public class ShapeFactory {
public Shape getShape(String type){
if (type == null){
return null;
}
if ("CIRCLE".equals(type)){
return new Circle();
}else if ("RECTANGLE".equals(type)){
return new Rectangle();
}
return null;
}
}
複製程式碼
4. 測試
/**
* Created by Fant.J.
*/
public class Test {
public static void main(String[] args) {
ShapeFactory shapeFactory = new ShapeFactory();
//獲取 圓形 例項化物件
Shape circle = shapeFactory.getShape("CIRCLE");
//呼叫物件方法
circle.describe();
//獲取 長方形 例項化物件
Shape rectangle = shapeFactory.getShape("RECTANGLE");
//呼叫方法
rectangle.describe();
}
}
複製程式碼
我是個圓形
我是一個長方形
複製程式碼
5. 進階
提問:如果你是一個愛動腦筋的人,你會發現,它和Bean工廠的作用是相似的,但是Bean工廠是這樣實現的嗎?它會自動去寫if語句去建立例項物件嗎?
答案肯定是不是的。
當然,我在這裡不會像spring 的bean工廠一樣,把它的邏輯寫出來,因為人家已經是經過很多年的迭代的產品,封裝的已經(目不忍視)太厚了,所以我把它的實現原理說明。
可能有些人都想到了,對,就是框架離不開的:反射機制(傳送門),不怎麼了解的可以很快的看一下我以前的一個文集。
5.1 優化工廠類程式碼
ShapeFactory.java
/**
* 反射
*/
public Object getShape(Class<? extends Shape> clazz){
Object object = null;
try {
object = Class.forName(clazz.getName()).newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return object;
}
複製程式碼
5.2 測試
Object shape = (Rectangle)shapeFactory.getShape(Rectangle.class);
((Rectangle) shape).describe();
複製程式碼
6 新的問題
如果我們忘記去對shapeFactory 做強制型別轉換的話,那就是一個完全的object物件,使用不方便,那我們如何能夠省略型別轉換的這一步驟呢?
6.1 再優化工廠類
/**
* 反射 + 泛型
*/
public <T> T getShape(Class<? extends T> clazz){
T object = null;
try {
//例項化,並在這裡做 型別轉換
object = (T) Class.forName(clazz.getName()).newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return object;
}
複製程式碼
6.2 測試類
Rectangle shape = shapeFactory.getShape(Rectangle.class);
shape.describe();
複製程式碼
我是一個長方形
複製程式碼
我們可以看到,事實上也是做了強制型別轉換的,只不過在ShapeFactory裡做的,我們看不到而已,這種做法會大大方便我們使用。
7. 最後的甜點
經過第五點和第六點的學習,我們想想,spring 框架裡 其實只有一個工廠,那就是BeanFactory,ApplicationContext 也需要用它來建立物件。那我們如何去寫一個針對多個介面實現一個公共的工廠類呢?
/**
* 針對多個介面實現一個公共的工廠類
*/
public <T> Object getObject(Class<T> clazz) {
if (clazz == null ) {
return null;
}
Object obj = null;
try {
obj = Class.forName(clazz.getName()).newInstance();
} catch (InstantiationException | IllegalAccessException | ClassNotFoundException e) {
e.printStackTrace();
}
return obj;
}
複製程式碼
8. 總結
留個思考題,我們在Spring 框架下,@Autowired 裝配Bean 具體是完成什麼樣的操作呢,這可能也是工廠模式最好的總結說明。
希望能幫到大家,謝謝大家!