一 工廠模式介紹
1.1 工廠模式的定義
在基類中定義創建對象的一個接口,讓子類決定實例化哪個類。工廠方法讓一個類的實例化延遲到子類中進行。
1.2 工廠模式的分類:
(1)簡單工廠(Simple Factory)模式,又稱靜態工廠方法模式(Static Factory Method Pattern)。
(2)工廠方法(Factory Method)模式,又稱多態性工廠(Polymorphic Factory)模式或虛擬構造子(Virtual Constructor)模式;
(3)抽象工廠(Abstract Factory)模式,又稱工具箱(Kit 或Toolkit)模式。
1.3 爲什麼要用工廠模式
(1) 解耦 :把對象的創建和使用的過程分開
(2)降低代碼重複: 如果創建某個對象的過程都很複雜,需要一定的代碼量,而且很多地方都要用到,那麼就會有很多的重複代碼。
(3) 降低維護成本 :由於創建過程都由工廠統一管理,所以發生業務邏輯變化,不需要找到所有需要創建某個對象的地方去逐個修正,只需要在工廠裏修改即可,降低維護成本。
public interface Shape {
void draw();
}
2)創建實現該接口的具體圖形類
圓形
public class Circle implements Shape {
public Circle(){
System.out.println("Circle");
}
@Override
public void draw() {
System.out.println("Draw Circle");
}
}
長方形
public class Rectangle implements Shape {
public Square(){
System.out.println("Rectangle");
}
@Override
public void draw() {
System.out.println("Draw Rectangle");
}
}
正方形
public class Square implements Shape {
public Square(){
System.out.println("Square");
}
@Override
public void draw() {
System.out.println("Draw Square");
}
}
(3)創建工廠類:
public class ShapeFactory{
// 使用 getShape 方法獲取形狀類型的對象
public static Shape getShape(String shapeType){
if(shapeType==null){
return null;
}
if(shapeType.equalsIgnoreCase("CIRCLE")){
return new Circle();
}else if(shapeType.equalsIgnoreCase("RECTANGLE")){
return new Rectangle();
}else if(shapeType.equalsIgnoreCase("SQUARE")){
return new Square();
}
return null;
}
}
(4)測試方法:
public class Test {
public static void main(String[] args){
// 獲取 Circle 的對象,並調用它的 draw 方法
Shape circle = ShapeFactory.getShape("CIRCLE");
circle.draw();
// 獲取 Rectangle 的對象,並調用它的 draw 方法
Shape rectangle = ShapeFactory.(");
rectangle.draw();
// 獲取 Square 的對象,並調用它的 draw 方法
Shape square = ShapeFactory.getShape("SQUARE");
square.draw();
}
}
輸出結果:
Circle
Draw Circle
Rectangle
Draw Rectangle
Square
Draw Square
2.5 使用反射機制改善簡單工廠
將工廠類改爲下面的形式:
package factory_pattern;
/**
* 利用反射解決簡單工廠每次增加新了產品類都要修改產品工廠的弊端
*
* @author Administrator
*
*/
public class ShapeFactory2 {
public static Object getClass(Class<? extends Shape> clazz) {
Object obj = null;
try {
obj = Class.forName(clazz.getName()).newInstance();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return obj;
}
}
測試方法:
package factory_pattern;
public class Test2 {
public static void main(String[] args) {
Circle circle = (Circle) ShapeFactory2.getClass(factory_pattern.Circle.class);
circle.draw();
Rectangle rectangle = (Rectangle) ShapeFactory2.getClass(factory_pattern.Rectangle.class);
rectangle.draw();
Square square = (Square) ShapeFactory2.getClass(factory_pattern.Square.class);
square.draw();
}
}
這種方式的雖然符合了 開放-關閉原則 ,但是每一次傳入的都是產品類的全部路徑,這樣比較麻煩。如果需要改善的話可以通過 反射+配置文件 的形式來改善,這種方式使用的也是比較多的。
3 工廠方法模式
3.1 介紹
工廠方法模式應該是在工廠模式家族中是用的最多模式,一般項目中存在最多的就是這個模式。
工廠方法模式是簡單工廠的僅一步深化, 在工廠方法模式中,我們不再提供一個統一的工廠類來創建所有的對象,而是針對不同的對象提供不同的工廠。也就是說 每個對象都有一個與之對應的工廠 。
3.2 適用場景
一個類不知道它所需要的對象的類:在工廠方法模式中,客戶端不需要知道具體產品類的類名,只需要知道所對應的工廠即可,具體的產品對象由具體工廠類創建;客戶端需要知道創建具體產品的工廠類。
一個類通過其子類來指定創建哪個對象:在工廠方法模式中,對於抽象工廠類只需要提供一個創建產品的接口,而由其子類來確定具體要創建的對象,利用面向對象的多態性和里氏
將創建對象的任務委託給多個工廠子類中的某一個,客戶端在使用時可以無需關心是哪一個工廠子類創建產品子類,需要時再動態指定,可將具體工廠類的類名存儲在配置文件或數據庫中。
3.3 工廠方法模式角色分配:
抽象工廠(Abstract Factory)角色:是工廠方法模式的核心,與應用程序無關。任何在模式中創建的對象的工廠類必須實現這個接口。
具體工廠(Concrete Factory)角色 :這是實現抽象工廠接口的具體工廠類,包含與應用程序密切相關的邏輯,並且受到應用程序調用以創建某一種產品對象。
抽象產品(AbstractProduct)角色 :工廠方法模式所創建的對象的超類型,也就是產品對象的共同父類或共同擁有的接口。
具體產品(Concrete Product)角色 :這個角色實現了抽象產品角色所定義的接口。某具體產品有專門的具體工廠創建,它們之間往往一一對應
3.4 工廠方法模式實例
上面簡單工廠例子中的圖形接口以及相關圖像實現類不變。我們只需要增加一個工廠接口以及實現這個接口的工廠類即可。
(1)增加一個工廠接口:
public interface Factory {
public Shape getShape();
}
(2)增加相關工廠類:
圓形工廠類
public class CircleFactory implements Factory {
@Override
public Shape getShape() {
// TODO Auto-generated method stub
return new Circle();
}
}
長方形工廠類
public class RectangleFactory implements Factory{
@Override
public Shape getShape(){
// TODO Auto-generated method stub
return new Rectangle();
}
}
方形工廠類
public class SquareFactory implements Factory{
@Override
public Shape getShape() {
// TODO Auto-generated method stub
return new Square();
}
}
(3)測試:
public class Test {
public static void main(String[] args) {
Factory circlefactory = new CircleFactory();
Shape circle = circlefactory.getShape();
circle.draw();
}
}
輸出結果:
Circle
Draw Circle
4 抽象工廠模式
4.1 介紹
在工廠方法模式中,其實我們有一個潛在意識的意識。那就是我們生產的都是同一類產品。抽象工廠模式是工廠方法的僅一步深化,在這個模式中的工廠類不單單可以創建一種產品,而是可以創建一組產品。
抽象工廠應該是比較最難理解的一個工廠模式了。
4.2 適用場景
和工廠方法一樣客戶端不需要知道它所創建的對象的類。
需要一組對象共同完成某種功能時,並且可能存在多組對象完成不同功能的情況。(同屬於同一個產品族的產品)
系統結構穩定,不會頻繁的增加對象。(因爲一旦增加就需要修改原有代碼,不符合開閉原則)
4.3 抽象工廠方法模式角色分配:
抽象工廠(AbstractFactory)角色 :是工廠方法模式的核心,與應用程序無關。任何在模式中創建的對象的工廠類必須實現這個接口。
具體工廠類(ConreteFactory)角色 :這是實現抽象工廠接口的具體工廠類,包含與應用程序密切相關的邏輯,並且受到應用程序調用以創建某一種產品對象。
抽象產品(Abstract Product)角色 :工廠方法模式所創建的對象的超類型,也就是產品對象的共同父類或共同擁有的接口。
具體產品(Concrete Product)角色 :抽象工廠模式所創建的任何產品對象都是某一個具體產品類的實例。在抽象工廠中創建的產品屬於同一產品族,這不同於工廠模式中的工廠只創建單一產品,我後面也會詳解介紹到。
。
4.4 抽象工廠的工廠和工廠方法中的工廠有什麼區別呢?
抽象工廠是生產一整套有產品的(至少要生產兩個產品),這些產品必須相互是有關係或有依賴的,而工廠方法中的工廠是生產單一產品的工廠。
4.5 抽象工廠模式實例
不知道大家玩過穿越火線或者吃雞這類遊戲了嗎,遊戲中存在各種槍。我們假設現在存在AK、M4A1兩類槍,每一種槍對應一種子彈。我們現在這樣考慮生產AK的工廠可以順便生產AK使用的子彈,生產M4A1的工廠可以順便生產M4A1使用的子彈。(AK工廠生產AK系列產品包括子彈啊,AK槍的類型啊這些,M4A1工廠同理)
(1)創建相關接口:
槍
public interface Gun {
public void shooting();
}
子彈
public interface Bullet {
public void load();
}
(2)創建接口對應實現類:
AK類
public class AK implements Gun{
@Override
public void shooting() {
System.out.println("shooting with AK");
}
}
M4A1類
public class M4A1 implements Gun {
@Override
public void shooting() {
System.out.println("shooting with M4A1");
}
}
AK子彈類
public class AK_Bullet implements Bullet {
@Override
public void load() {
System.out.println("Load bullets with AK");
}
}
M4A1子彈類
public class M4A1_Bullet implements Bullet {
@Override
public void load() {
System.out.println("Load bullets with M4A1");
}
}
(3)創建工廠接口
public interface Factory {
public Gun produceGun();
public Bullet produceBullet();
}
(4)創建具體工廠
生產AK和AK子彈的工廠
public class AK_Factory implements Factory{
@Override
public Gun produceGun() {
return new AK();
}
@Override
public Bullet produceBullet() {
return new AK_Bullet();
}
}
生產M4A1和M4A1子彈的工廠
public class M4A1_Factory implements Factory{
@Override
public Gun produceGun() {
return new M4A1();
}
@Override
public Bullet produceBullet() {
return new M4A1_Bullet();
}
}
(5)測試
public class Test {
public static void main(String[] args) {
Factory factory;
Gun gun;
Bullet bullet;
factory =new AK_Factory();
bullet=factory.produceBullet();
bullet.load();
gun=factory.produceGun();
gun.shooting();
}
}
輸出結果:
Load bullets with AK
shooting with AK