设计模式(二)

本文介绍了简单工厂和工厂方法两种设计模式,强调了它们在代码组织和扩展性上的优势。简单工厂模式通过静态工厂方法封装对象创建过程,使代码更易读和测试。工厂方法模式进一步抽象,将具体产品创建交给专门的工厂类,遵循开闭原则。文章还展示了如何通过缓存和配置文件优化工厂方法,以及如何结合抽象类和接口处理复杂产品结构。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

二、工厂设计模式

1、简单工厂

        简单工厂是静态工厂方法模式。简单工厂就是你给一个type,返回给你一个对象,整个创建过程可以放在工厂里面。

public class ResourceLoader {

    public Resource load(String url) {
        // 根据url获取前缀
        String prefix = getPrefix(url);
        Resource resource = null;
        // 根据前缀获取资源
        return ResourceFactory.create(prefix, url);
    }

    private String getPrefix(String url) {
        if (null == url || "".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");
        }
    }

}

        如上面代码举例,将根据if-else创建实例的业务逻辑抽离出来,放到ResourceFactory工厂类中 。有了工厂类,我们可以将创建资源产品这个单一的能力赋予产品工厂,这样更符合单一原则。

        简单工厂设计模式,提取一个工厂类,工厂会根据传入的不同参数类型,当需要某个产品时直接使用create创建就可以,不用关注具体的创建过程,具体好处如下:

(1)工厂将创建的过程进行封装,不需要关系创建的细节,更加符合面向对象思想;

(2)这样主要的业务逻辑不会被创建对象的代码干扰,代码更易阅读;

(3)产品的创建可以独立测试,更将容易测试;

(4)独立的工厂类只负责创建产品,更加符合单一原则。

2、工厂方法

        如果有一天,我们的if分支逻辑不断膨胀,有变为肿瘤代码的可能,就有必要将if分支逻辑去掉,为了解决这个问题,工厂方法设计模式出现了。之前的简单工厂是一个大而全的工厂,一个工厂需要创建不同的产品,工厂方法讲究的是工厂也要专而精。

        首先,我们需要将生产资源的工厂类进行抽象:

public interface IResourceLoader {
    Resource load(String url);
}

        具体的资源要实现这个抽象的接口:

public class ClassloaderResourceFactoryImpl implements IResourceLoader {
    @Override
    public Resource load(String url) {
        // 省略复杂的创建过程...
        return new Resource(url);
    }
}

public class DefaultResourceFactoryImpl implements IResourceLoader {
    @Override
    public Resource load(String url) {
        // 省略复杂的创建过程...
        return new Resource(url);
    }
}

public class FileResourceFactoryImpl implements IResourceLoader {
    @Override
    public Resource load(String url) {
        // 省略复杂的创建过程...
        return new Resource(url);
    }
}

public class HttpResourceFactoryImpl implements IResourceLoader {
    @Override
    public Resource load(String url) {
        // 省略复杂的创建过程...
        return new Resource(url);
    }
}

        这样,之后我们只要想新增一种资源加载的方法,就去实现IResourceLoader 接口,工厂方法模式相比于简单工厂模式更符合开闭原则。

public class ResourceLoader {

    String prefix = getPrefix(url);
    ResourceLoader resourceLoader = null;

    public Resource load(String url) {
        // 2. 根据前缀选择不同的工厂,产生独自的产品
        if ("http".equals(prefix)) {
            resourceLoader = new HttpResourceFactoryImpl();
        } else if ("file".equals(prefix)) {
            resourceLoader = new FileResourceFactoryImpl();
        } else if ("classpath".equals(prefix)) {
            resourceLoader = new ClassloaderResourceFactoryImpl();
        } else {
            resourceLoader = new DefaultResourceFactoryImpl();
        }
        return resourceLoader.load(url);
    }

    private String getPrefix(String url) {
        if (null == url || "".equals(url) || !url.contains(":")) {
            throw new ResourceLoadException("传入资源的url不合法");
        }
        String[] split = url.split(":");
        return split[0];
    }

}

        但是,这样改造代码后,我们增加资源加载方法仍然需要增加if-else分支,不符合开闭原则,所以,进行如下改造:

public class ResourceLoader {

    private static Map<String,IResourceLoader> resourceLoaderCache = new HashMap<>(8);

    static {
        resourceLoaderCache.put("http",new HttpResourceLoader());
        resourceLoaderCache.put("file",new FileResourceLoader());
        resourceLoaderCache.put("classpath",new ClassPathResourceLoader());
        resourceLoaderCache.put("default",new DefaultResourceLoader());
    }

