首頁 > 軟體

Java超詳細講解設計模式之一的工廠模式

2022-03-26 19:00:46

工廠模式

在Java應用程式中物件無處不在,這些物件都需要進行建立,如果建立的時候直接new物件,那麼如果我們要更換物件,所有new物件的地方都需要進行更改。違背了軟體設計原則中的開閉原則。如果我們使用工廠生產物件,只需要在工廠中關注物件的改變即可,達到了與物件解耦的目的,工廠模式最大的特點就是解耦合

補充:

開閉原則: 對擴充套件開放,對修改關閉。在程式需要進行擴充套件的時候,不能去修改原有的程式碼,實現一個熱插拔的效果。是為了使程式的擴充套件性好,易於維護和升級。

1.簡單工廠

1.1結構

  • 抽象產品: 定義了產品的規範,描述了產品的主要特性和功能。
  • 具體產品: 實現或者繼承抽象產品的子類
  • 具體工廠: 提供了建立產品的方法,呼叫者通過該方法來建立產品。

1.2實現

以點咖啡為例:

咖啡抽象類

public abstract class Coffee {
    /**
     * 獲取咖啡型別
     * @return
     */
    public abstract  String getName();

    /**
     *加糖
     */
    public  void addSugar(){
        System.out.println("加糖");
    }
    /**
     *加奶
     */
    public void addMilk(){
        System.out.println("加奶");
    }
}

美式咖啡類

public class AmericanCoffee  extends Coffee{

@Override
    public String getName(){
        return "美式咖啡";
    }
}

拿鐵咖啡類

public class LatteCoffee extends Coffee {

    @Override
    public String getName(){
        return "拿鐵咖啡";
    }
}

咖啡工廠類

public class CoffeeFactory {

    public Coffee createCoffee(String type){
      Coffee coffee = null;
      if("american".equals(type)){
          coffee = new AmericanCoffee();
      }else if("latten".equals(type)){
          coffee = new LatteCoffee();
      }else{
          throw new RuntimeException("沒有此型別的咖啡");
      }
      return coffee;
    }
}

咖啡店類

public class CoffeeStore {

    public Coffee orderCoffee(String type){
        CoffeeFactory factory = new CoffeeFactory();

      //呼叫生產咖啡的方法
        Coffee coffee = factory.createCoffee(type);
        coffee.addMilk();
        coffee.addSugar();

        return  coffee;
    }
}

測試類

public class Test {
    public static void main(String[] args) {
        CoffeeStore coffeeStore = new CoffeeStore();
        Coffee coffee = coffeeStore.orderCoffee("latten");
        System.out.println(coffee.getName());
    }
}

類圖

咖啡工廠負責生產咖啡(具體工廠),咖啡店通過咖啡工廠選取咖啡

其實簡單工廠是大家在實際寫程式碼的時候經常用到的,雖然簡單工廠實現了咖啡店與咖啡的耦合,但是可以明顯看到咖啡與咖啡工廠又耦合起來了,後期如果增加咖啡的新品種,我們需要修改咖啡工廠的程式碼,這又違背了「開閉原則」。

注意:

簡單工廠和不使用工廠是有很大區別的,如果咖啡店有多個,不使用工廠如果遇到新增咖啡需要修改所有咖啡店,但是使用工廠只需要修改咖啡工廠,類似於將所有咖啡店抽取出一個抽象的咖啡店。

1.3優缺點

優點:

封裝了建立物件的過程,可以通過引數直接獲取物件,把物件的建立和業務邏輯層分開,這樣可以避免之後修改客戶程式碼,如果需要實現新產品直接修改工廠類,更容易擴充套件。

缺點:

增加新產品時還需要修改工廠類的程式碼,違背了「開閉原則」。

1.4擴充套件

靜態工廠,將工廠類中建立物件的功能定義為靜態的,這樣不需要再建立工廠類,直接通過類名呼叫靜態方法,類似於工具類

public class CoffeeFactory {
  //靜態方法
    public static Coffee createCoffee(String type){
      Coffee coffee = null;
      if("american".equals(type)){
          coffee = new AmericanCoffee();
      }else if("latten".equals(type)){
          coffee = new LatteCoffee();
      }else{
          throw new RuntimeException("沒有此型別的咖啡");
      }
      return coffee;
    }
}

