• 设计模式之工厂方法模式


    什么时候需要

    • 对象的创建与使用分离
    • 对于不同类型的产品创建,使用不同的工厂类实现
    • 对象创建难度不同,比如某个service用到dao,dao的实现有redis,mysql,mongodb,创建dao的难度不同,需要的细节不同,如果把创建代码放到service里面,就会显得臃肿难以维护

    模式结构

    • 抽象产品:Product
    • 具体产品:ConcreteProduct
    • 抽象工厂:Factory
    • 具体工厂:ConcreteFactory

    模式实现

    interface Product {
    	void otherMethods();
    }
    
    class ConcreteProductA implements Product {
    
    	@Override
    	public void otherMethods() {
    		// do something
    	}
    
    }
    
    class ConcreteProductB implements Product {
    
    	@Override
    	public void otherMethods() {
    		// do something
    	}
    
    }
    
    interface Factory {
    	Product createProduct();
    }
    
    class ConcreteProductAFactory implements Factory {
    
    	@Override
    	public Product createProduct() {
    		return new ConcreteProductA();
    	}
    
    }
    
    class ConcreteProductBFactory implements Factory {
    
    	@Override
    	public Product createProduct() {
    		return new ConcreteProductB();
    	}
    
    }
    
    • 通过实例化不同的具体工厂,调用createProduct方法,创建不同的具体产品
    • 客户端不需要知道具体产品细节,只需要知道具体工厂
    • 新增具体产品只需要新增具体工厂,符合开闭原则
    • 工厂方法可以重载,通过多种方式初始化同一个产品类
    interface Factory {
    	Product createProduct();
    	Product createProduct(String name);
    	Product createProduct(Object obj);
    }
    
    • 这里创建了工厂,然后创建产品,最后调用产品的方法实现业务逻辑,还可以通过工厂类实现工厂方法的隐藏减少客户端具体产品的了解
    abstract class Factory {
    
    	public abstract Product createProduct();
    
    	public void otherMethods() {
    		createProduct().otherMethods();
    	}
    }
    
    • 客户端创建具体工厂后直接调用业务方法即可

    优点

    • 符合开闭原则
    • 隐藏创建细节
    • 多态性设计

    缺点

    • 类膨胀
    • 增加抽象性和理解难度

    使用实例

    Spring

    • Object:抽象产品
    • Object子类:具体产品
    • BeanFactory:抽象工厂
    • XmlBeanFactory、SimpleJndiBeanFactory、DefaultListableBeanFactory、ApplicationContext具体实现等:具体工厂

    Dubbo

    • Registry:抽象产品
    • ZookeeperRegistry、ZookeeperRegistry、NacosRegistry等:具体产品
    • RegistryFactory:抽象工厂
    • ZookeeperRegistryFactory、RedisRegistryFactory、NacosRegistryFactory等:具体工厂
    @SPI("dubbo")
    public interface RegistryFactory {
    
        @Adaptive({"protocol"})
        Registry getRegistry(URL url);
    
    }
    public class NacosRegistryFactory extends AbstractRegistryFactory {
    
        private final Logger logger = LoggerFactory.getLogger(getClass());
    
        protected Registry createRegistry(URL url) {
            return new NacosRegistry(url, buildNamingService(url));
        }
    
        private NamingService buildNamingService(URL url) {
            Properties nacosProperties = buildNacosProperties(url);
            NamingService namingService = null;
            try {
                namingService = NacosFactory.createNamingService(nacosProperties);
            } catch (NacosException e) {
                if (logger.isErrorEnabled()) {
                    logger.error(e.getErrMsg(), e);
                }
                throw new IllegalStateException(e);
            }
            return namingService;
        }
    
        private Properties buildNacosProperties(URL url) {
            Properties properties = new Properties();
            setServerAddr(url, properties);
            setProperties(url, properties);
            return properties;
        }
    
        private void setServerAddr(URL url, Properties properties) {
            StringBuilder serverAddrBuilder =
                    new StringBuilder(url.getHost()) // Host
                            .append(":")
                            .append(url.getPort()); // Port
    
            // Append backup parameter as other servers
            String backup = url.getParameter(BACKUP_KEY);
            if (backup != null) {
                serverAddrBuilder.append(",").append(backup);
            }
    
            String serverAddr = serverAddrBuilder.toString();
            properties.put(SERVER_ADDR, serverAddr);
        }
    
        private void setProperties(URL url, Properties properties) {
            putPropertyIfAbsent(url, properties, NAMESPACE);
            putPropertyIfAbsent(url, properties, NACOS_NAMING_LOG_NAME);
            putPropertyIfAbsent(url, properties, ENDPOINT);
            putPropertyIfAbsent(url, properties, ACCESS_KEY);
            putPropertyIfAbsent(url, properties, SECRET_KEY);
            putPropertyIfAbsent(url, properties, CLUSTER_NAME);
        }
    
        private void putPropertyIfAbsent(URL url, Properties properties, String propertyName) {
            String propertyValue = url.getParameter(propertyName);
            if (StringUtils.isNotEmpty(propertyValue)) {
                properties.setProperty(propertyName, propertyValue);
            }
        }
    }
    public class RedisRegistryFactory extends AbstractRegistryFactory {
    
        @Override
        protected Registry createRegistry(URL url) {
            return new RedisRegistry(url);
        }
    
    }
    
    • 可以看到NacosRegistryFactory就很复杂,而NacosRegistryFactory很简单,如果把NacosRegistryFactory里面的创建代码放到业务代码里面去,无疑会很糟糕
    • Dubbo中还有很多其它抽象工厂的实例

    其它

    • 如果创建的对象比较单一、灵活性要求不高,可以使用简单工厂模式
  • 相关阅读:
    Maven 查看jar包的间接依赖
    Maven 下载一个 jar 包 及其它所有依赖的jar 到本地
    maven 自动构建 web 工程模板命令
    Linux Centos系统中设置定时重启
    (转)关于Android中为什么主线程不会因为Looper.loop()里的死循环卡死?引发的思考,事实可能不是一个 epoll 那么 简单。
    Linux中设置系统时间和时区
    版本管理工具svn(转)
    #pragma预处理命令
    插入排序
    选择排序
  • 原文地址:https://www.cnblogs.com/zby9527/p/13287326.html
Copyright © 2020-2023  润新知