【工厂模式】重构代码 - 工厂模式用起来

背景

记得刚入行的时候,听过一个段子,同样开发一个功能初级程序员要1天,中级程序员要2天,高级程序员要1个星期。当时以为是老油条划水(虽然肯定还是会划一点水,哈哈),但是其实更重要的还是设计思想的不同,经验丰富的程序员往往考虑的更多,不光是业务拓展性,更有程序拓展性。结合最近的一次重构代码,这里想记录下程序员的内功心法之工厂模式。

我这次重构的功能点是一个图片上传功能,逻辑很简单,就是把图片上传到静态服务器,但是当第一个人写下第一行代码后,并没有把他提炼成一个公共方法,导致后面的同学把这一大段代码当祖传代码用了,只要是上传功能,直接ctrl+c加ctrl+v,结果这一段臃肿的代码到处都是,如果上传逻辑发生了变化,那么结果将是灾难性的。

考虑到我们现在上传用的是AWS的静态服务器,而且以后有可能会用其他文件存储方式,所以我准备把这一簇对象放在一起,要用的时候,再决定用哪一个,这样就自然而然的想到了工厂模式。

思考过程

首先我们需要一个抽象的基类,它是所有服务器的父类。

public abstract class BaseUpDownloader {
    /**
     * 执行文件上传操作(延迟到子类实现)
     *
     * @param businessDir 微服务的上下文路径,如:/img/test/。
     * @param rootBaseDir        存放上传文件的根目录。
     * @param fileName           文件名。
     * @param uploadFile         Http请求中上传的文件对象。
     * @return 图片返回通用对象。
     * @throws Exception 操作错误。
     */
    public abstract String doUpload(
            String businessDir,
            String rootBaseDir,
            String fileName,
            MultipartFile uploadFile) throws Exception;
}

然后准备一个工厂,用于生产不同的合适的对象。

public class UpDownloaderFactory {
/**
 * 注册上传下载对象到工厂。
 *
 * @param upDownloaderName 下载器名称
 * @return {@link BaseUpDownloader}
 */
public BaseUpDownloader registerUpDownloader(String upDownloaderName) {
    if ("aws".equalsIgnoreCase(upDownloaderName)) {
        return new AwsUpDownloader();
    } else if ("ali".equalsIgnoreCase(upDownloaderName)) {
        return new AliOssUpDownloader();
    }
    return null;
}
}

子类一:基于aws的上传功能

public class AwsUpDownloader extends BaseUpDownloader {
    @Override
    public String doUpload(
            String businessDir,
            String rootBaseDir,
            String fileName,
            MultipartFile uploadFile) throws Exception {
        System.out.println("使用AWS进行上传操作");
        return null;
    }
}

子类二:基于ali的上传功能

public class AliOssUpDownloader extends BaseUpDownloader {
    @Override
    public String doUpload(
            String businessDir,
            String rootBaseDir,
            String fileName,
            MultipartFile uploadFile) throws Exception {
        System.out.println("使用阿里云OSS进行上传操作");
        return null;
    }
}

测试类:

public class Test {
    public static void main(String[] args) throws Exception {
        UpDownloaderFactory factory = new UpDownloaderFactory();
        BaseUpDownloader awsUpDownloader = factory.registerUpDownloader("aws");
        if(awsUpDownloader == null){
            return;
        }
        awsUpDownloader.doUpload("","","",null);
    }
}

结果: 图片

使用反射优化代码

写到这里感觉改造就完成了,但是这里其实有个很大的问题,也是传统工厂模式的弊端——

如果我们工厂类里面的方法进行扩展,那么就并不符合开闭原则。那有没有什么办法可以解决这个问题呢?当然有~反射,我们的好朋友~

工厂注册方法演变成这样:

public BaseUpDownloader registerUpDownloader(Class clazz) {
    BaseUpDownloader upDownloader= null;
    try {
        upDownloader = (BaseUpDownloader)Class.forName(clazz.getName()).newInstance();
    } catch (InstantiationException e) {
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    }
    return upDownloader;
}

测试方法:

public class Test {
    public static void main(String[] args) throws Exception {
        UpDownloaderFactory factory = new UpDownloaderFactory();
        BaseUpDownloader awsUpDownloader = factory.registerUpDownloader(AliOssUpDownloader.class);
        if(awsUpDownloader == null){
            return;
        }
        awsUpDownloader.doUpload("","","",null);
    }
}

结果: 图片

使用Spring优化代码

反射解决了开闭原则问题,但是却又引来了新的风暴,反射虽然能拿到我们需要的对象,但是直我们一旦在子类里面调用service等方法就会出现,代理对象为空的问题,这是因为子类没有交接Spring管理。所以我们稍稍优化下代码,将子类交给Spring,并且注册时使用上下文直接拿到Bean实例。

子类一:基于aws的上传功能

@Component
public class AwsUpDownloader extends BaseUpDownloader {
    @Override
    public String doUpload(
            String businessDir,
            String rootBaseDir,
            String fileName,
            MultipartFile uploadFile) throws Exception {
        System.out.println("使用AWS进行上传操作");
        return null;
    }
}

子类二:基于ali的上传功能

@Component
public class AliOssUpDownloader extends BaseUpDownloader {
    @Override
    public String doUpload(
            String businessDir,
            String rootBaseDir,
            String fileName,
            MultipartFile uploadFile) throws Exception {
        System.out.println("使用阿里云OSS进行上传操作");
        return null;
    }
}

工厂:

public class UpDownloaderFactory {
    /**
     * 注册上传下载对象到工厂。
     *
     * @param clazz clazz
     * @return {@link BaseUpDownloader}
     */
    public BaseUpDownloader registerUpDownloader(Class<?> clazz) {
        return (BaseUpDownloader)SpringHelper.getBean(clazz);
    }
}

SpringHelper代码:

public class SpringHelper {
    private static ApplicationContext APPLICATION_CONTEXT;
    /**
     * <p>
     * 获取 applicationContext
     * </p>
     */
    public static ApplicationContext getApplicationContext() {
        return APPLICATION_CONTEXT;
    }
    /**
     * <p>
     * 设置 applicationContext
     * </p>
     */
    public static void setApplicationContext(ApplicationContext applicationContext) {
        if (null == APPLICATION_CONTEXT) {
            APPLICATION_CONTEXT = applicationContext;
        }
    }
    /**
     * <p>
     * 通过class获取Bean
     * </p>
     *
     * @param clazz
     * @param <T>
     * @return
     */
    public static <T> T getBean(Class<T> clazz) {
        try {
            return getApplicationContext().getBean(clazz);
        } catch (Throwable e) {
            e.printStackTrace();
        }
        return null;
    }
}

使用注解优化代码

虽然代码写到这里就优化的差不多了,但是想了想,其实可以做成注解,因为上传下载在数据库一般都有对应的字段,比如说文件名称啥的,只需要自定义一个注解,把相关参数传过去就行了,可是由于时间和其他第三方原因,暂时作罢,这里就留给读者思考一下。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

第七人格

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值