3.java設計模式之工廠模式

xiaokantianse發表於2020-11-11

基本需求:

  • 一個披薩店需要訂購不同種類的披薩

傳統方式:

  • 實現思路

    • 在訂購類中根據使用者不同的輸入直接建立不同的披薩實體類進行返回
  • UML類圖

  • 程式碼實現

    • 披薩類

      • // 抽象父類
        public abstract class Pizza {
        
           String name;
        
           public abstract void prepare();
        
           public void bake() {
               System.out.println(this.name + "披薩烘焙中");
           }
        
           public void cut() {
               System.out.println(this.name + "披薩切割中");
           }
        
           public void box() {
               System.out.println(this.name + "披薩打包中");
           }
        
        }
        
        // 乳酪披薩 子類1
        public class CheesePizza extends Pizza {
        
           public CheesePizza(String name) {
               this.name = name;
           }
        
           @Override
           public void prepare() {
               System.out.println(this.name + "披薩準備原材料中");
           }
        
        }
        // 希臘披薩 子類2
        public class GreekPizza extends Pizza{
        
           public GreekPizza(String name) {
               this.name = name;
           }
        
           @Override
           public void prepare() {
               System.out.println(this.name + "披薩準備原材料中");
           }
        
        }
        
    • 訂購類

      • public class PizzaStore {
        
           public static void main(String[] args) {
               new PizzaStore().OrderPizza();
           }
        
           // 訂購披薩方法 傳統方式直接採用new出子類的方法進行返回對應的披薩
           public void OrderPizza() {
               Pizza pizza;
               Scanner scanner = new Scanner(System.in);
               String orderType;
               while (true) {
                   System.out.println("輸入披薩的種類:");
                   orderType = scanner.nextLine();
                   if (StringUtils.equals("cheese", orderType)) {
                       pizza = new CheesePizza("cheese");
                   } else if (StringUtils.equals("greek", orderType)) {
                       pizza = new GreekPizza("greek");
                   } else {
                       System.out.println("沒有該型別的披薩");
                       break;
                   }
                   pizza.prepare();
                   pizza.bake();
                   pizza.cut();
                   pizza.box();
               }
           }
        
        }
        

缺陷及改進:

  • 在訂購方法中,直接採用new出子類的方法進行返回對應的披薩,違反了設計模式的ocp原則,即對擴充套件開放,對修改關閉。即當我們給類增加新功能的時候,儘量不修改程式碼,或者儘可能少修改程式碼
  • 如果新增加一種型別的披薩,則需要修改OrderPizza的方法實現,如果有多個建立Pizza的方法則都需要修改
  • 把建立Pizza物件封裝到一個類中,這樣我們有新的Pizza種類時,只需要修改該類就可,其它有建立Pizza物件的程式碼就不需要修改了——>簡單工廠模式

簡單工廠模式:

  • 簡單工廠模式是屬於建立型模式,是工廠模式的一種。簡單工廠模式是由一個工廠物件決定建立出哪一種產品類的例項。簡單工廠模式是工廠模式家族中最簡單實用的模式

  • 簡單工廠模式:定義了一個建立物件的類,由這個類來封裝例項化物件的行為(程式碼)

  • 當我們會用到大量的建立某種、某類或者某批物件時,就會使用到工廠模式

  • 實現思路

    • 建立一個簡單工廠類SimpleFactory,提供一個建立Pizza的方法,PizzaStore想要Pizza時,直接在工廠中獲取即可
  • UML類圖

  • 程式碼實現

    • // 簡單工廠類 Pizza類及實現類同上
      public class SimpleFactory {
      
         // 建立Pizza的方法 可在多個地方呼叫,如果需要增加其他種類的Pizza則只需要修改此方法內部的實現即可,不需要呼叫方
         public Pizza createPizza(String orderType) {
             Pizza pizza = null;
             if (StringUtils.equals("cheese", orderType)) {
                 pizza = new CheesePizza("cheese");
             } else if (StringUtils.equals("greek", orderType)) {
                 pizza = new GreekPizza("greek");
             } else {
                 System.out.println("沒有該型別的披薩");
             }
             return pizza;
         }
      
         // 簡單工廠也叫靜態工廠 可直接提供靜態的方法獲取物件
         public static Pizza createPizza1(String orderType) {
             Pizza pizza = null;
             if (StringUtils.equals("cheese", orderType)) {
                 pizza = new CheesePizza("cheese");
             } else if (StringUtils.equals("greek", orderType)) {
                 pizza = new GreekPizza("greek");
             } else {
                 System.out.println("沒有該型別的披薩");
             }
             return pizza;
         }
         
      }
      
    • public class PizzaStore {
      
         public static void main(String[] args) {
             // new PizzaStore().OrderPizza();
             new PizzaStore(new SimpleFactory());
         }
      
         private SimpleFactory simpleFactory;
      
         public PizzaStore(SimpleFactory simpleFactory) {
             this.simpleFactory = simpleFactory;
             OrderPizza();
         }
      
         private void OrderPizza() {
             Pizza pizza;
             Scanner scanner = new Scanner(System.in);
             String orderType;
             while (true) {
                 System.out.println("輸入披薩的種類:");
                 orderType = scanner.nextLine();
                 // 通過Pizza工廠獲取Pizza物件
                 pizza = simpleFactory.createPizza(orderType);
                 if (null == pizza) {
                     break;
                 }
                 pizza.prepare();
                 pizza.bake();
                 pizza.cut();
                 pizza.box();
             }
         }
      
      }
      

