设计模式-工厂模式

工厂模式的意义

更符合面向对象思想,将创建对象交给工厂实现,主代码跟创建过程分开

工厂模式的实践(简单工厂和工厂方法)

动手走一遍体会一下工厂模式的好处

需求:根据传入的url获得不同的资源(http协议,ftp协议的)

  1. 需要的两个类:资源类和资源加载出错的异常
@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);
    }
}
  1. 加载资源的最简单的实现

 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];
    }
  1. 充斥许多分支,但本质就是一个创建资源,可以使用简单工厂模式,创建一个工厂类
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];
    }
}
  1. 不符合开闭原则,拓展的时候需要更改,这时候需要使用到工厂方法模式

工厂类抽象


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就是一个典型的工厂方法

总结

  1. 创建逻辑比较复杂,可以考虑工厂模式,将对象的创建和使用分离开
  2. 当每个对象的创建逻辑都比较复杂的时候,为了避免设计一个过于庞大的简单工厂类,推荐使用工厂方法模式,将创建逻辑拆分得更细,每个对象的创建逻辑独立到各自的工厂类中。
  3. 比如数据库连接的创建:不同的数据库的连接对象的建立都是不同的且比较复杂,就可以用工厂方法模式
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值