2.工廠方法

對工廠進行抽象,每一種產品對應一個具體工廠,新增產品只需要再新增對應的具體工廠,符合」開閉原則「

2.1結構

  • 抽象工廠: 提供建立產品的介面,呼叫者通過它存取具體工廠的工廠方法來建立產品
  • 具體工廠: 主要是實現抽象工廠中的抽象方法,完成具體產品的建立
  • 抽象產品: 定義了產品的規範,描述了產品的主要特性和功能
  • 具體產品: 實現抽象產品所定義的介面,由具體工廠來建立,與具體工廠之間一一對應

2.2實現

抽象咖啡類和具體咖啡類不變

咖啡工廠(抽象工廠)

public interface CoffeeFactory {
    /**
     * 建立咖啡
     * @return
     */
    Coffee createCoffee();
}

美式咖啡工廠(具體工廠)

public class AmericanCoffeeFactory implements CoffeeFactory{
    //美式咖啡工廠物件,專門生產美式咖啡
    @Override
    public Coffee createCoffee() {
        return new AmericanCoffee();
    }
}

拿鐵咖啡工廠(具體工廠)

public class LatteCoffeeFactory implements CoffeeFactory{
    //拿鐵咖啡工廠物件,專門生產拿鐵咖啡
    @Override
    public Coffee createCoffee() {
        return new LatteCoffee();
    }
}

咖啡店

public class CoffeeStore {

    private CoffeeFactory factory;

    public void setFactory(CoffeeFactory factory) {
        this.factory = factory;
    }

    /**
     * 點咖啡
     */
    public Coffee orderCoffee() {
        Coffee coffee = factory.createCoffee();
        coffee.addSugar();
        coffee.addMilk();
        return coffee;
    }
}

測試類

public class Test {
    public static void main(String[] args) {
        //建立咖啡店物件
        CoffeeStore coffeeStore = new CoffeeStore();
        //建立工廠物件
        CoffeeFactory factory = new AmericanCoffeeFactory();
        coffeeStore.setFactory(factory);

        //點咖啡
        Coffee coffee = coffeeStore.orderCoffee();

        System.out.println(coffee.getName());
    }
}

類圖

我們只需要知道所點咖啡具體對應的工廠物件,通過咖啡店呼叫對應的工廠,由工廠建立咖啡物件實現點咖啡的過程

2.3優缺點

優點:

  • 使用者只需要知道具體工廠就可以獲得所需產品,無需知道產品的具體建立過程
  • 在系統新增產品時只需要新增具體產品類和對應的具體工廠類,無需對原工廠進行修改符合「開閉原則」

缺點:

每增加一個產品就要增加一個對應的具體工廠類,增加的系統的複雜性。如果具體產品種類過多,那麼大量的工廠類不僅難以管理,而且也會造成程式中建立的物件過多,嚴重影響記憶體效能

3.抽象工廠

3.1結構

  • 抽象工廠: 提供建立產品的介面,包含多個建立產品的方法,可以建立多個不同等級的產品
  • 具體工廠: 主要是實現抽象工廠中的多個抽象方法,完成具體產品的建立
  • 抽象產品: 定義了產品的規範,描述了產品的主要特性和功能,抽象工廠模式有多個抽象產品
  • 具體產品: 實現抽象產品所定義的介面,由具體工廠來建立,與具體工廠是多對一關係

3.2實現

咖啡抽象類

public abstract class Coffee {
    /**
     * 獲取咖啡型別
     * @return
     */
    public abstract  String getName();

    /**
     *加糖
     */
    public  void addSugar(){
        System.out.println("加糖");
    }
    /**
     *加奶
     */
    public void addMilk(){
        System.out.println("加奶");
    }
}

美式咖啡類

public class AmericanCoffee  extends Coffee{

@Override
    public String getName(){
        return "美式咖啡";
    }
}

拿鐵咖啡類

public class LatteCoffee extends Coffee {

    @Override
    public String getName(){
        return "拿鐵咖啡";
    }
}

甜品抽象類

public abstract class Dessert {
 //甜品抽象類

    public abstract void show();
}

抹茶慕斯類

public class MatchaMousse extends Dessert{

//抹茶慕斯類
    @Override
    public void show() {
        System.out.println("抹茶慕斯");
    }


}