工廠方法模式:

  • 在上面的基礎上增加了新的需求,需要在每種pizza上加上產地,例如 北京的乳酪pizza、北京的胡椒pizza或者是倫敦的乳酪pizza、倫敦的胡椒pizza

  • 思路1:可以使用簡單工廠模式,新建兩個簡單工廠,BJPizzaSimpleFactory和LDPizzaSimpleFactory 但是考慮到專案的規模,以及軟體的可維護性、可擴充套件性並不是特別好

  • 思路2:採用工廠方法模式

    • 將披薩專案的例項化功能抽象成抽象方法,在不同的口味點餐子類中具體實現
    • 工廠方法模式:定義了一個建立物件的抽象方法,由子類決定要例項化的類。工廠方法模式將物件的例項化推遲到子類。
  • 實現思路

    • 在PizzaStore中增加一個建立Pizza抽象方法,由BJPizzaStore和LDPizzaStore去實現,至此該兩個子類建立的Pizza都是各自地區的
  • UML類圖

  • 程式碼實現

    • // 含有抽象方法的抽象類
      public abstract class PizzaStore {
      
         public static void main(String[] args) {
             // 根據地區選工廠? 沒品出來和建立多個簡單工廠有什麼好處
             String area = "BJ";
             if (area.equals("BJ")) {
                 new BJPizzaStore();
             } else {
                 new LDPizzaStore();
             }
         }
      
         public PizzaStore() {
             OrderPizza();
         }
      
         private void OrderPizza() {
             Pizza pizza;
             Scanner scanner = new Scanner(System.in);
             String orderType;
             while (true) {
                 System.out.println("輸入披薩的種類:");
                 orderType = scanner.nextLine();
                 // 使用抽象方法來建立Pizza,使用不同的實現類將會得到不同的披薩
                 pizza = createPizza(orderType);
                 if (null == pizza) {
                     break;
                 }
                 pizza.prepare();
                 pizza.bake();
                 pizza.cut();
                 pizza.box();
             }
         }
      
         // 建立Pizza的抽象方法 由子類實現
         public abstract Pizza createPizza(String orderType);
      
      }
      
    • // 子類1
      public class BJPizzaStore extends PizzaStore {
      
         @Override
         public Pizza createPizza(String orderType) {
             Pizza pizza = null;
             if (StringUtils.equals("cheese", orderType)) {
                 pizza = new BJCheesePizza("BJCheese");
             } else if (StringUtils.equals("pepper", orderType)) {
                 pizza = new BJPepperPizza("BJPepper");
             } else {
                 System.out.println("沒有該型別的披薩");
             }
             return pizza;
         }
      }
      
      // 子類2
      public class LDPizzaStore extends PizzaStore {
      
         @Override
         public Pizza createPizza(String orderType) {
             Pizza pizza = null;
             if (StringUtils.equals("cheese", orderType)) {
                 pizza = new BJCheesePizza("LDCheese");
             } else if (StringUtils.equals("pepper", orderType)) {
                 pizza = new BJPepperPizza("LDPepper");
             } else {
                 System.out.println("沒有該型別的披薩");
             }
             return pizza;
         }
      }
      

