• 抽象工厂模式


    业务场景

    我们经常有这样子的代码

    我们根据配置文件的后缀(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,那么所有的工厂包括工厂接口都面临修改

  • 相关阅读:
    CRM
    eclipse 全局替换
    ps 泡泡
    SSH重新登录的问题
    又说oracle spatial 将Geometry转为gml
    其实你可以这样折腾java enum
    Sqlite 多线程入库
    Oracle spatial 将Geometry转换为gml字符串
    Oracle 关于WKT构造SDO_GEOMETRY的问题。
    Lucene之拉框查询
  • 原文地址:https://www.cnblogs.com/cg-ww/p/15366092.html
Copyright © 2020-2023  润新知