    public Resource load(String url){
        // 1、根据url获取前缀
        String prefix = getPrefix(url);
        return resourceLoaderCache.get(prefix).load(url);
    }

    private String getPrefix(String url) {
        if (null == url || "".equals(url) || !url.contains(":")) {
            throw new ResourceLoadException("传入资源的url不合法");
        }
        String[] split = url.split(":");
        return split[0];
    }

}

        通过加入缓存的方式,可以减去if-else的复杂分支逻辑,之后如果增加具体资源加载方法,只需要实现IResourceLoader接口,并在缓存中注册即可。

        虽然已经极大的优化了我们的代码,但是我们发现,新增方法仍然需要,修改我们static中的源代码,还是有一点点不符合开闭原则,所以还可以用我们的配置文件进行优化:

        首先,我们要搞一个配置文件,文件名为resourceLoader.properties。

http=com.ydlclass.factoryMethod.resourceFactory.impl.HttpResourceLoader
file=com.ydlclass.factoryMethod.resourceFactory.impl.FileResourceLoader
classpath=com.ydlclass.factoryMethod.resourceFactory.impl.ClassPathResourceLoader
default=com.ydlclass.factoryMethod.resourceFactory.impl.DefaultResourceLoader
public class ResourceLoader {

    private static Map<String,IResourceLoader> resourceLoaderCache = new HashMap<>(8);

    static {
        // 通过输入流加载配置文件
        InputStream in = Thread.currentThread().getContextClassLoader().getResourceAsStream("resourceLoader.properties");
        Properties properties = new Properties();
        try {
            // 调用properties的load方法将输入流加载到内存
            properties.load(in);
            for (Map.Entry<Object, Object> property : properties.entrySet()) {
                String key = property.getKey().toString();
                Class<?> clazz = Class.forName(property.getValue().toString());
                IResourceLoader loader = (IResourceLoader)clazz.getConstructor().newInstance();
                resourceLoaderCache.put(key, loader);
            }
        } catch (IOException | ClassNotFoundException | NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e) {
            throw new RuntimeException(e);
        }
    }

    public Resource load(String url){
        // 1、根据url获取前缀
        String prefix = getPrefix(url);
        return resourceLoaderCache.get(prefix).load(url);
    }

    private String getPrefix(String url) {
        if (null == url || "".equals(url) || !url.contains(":")) {
            throw new ResourceLoadException("传入资源的url不合法");
        }
        String[] split = url.split(":");
        return split[0];
    }

}

        以后我们想新增或删除一个resourceLoader只需要写一个类实现IResourceLoader接口,并在配置文件中进行配置即可。此时此刻我们已经看不到if-else的影子了。

        我们的代码中产品是简单单一的类,事实上,在工作中,我们的产品可能是及其复杂的,我们同样需要对整个产品线进行抽象,具体产品继承抽象类。

public abstract class AbstractResource {

    private String url;

    public AbstractResource(){}

    public AbstractResource(String url) {
        this.url = url;
    }

    protected void shared(){
        System.out.println("这是共享方法");
    }

    /**
     * 每个子类需要独自实现的方法
     * @return 字节流
     */
    public abstract InputStream getInputStream();
}
public class ClasspathResource extends AbstractResource {

    public ClasspathResource() {
    }

    public ClasspathResource(String url) {
        super(url);
    }

    @Override
    public InputStream getInputStream() {
        return null;
    }
}

public class HttpResource extends AbstractResource {

    public HttpResource() {
        super();
    }

    public HttpResource(String url) {
        super(url);
    }

    @Override
    public InputStream getInputStream() {
        return null;
    }
}

public class FileResource extends AbstractResource {

    public FileResource() {
        super();
    }

    public FileResource(String url) {
        super(url);
    }

    @Override
    public InputStream getInputStream() {
        return null;
    }
}

public class DefaultResource extends AbstractResource {

    public DefaultResource() {
        super();
    }

    public DefaultResource(String url) {
        super(url);
    }

    @Override
    public InputStream getInputStream() {
        return null;
    }
}

        而我们的具体的工厂也要做一些改变

public class ClassPathResourceLoader implements IResourceLoader {
    @Override
    public AbstractResource load(String url) {
        // 中间省略复杂的创建过程
        return new ClasspathResource(url);
    }
}

// 其他工厂一样要做改变

 3、抽象工厂

        抽象工厂就是要抽象出一个产品族。就是一类产品有多种类型的产品,这里就不过多介绍了,之后如果大家感兴趣我会再更新的。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值