设计模式之工厂方法模式

什么时候需要

  • 对象的创建与使用分离
  • 对于不同类型的产品创建,使用不同的工厂类实现
  • 对象创建难度不同,比如某个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中还有很多其它抽象工厂的实例

其它

  • 如果创建的对象比较单一、灵活性要求不高,可以使用简单工厂模式
posted @ 2020-07-12 10:43  java拌饭  阅读(173)  评论(0编辑  收藏  举报