<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          沒事最好不要隨便用工廠模式

          共 11001字,需瀏覽 23分鐘

           ·

          2021-09-17 22:08

          在軟件開發(fā)過程中,我們會(huì)用到很多設(shè)計(jì)模式,上一篇介紹過的單例模式,還有后面會(huì)介紹的代理模式、適配器模式、建造者模式等等,合適的場景運(yùn)用合適的設(shè)計(jì)模式,你會(huì)發(fā)現(xiàn)業(yè)務(wù)邏輯會(huì)清晰很多。
          但是對于工廠模式,我奉勸大家,沒事最好不要用,什么,你問為啥?

          1、什么是工廠模式

          Define an interface for creating an object,but let subclasses decide which class toinstantiate.Factory Method lets a class defer instantiation to subclasses.

          定義一個(gè)創(chuàng)建對象的接口,讓其子類自己決定實(shí)例化哪一個(gè)工廠類,工廠模式使其創(chuàng)建過程延遲到子類進(jìn)行。

          說人話:提供創(chuàng)建對象的接口,將創(chuàng)建對象的過程屏蔽,從而達(dá)到靈活的目的。

          2、工廠模式分類

          一般情況下,工廠模式分為三類:

          ①、簡單工廠模式(Simple Factory)

          ②、工廠方法模式(Factory Method)

          ③、抽象工廠模式(Abstract Factory)

          這三種模式從上到下逐步抽象,并且更具一般性。

          需要說明的是:GOF 在《設(shè)計(jì)模式》一書中將工廠模式分為兩類:工廠方法模式(Factory Method)與抽象工廠模式(Abstract Factory),將簡單工廠模式(Simple Factory)看為工廠方法模式的一種特例,兩者歸為一類。

          下面我們分別介紹這三種工廠模式。

          2.1 簡單工廠(Simple Factory)

          比如有這樣一個(gè)需求:

          根據(jù)導(dǎo)入的不同文件(docx,xlsx,pptx),選擇不同的解析器進(jìn)行解析。

          簡單工廠有三個(gè)核心對象:

          1.工廠:簡單工廠模式的核心,它負(fù)責(zé)實(shí)現(xiàn)創(chuàng)建所有實(shí)例的內(nèi)部邏輯。工廠類的創(chuàng)建產(chǎn)品類的方法可以被外界直接調(diào)用,創(chuàng)建所需的產(chǎn)品對象。

          2.抽象產(chǎn)品 :簡單工廠模式所創(chuàng)建的所有對象的父類,它負(fù)責(zé)描述所有實(shí)例所共有的公共接口。

          3.具體產(chǎn)品:是簡單工廠模式的創(chuàng)建目標(biāo),所有創(chuàng)建的對象都是充當(dāng)這個(gè)角色的某個(gè)具體類的實(shí)例。

          ①、抽象解析器

          public interface IOfficeParser {
              void parse();
          }

          ②、具體解析器(docx,xlsx,pptx)

          public class WordParser implements IOfficeParser{
              private String filePath;
              public WordParser(String filePath){
                  this.filePath = filePath;
              }
              @Override
              public void parse() {
                  System.out.println("解析 docx 文件");
              }
          }
          public class ExcelParser implements IOfficeParser{
              private String filePath;
              public ExcelParser(String filePath){
                  this.filePath = filePath;
              }
              @Override
              public void parse() {
                  System.out.println("解析 xlsx 文件");
              }
          }
          public class PptParser implements IOfficeParser {
              private String filePath;
              public PptParser(String filePath){
                  this.filePath = filePath;
              }
              @Override
              public void parse() {
                  System.out.println("解析 pptx 文件");
              }
          }

          ③、構(gòu)造解析器的工廠

          public class OfficeParserFactory {

              public static IOfficeParser getParser(String filePath) throws Exception {
                  String fileExtension = getFileExtension(filePath);
                  IOfficeParser parser = null;
                  if("docx".equalsIgnoreCase(fileExtension)){
                      parser = new WordParser(filePath);
                  }else if("xlsx".equalsIgnoreCase(fileExtension)){
                      parser = new ExcelParser(filePath);
                  }else if("pptx".equalsIgnoreCase(fileExtension)){
                      parser = new PptParser(filePath);
                  }else{
                      throw new Exception("file is not supported:"+fileExtension);
                  }
                  return parser;
              }

              private static String getFileExtension(String filePath){
                  // 解析文件名獲取文件擴(kuò)展名,比如 文檔.docx,返回 docx
                  String fileExtension = filePath.substring(filePath.lastIndexOf(".")+1);
                  return fileExtension;
              }
          }

          ④、測試類

          public class SimpleFactoryTest {

              public static void main(String[] args) throws Exception {
                  String filePath = "文檔.docx";
                  IOfficeParser parser = OfficeParserFactory.getParser(filePath);
                  parser.parse();

                  String filePath1 = "表格.xlsx";
                  IOfficeParser parser1 = OfficeParserFactory.getParser(filePath1);
                  parser1.parse();
              }
          }

          ⑤、總結(jié)

          這便是簡單工廠,客戶端避免了直接創(chuàng)建解析器的責(zé)任,只需要調(diào)用工廠類去解析就行了。

          可以從開閉原則(對擴(kuò)展開放,對修改關(guān)閉)來分析簡單工廠模式:當(dāng)增加一種文件解析,比如老版本的 doc 格式。這時(shí)候只需要新增一個(gè) parser 類即可,客戶端(理解為測試類,調(diào)用端)不用改變,然后在工廠類 OfficeParserFactory 新增一個(gè) else-if 分支即可。

          這時(shí)候可能有同學(xué)會(huì)問了,那修改了 OfficeParserFactory 類,不就違反開閉原則了嗎?但其實(shí)只要不是頻繁的添加新的 parser,偶爾修改一下 OfficeParserFactory 類,稍微不符合開閉原則,也是可以接受的。

          看上去比較完美,細(xì)心的同學(xué)可能會(huì)問,所有的解析類對象創(chuàng)建都在 OfficeParserFactory 類中,假設(shè)某個(gè)解析類,比如 doc 創(chuàng)建parser 對象并不是簡單的 new ,還包括一些其它的操作,這時(shí)候難道把這些代碼也全部寫到 OfficeParserFactory 中嗎?有沒有更優(yōu)雅的寫法呢?

          有,就是下面要介紹的 工廠模式。

          2.2 工廠方法(Factory Method)

          為了解決上面的問題,我們可以為工廠類在創(chuàng)建一個(gè)工廠,也就是工廠的工廠,用來創(chuàng)建工廠類對象。

          ①、給每一個(gè)具體解析器創(chuàng)建工廠

          public class ExcelParserFactory implements IOfficeParserFactory {

              @Override
              public IOfficeParser createParser() {
                  // TODO 進(jìn)行創(chuàng)建對象的一些操作
                  return new ExcelParser();
              }
          }

          ②、創(chuàng)建解析器的工廠

          public class OfficeParserFactory {

              public static IOfficeParser getParser(String filePath) throws Exception {
                  String fileExtension = getFileExtension(filePath);
                  IOfficeParserFactory parserFactory = OfficeParserFactoryMap.getOfficeParseFactory(fileExtension);
                  if(parserFactory == null){
                      throw new Exception("file is not supported:"+fileExtension);
                  }
                  IOfficeParser parser = parserFactory.createParser();
                  return parser;
              }

              private static String getFileExtension(String filePath){
                  // 解析文件名獲取文件擴(kuò)展名,比如 文檔.docx,返回 docx
                  String fileExtension = filePath.substring(filePath.lastIndexOf(".")+1);
                  return fileExtension;
              }
          }

          ③、創(chuàng)建解析器工廠的工廠類

          public class OfficeParserFactoryMap {
              private static final Map<String, IOfficeParserFactory> parserFactoryCached = new HashMap<>();
              static {
                  parserFactoryCached.put("docx",new WordParserFactory());
                  parserFactoryCached.put("xlxs",new ExcelParserFactory());
                  parserFactoryCached.put("pptx",new PptParserFactory());
              }
              public static IOfficeParserFactory getOfficeParseFactory(String type){
                  if(type == null || type.isEmpty()){
                      return null;
                  }
                  return parserFactoryCached.get(type.toLowerCase());
              }


          }

          ④、測試類

          public class FactoryTest {
              public static void main(String[] args) throws Exception {
                  String filePath = "文檔.docx";
                  IOfficeParser parser = OfficeParserFactory.getParser(filePath);
                  parser.parse();
              }
          }

          ⑤、總結(jié)

          在工廠模式中,如果我們要增加新的文件解析,比如 mdb 格式(office access套件),就只需要?jiǎng)?chuàng)建新的 parser 類和 parserFactory 類,并且在 OfficeParserFactoryMap 類中將新的 parserFactory 類添加到 map 中即可。代碼的改動(dòng)非常少,基本上是符合開閉原則的。

          但是,我們看到工廠模式新增了很多 factory 類,會(huì)增加代碼的復(fù)制性,如果每個(gè) factory 類只是做簡單的 new 操作,則沒必要使用該模式,直接用簡單工廠模式即可。

          2.3 抽象工廠(Abstract Factory)

          這種模式比較特殊,使用場景不多,大家簡單了解一下就行。

          我們知道 doc 和 docx 都是 office word 文檔后綴,類似 xls 和 xlsx 都是 office Excel 表格后綴,還有 ppt 和 pptx。doc/xlx/ppt 都是舊版本 office 文件后綴,都是二進(jìn)制組成,解析的時(shí)候有共同之處,而 docx/xlsx/pptx 是office新版本文件后綴,是通過 ooxml 結(jié)構(gòu)組成。相當(dāng)于一組是老的office,一組是新的office。

          如果我們還是用工廠模式來實(shí)現(xiàn)的話,那每一種都要編寫一個(gè)工廠類,過多的類會(huì)難以維護(hù),那怎么解決呢?

          抽象工廠模式就是針對這種特殊的場景誕生,我們可以讓一個(gè)工廠復(fù)制創(chuàng)建多個(gè)不同類型的對象,而不是只創(chuàng)建一個(gè) parser 對象。

          具體代碼實(shí)現(xiàn)如下:

          public interface IOfficeParserFactory {

              IOfficeParser createParser();

              IOldOfficeParser createOldParser();
          }
          public class ExcelParserFactory implements IOfficeParserFactory {

              @Override
              public IOfficeParser createParser() {
                  return new ExcelParser();
              }

              @Override
              public IOldOfficeParser createOldParser() {
                  return new DocParser();
              }
          }

          3、簡單工廠和工廠方法區(qū)別

          簡單工廠:將創(chuàng)建不同對象的邏輯放在一個(gè)工廠類中。

          工廠方法:將創(chuàng)建不同對象的邏輯放在不同工廠類中,先用一個(gè)工廠類的工廠類得到某個(gè)工廠,在某這個(gè)工廠來創(chuàng)建對象。

          這樣講區(qū)別就很明顯了,如果創(chuàng)建對象的邏輯比較復(fù)雜,要做各種初始化操作,這時(shí)候使用工廠方法,能夠?qū)?fù)雜的創(chuàng)建邏輯拆分到多個(gè)工廠類中;而創(chuàng)建對象的邏輯很簡單,就沒必要額外創(chuàng)建多個(gè)工廠類,直接使用簡單工廠即可。

          4、工廠模式的作用

          封裝變化:創(chuàng)建邏輯有可能變化,封裝成工廠類之后,創(chuàng)建邏輯的變更對調(diào)用者透明。

          代碼復(fù)用:創(chuàng)建代碼抽離到獨(dú)立的工廠類之后可以復(fù)用。

          隔離復(fù)雜性:封裝復(fù)雜的創(chuàng)建邏輯,調(diào)用者無需了解如何創(chuàng)建對象。

          控制復(fù)雜度:將創(chuàng)建代碼抽離出來,讓原本的函數(shù)或類職責(zé)更單一,代碼更簡潔。

          看完知道為啥沒事別用工廠模式了吧,因?yàn)閷?shí)在是太好用了,你會(huì)愛上它的。(狗頭.gif)

          關(guān)于我

          可樂是一個(gè)熱愛技術(shù)的Java程序猿,公眾號(hào)「IT可樂」定期分享有趣有料的精品原創(chuàng)文章!

          非常感謝各位人才能看到這里,原創(chuàng)不易,文章如果有幫助可以關(guān)注、點(diǎn)贊、分享或評(píng)論,這都是對我的莫大支持!

          愿你我人生盡量沒有遺憾的事,愿你我都能奔赴在各自想去的路上。


          瀏覽 96
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  亚洲200p | 狠狠综合久久 | 爱情岛 论坛成人AV | 黄色片免费视频网站 | 日本内射黄页 |