抽象工廠模式

業務場景

我們經常有這樣子的程式碼

我們根據配置文件的後綴(json、xml、yaml、properties),選擇不同的解析器(JsonRuleConfigParser、XmlRuleConfigParser……),將存儲在文件中的配置解析成記憶體對象 RuleConfig

 public RuleConfig load(String ruleConfigFilePath) {
        //根據文件獲取文件後綴名
        String ruleConfigFileExtension = getFileExtension(ruleConfigFilePath);
        IRuleConfigParser parser = null;
        if ("json".equalsIgnoreCase(ruleConfigFileExtension)) {
            parser = new JsonRuleConfigParser();
        } else if ("xml".equalsIgnoreCase(ruleConfigFileExtension))  {
            parser = new XmlRuleConfigParser();
        } else if ("yaml".equalsIgnoreCase(ruleConfigFileExtension)) {
            parser = new YamlRuleConfigParser();
        } else if ("properties".equalsIgnoreCase(ruleConfigFileExtension)){
            parser = new PropertiesRuleConfigParser();
        } else
        {
            throw new InvalidRuleConfigException( "Rule config file format is not supported: " + ruleConfigFilePath)
        }
        String configText = "";
        //從ruleConfigFilePath文件中讀取配置文本到configText中
         RuleConfig ruleConfig = parser.parse(configText);
         return ruleConfig;
    }

碰到這種非得要將 if 分支邏輯去掉,想到的解決方式就是利用工廠模式進行優化

工廠模式程式碼重構

解析器工廠介面

public interface IRuleConfigParserFactory {
    IRuleConfigParser createParser();
}

具體解析器工廠

public class JsonRuleConfigParserFactory implements IRuleConfigParserFactory {
    @Override
    public IRuleConfigParser createParser() {
        return new JsonRuleConfigParser();
    }
}
public class PropertiesRuleConfigParserFactory implements IRuleConfigParserFactory {
    @Override
    public IRuleConfigParser createParser() {
        return new PropertiesRuleConfigParser();
    }
}
public class XmlRuleConfigParserFactory implements IRuleConfigParserFactory {
    @Override
    public IRuleConfigParser createParser() {
        return new XmlRuleConfigParser();
    }
}
public class YamlRuleConfigParserFactory implements IRuleConfigParserFactory {
    @Override
    public IRuleConfigParser createParser() {
        return new YamlRuleConfigParser();
    }
}

工廠類實例集合

 public static class RuleConfigParserFactoryMap {
        static  Map<String, IRuleConfigParserFactory> cachedFactories =new HashMap<String, IRuleConfigParserFactory>();
        static
        {
            cachedFactories.put("json", new JsonRuleConfigParserFactory());
            cachedFactories.put("xml", new XmlRuleConfigParserFactory());
            cachedFactories.put("yaml", new YamlRuleConfigParserFactory());
            cachedFactories.put("properties", new PropertiesRuleConfigParserFactory());
        }
    }

主方法

    public static void main(String[] args) {
        IRuleConfigParserFactory parserFactory  =RuleConfigParserFactoryMap.cachedFactories.get("properties");
        parserFactory.createParser();
    }

image-20210927113832174

這樣就完成了

當對象的創建邏輯比較複雜,不只是簡單的 new 一下就可以,而是要組合其他類對象,做各種初始化操作的時候,我們推薦使用工廠方法模式,將複雜的創建邏輯拆分到多個工廠類中,讓每個工廠類都不至於過於複雜。而使用簡單工廠模式,將所有的創建邏輯都放到一個工廠類中,會導致這個工廠類變得很複雜

除此之外,在某些場景下,如果對象不可復用,那工廠類每次都要返回不同的對象。如果使用簡單工廠模式來實現,就只能選擇第一種包含 if 分支邏輯的實現方式。如果還想避免煩人的 if-else 分支邏輯,這個時候,就推薦使用工廠方法模式

抽象工廠

在簡單工廠和工廠方法中,類只有一種分類方式。比如,在規則配置解析那個例子中,解析器類只會根據配置文件格式(Json、Xml、Yaml……)來分類。但是,如果類有兩種分類方式,比如,我們既可以按照配置文件格式來分類,也可以按照解析的對象(Rule 規則配置還是 System 系統配置)來分類,那就會對應下面這 8 個 parser 類

針對規則配置的解析器:基於介面
IRuleConfigParser 
JsonRuleConfigParser 
XmlRuleConfigParser 
YamlRuleConfigParser 
PropertiesRuleConfigParser
針對系統配置的解析器:基於介面
ISystemConfigParser 
JsonSystemConfigParser 
XmlSystemConfigParser 
YamlSystemConfigParser 
PropertiesSystemConfigParser

抽象工廠就是針對這種非常特殊的場景而誕生的。我們可以讓一個工廠負責創建多個不同類型的對象(IRuleConfigParser、ISystemConfigParser 等),而不是只創建一種 parser對象。這樣就可以有效地減少工廠類的個數。具體的程式碼實現如下所示

public interface IConfigParserFactory { 
IRuleConfigParser createRuleParser();
ISystemConfigParser createSystemParser(); 
//此處可以擴展新的parser類型,比如IBizConfigParser
}
public class JsonConfigParserFactory implements IConfigParserFactory {
    @Override 
    public IRuleConfigParser createRuleParser() {
        return new JsonRuleConfigParser(); 
    }
    @Override 
    public ISystemConfigParser createSystemParser() { 
        return new JsonSystemConfigParser(); 
    } 
}
public class XmlConfigParserFactory implements IConfigParserFactory { 
    @Override 
    public IRuleConfigParser createRuleParser() { 
        return new XmlRuleConfigParser();
        }
    @Override
    public ISystemConfigParser createSystemParser() {
        return new XmlSystemConfigParser(); 
    } 
}
////略.....

主啟動類

 public static void main(String[] args) {
        IConfigParserFactory configParserFactory=new JsonConfigParserFactory();
        IRuleConfigParser ruleConfigParser= configParserFactory.createRuleParser();
        ISystemConfigParser systemParser= configParserFactory.createSystemParser();
    }

image-20210927140303793

抽象工廠模式面對的是一個組合體,但是有一種情況,會導致修改原有類,那就是當目標需要在解析器中新增一種解析器類型的時候,比如例子中,解析器組合中只包含SystemRule,如果再添加一種IBiz,那麼所有的工廠包括工廠介面都面臨修改