抽象工廠模式:

  • 抽象工廠模式定義了一個interface用於建立相關或有依賴關係的物件簇,而無需指明具體的類,可以將簡單工廠模式和工廠方法模式進行整合

  • 從設計層面看,抽象工廠模式就是對簡單工廠模式的改進(或者稱為進一步的抽象),將工廠抽象成兩層,

  • 實現思路

    • 建立AbsFactory( 抽象工廠) 和具體實現的工廠子類,使用時建立工廠子類即可
  • UML類圖

  • 程式碼實現

    • // 抽象工廠類
      public interface AbsFactory {
      
         Pizza createPizza(String orderType);
      
      }
      
      // 子類1
      public class BJFactory implements AbsFactory {
      
         @Override
         public Pizza createPizza(String orderType) {
             Pizza pizza = null;
             if (StringUtils.equals("cheese", orderType)) {
                 pizza = new BJCheesePizza("BJCheese");
             } else if (StringUtils.equals("pepper", orderType)) {
                 pizza = new BJPepperPizza("BJPepper");
             } else {
                 System.out.println("沒有該型別的披薩");
             }
             return pizza;
         }
      
      }
      
      // 子類2
      public class LDFactory implements AbsFactory {
      
         @Override
         public Pizza createPizza(String orderType) {
             Pizza pizza = null;
             if (StringUtils.equals("cheese", orderType)) {
                 pizza = new BJCheesePizza("LDCheese");
             } else if (StringUtils.equals("pepper", orderType)) {
                 pizza = new BJPepperPizza("LDPepper");
             } else {
                 System.out.println("沒有該型別的披薩");
             }
             return pizza;
         }
      
      }
      
    • public class PizzaStore {
      
         public static void main(String[] args) {
             new PizzaStore(new BJFactory());
         }
      
         private AbsFactory factory;
      
         public PizzaStore(AbsFactory factory) {
             // 在此處宣告需要工廠類物件 , 使用時直接建立抽象工廠的子類物件即可
             this.factory = factory;
             OrderPizza();
         }
      
         private void OrderPizza() {
             Pizza pizza;
             Scanner scanner = new Scanner(System.in);
             String orderType;
             while (true) {
                 System.out.println("輸入披薩的種類:");
                 orderType = scanner.nextLine();
                 // 使用抽象方法來建立Pizza,使用不同的實現類將會得到不同的披薩
                 pizza = factory.createPizza(orderType);
                 if (null == pizza) {
                     break;
                 }
                 pizza.prepare();
                 pizza.bake();
                 pizza.cut();
                 pizza.box();
             }
         }
      
      }
      

jdk原始碼:

  • 在Calendar類中的getInstance()方法中有用到簡單工廠模式

  • public static Calendar getInstance()
       {
       	// 獲取預設的zone和aLocale
           return createCalendar(TimeZone.getDefault(), Locale.getDefault(Locale.Category.FORMAT));
       }
    
    public static Calendar getInstance(TimeZone zone,
                                          Locale aLocale)
       {
           return createCalendar(zone, aLocale);
       }
    
    private static Calendar createCalendar(TimeZone zone,
                                              Locale aLocale)
       {
           CalendarProvider provider =
               LocaleProviderAdapter.getAdapter(CalendarProvider.class, aLocale)
                                    .getCalendarProvider();
           if (provider != null) {
               try {
                   return provider.getInstance(zone, aLocale);
               } catch (IllegalArgumentException iae) {
                   // fall back to the default instantiation
               }
           }
    
           Calendar cal = null;
       	// 此處使用了簡單工廠模式來建立Calender物件 通過aLocale不同的字尾
           if (aLocale.hasExtensions()) {
               String caltype = aLocale.getUnicodeLocaleType("ca");
               if (caltype != null) {
                   switch (caltype) {
                   case "buddhist":
                   cal = new BuddhistCalendar(zone, aLocale);
                       break;
                   case "japanese":
                       cal = new JapaneseImperialCalendar(zone, aLocale);
                       break;
                   case "gregory":
                       cal = new GregorianCalendar(zone, aLocale);
                       break;
                   }
               }
           }
       
           ......
               
           return cal;
       }
    

注意事項:

  • 意義:將例項化物件的的程式碼抽取出來,放到一個類中同一管理和維護,在專案中達到依賴解耦的目的,提高專案的擴充套件性和維護性
  • 依賴抽象原則(儘量依賴抽象的東西)
    • 建立物件時,不要使用new,而是將new的東西放在一個工廠中並返回
    • 不要讓類繼承一個具體的類,而是繼承抽象類或者實現介面,不要覆蓋基類彙總已經實現的方法

相關文章