提拉米蘇類

public class Tiramisu  extends Dessert{
 //提拉米蘇類
    @Override
    public void show() {
        System.out.println("提拉米蘇");
    }
}

甜品工廠

public  interface DessertFactory {

    /**
     * 生產咖啡
     * @return
     */
    Coffee createCoffee();

    /**
     * 生產甜品
     * @return
     */
    Dessert createDessert();

}

美式風味甜品工廠類

public class AmericanDessertFactory  implements DessertFactory{
    /**
     *美式風味甜品工廠
     * 可以生產美式咖啡和抹茶慕斯
     */
    @Override
    public Coffee createCoffee() {
        return new AmericanCoffee();
    }

    @Override
    public Dessert createDessert() {
        return new MatchaMousse();
    }
}

義大利風味甜品工廠類

public class ItalyDessertFactory implements DessertFactory {

    /**
     *義大利風味甜品工廠
     * 可以生產拿鐵咖啡和提拉米蘇
     */
    @Override
    public Coffee createCoffee() {
        return new LatteCoffee();
    }

    @Override
    public Dessert createDessert() {
        return new Tiramisu();
    }
}

測試類

public class Test {
    public static void main(String[] args) {
        //ItalyDessertFactory factory = new ItalyDessertFactory();
           AmericanDessertFactory factory = new AmericanDessertFactory();
 
          Coffee coffee = factory.createCoffee()
        Dessert dessert = factory.createDessert();

        System.out.println(coffee.getName());

        dessert.show();

    }
}

類圖

由類圖可見,抽象工廠不再是一個具體工廠對應一個產品,而是一個具體工廠對應一個產品族。如果需要增加一個產品族只需加對應的工廠類,符合」開閉原則「

3.3優缺點

優點:

在工廠方法的基礎上減少了部分物件的建立,適合於每次只使用同一產品族的物件這類應用場景

缺點:

當產品族中需要增加一個產品時,所有工廠都要修改

4.模式擴充套件

組態檔+簡單工廠

通過工廠模式+組態檔的方式解除工廠物件和產品物件的耦合。在工廠類中載入組態檔的全類名,通過反射建立物件並儲存在容器中,如果需要直接從容器中獲取(Spring IOC原理)

4.1實現

1.定義組態檔

american = com.xue.config_factory.AmericanCoffee
latten = com.xue.config_factory.LatteCoffee

2.改進工廠類

public class CoffeeFactory {

    /**
     * 載入組態檔,獲取組態檔中設定的全類名,並建立該類的物件進行儲存
     */

    //1.定義容器物件儲存咖啡物件
    private static HashMap<String, Coffee> map = new HashMap<>();


    //2.載入組態檔
    static {
        //建立 Properties物件
        Properties properties = new Properties();
        //呼叫properties物件中的load方法進行組態檔的載入
        InputStream is = CoffeeFactory.class.getClassLoader().getResourceAsStream("bean.properties");
        try {
            properties.load(is);
            //從properties中獲取全類名
            Set<Object> keys = properties.keySet();

            for (Object key : keys) {
                String className = properties.getProperty((String) key);
                //通過反射建立物件
                Class class1 = Class.forName(className);
                Coffee coffee = (Coffee) class1.newInstance();

                //將名稱和物件儲存在容器中
                map.put((String) key,coffee);

            }
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    //根據名稱獲取物件
    public static Coffee createCoffee(String name) {


        return map.get(name);
    }
}

靜態成員變數用來儲存建立的物件(鍵儲存的是名稱,值儲存的是對應的物件),而讀取組態檔和建立物件寫在靜態程式碼塊中只需要執行一次

測試類

public class Test {
    public static void main(String[] args) {
        Coffee coffee = CoffeeFactory.createCoffee("american");
        System.out.println(coffee.getName());

        System.out.println("------------");

        Coffee latten = CoffeeFactory.createCoffee("latten");
        System.out.println(latten.getName());

    }

}

成功!!!

以上就是Java設計模式——工廠模式的介紹及四種實現方式 往期連結:單例模式

到此這篇關於Java超詳細講解設計模式之一的工廠模式的文章就介紹到這了,更多相關Java 工廠模式內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com