工厂模式的意义
更符合面向对象思想,将创建对象交给工厂实现,主代码跟创建过程分开
工厂模式的实践(简单工厂和工厂方法)
动手走一遍体会一下工厂模式的好处
需求:根据传入的url获得不同的资源(http协议,ftp协议的)
- 需要的两个类:资源类和资源加载出错的异常
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Resource {
private String url;
}
package com.hillky.desgin_learn.Factory;
public class ResourceLoadException extends RuntimeException{
public ResourceLoadException() {
super("加载资源出现问题");
}
public ResourceLoadException(String message) {
super(message);
}
}
- 加载资源的最简单的实现
public Resource load(String filePath) {
String prefix = getResourcePrefix(filePath);
Resource resource = null;
if ("http".equals(type)) {
// ..发起请求下载资源... 可能很复杂
return new Resource(url);
} else if ("file".equals(type)) {
// ..建立流,做异常处理等等
return new Resource(url);
} else if ("classpath".equals(type)) {
// ...
return new Resource(url);
} else {
return new Resource("default");
}
return resource;
}
private String getPrefix(String url) {
if (url == null ||
"".equals(url) ||
!url.contains(":")) {
throw new ResourceLoadException("此资源url不合法.");
}
String[] split = url.split(":");
return split[0];
}
- 充斥许多分支,但本质就是一个创建资源,可以使用简单工厂模式,创建一个工厂类
public class ResourceFactory {
public static Resource create(String type,String url){
if("http".equals(type)){
// ..发起请求下载资源... 可能很复杂
return new Resource(url);
} else if ("file".equals(type)) {
// ..建立流,做异常处理等等
return new Resource(url);
} else if ("classpath".equals(type)) {
// ...
return new Resource(url);
} else {
return new Resource("default");
}
}
}
主要逻辑得到简化
public class ResourceLoader {
public Resource load(String url){
// 1、根据url获取前缀
String prefix = getPrefix(url);
// 2、根据前缀处理不同的资源
return ResourceFactory.create(prefix,url);
}
private String getPrefix(String url) {
if(url == null || "".equals(url) || !url.contains(":")){
throw new ResourceLoadException("此资源url不合法.");
}
String[] split = url.split(":");
return split[0];
}
}
- 不符合开闭原则,拓展的时候需要更改,这时候需要使用到工厂方法模式
工厂类抽象
public interface IResourceLoader {
Resource load(String url);
}
并为每一种资源创建与之匹配的实现:
public class ClassPathResourceLoader implements IResourceLoader {
@Override
public Resource load(String url) {
// 中间省略复杂的创建过程
return new Resource(url);
}
}
public class FileResourceLoader implements IResourceLoader {
@Override
public Resource load(String url) {
// 中间省略复杂的创建过程
return new Resource(url);
}
}
public class HttpResourceLoader implements IResourceLoader {
@Override
public Resource load(String url) {
// 中间省略复杂的创建过程
return new Resource(url);
}
}
public class FtpResourceLoader implements IResourceLoader {
@Override
public Resource load(String url) {
// 中间省略复杂的创建过程
return new Resource(url);}
}
public class DefaultResourceLoader implements IResourceLoader {
@Override
public Resource load(String url) {
// 中间省略复杂的创建过程
return new Resource(url);
}
}
主逻辑
public class ResourceLoader {
public Resource load(String url){
// 1、根据url获取前缀
String prefix = getPrefix(url);
ResourceLoader resourceLoader = null;
// 2、根据前缀选择不同的工厂,生产独自的产品
// 版本一
if("http".equals(prefix)){
resourceLoader = new HttpResourceLoader();
} else if ("file".equals(prefix)) {
resourceLoader = new FileResourceLoader();
} else if ("classpath".equals(prefix)) {
resourceLoader = new ClassPathResourceLoader()
} else {
resourceLoader = new DefaultResourceLoader();
}
return resourceLoader.load();
}
private String getResourcePrefix(String filePath) {
if (filePath == null || "".equals(filePath)) {
throw new RuntimeException("The file path is illegal");
}
filePath = filePath.trim().toLowerCase();
String[] split = filePath.split(":");
if (split.length > 1) {
return split[0];
} else {
return "classpath";
}
}
}
缓存优化
//添加静态缓存不用为创建工厂付出代价
private static HashMap<String,IResourceLoader> resourceLoaderCache=new HashMap<>();
//自己配置
// static {
// resourceLoaderCache.put("http",new HttpResourceLoader());
// resourceLoaderCache.put("file",new FileResourceLoader());
// resourceLoaderCache.put("classpath",new ClassPathResourceLoader());
// resourceLoaderCache.put("default",new DefaultResourceLoader());
// }
根据配置文件读取 完全符合开闭原则
http=com.ydlclass.factoryMethod.resourceFactory.impl.HttpResourceLoader
file=com.ydlclass.factoryMethod.resourceFactory.impl.FileResourceLoader
classpath=com.ydlclass.factoryMethod.resourceFactory.impl.ClassPathResource
Loader
default=com.ydlclass.factoryMethod.resourceFactory.impl.DefaultResourceLoad
er
//通过配置文件读取,修改配置即可
static {
InputStream inputStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("resourceLoader.properties");
Properties properties = new Properties();
try {
properties.load(inputStream);
for (Map.Entry<Object, Object> entry : properties.entrySet()) {
String key = entry.getKey().toString();
Class clazz=Class.forName(entry.getValue().toString());
IResourceLoader loader= (IResourceLoader) clazz.getConstructor().newInstance();
resourceLoaderCache.put(key,loader);
}
} catch (IOException | ClassNotFoundException | NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
}
源码应用
● jdk中的Calendar,jdk中的日历类可以根据时区、地点创建一个满足当时需求的日历实例,这就是一个简单工厂
● spring中的bean工厂就是一个典型的简单工厂设计模式:
beanFactory.getBean(“userService”);
● spring中的FactoryBean提供了三个方法,其中getObject就是一个典型的工厂方法
总结
- 创建逻辑比较复杂,可以考虑工厂模式,将对象的创建和使用分离开
- 当每个对象的创建逻辑都比较复杂的时候,为了避免设计一个过于庞大的简单工厂类,推荐使用工厂方法模式,将创建逻辑拆分得更细,每个对象的创建逻辑独立到各自的工厂类中。
- 比如数据库连接的创建:不同的数据库的连接对象的建立都是不同的且比较复杂,就可以用工厂方法模式