什么时候需要
- 对象的创建与使用分离
- 对于不同类型的产品创建,使用不同的工厂类实现
- 对象创建难度不同,比如某个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中还有很多其它抽象工厂的实例
其它
- 如果创建的对象比较单一、灵活性要求不高,可以使用简单工厂模式