[设计模式] 抽象工厂模式实现文件数据导出功能

一、需求场景

将数据库中的数据进行处理然后使用excel进行导出。

二、数据特点

数据种类(产品族)分为指定几种,种类下面有不同的产品(类似不同工厂有不同的产品),每次触发接口都是使用某一产品作为实体返回。考虑到需求和数据格式决定使用抽象工厂设计模式完成此需求。

三、抽象工厂模式 (参考runoob.com

抽象工厂模式(Abstract Factory Pattern)是围绕一个超级工厂创建其他工厂。该超级工厂又称为其他工厂的工厂。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
意图:提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
主要解决:主要解决接口选择的问题
何时使用:系统的产品有多于一个的产品族,而系统只消费其中某一族的产品。
如何解决:在一个产品族里面,定义多个产品。
关键代码:在一个工厂里聚合多个同类产品。
应用实例:工作了,为了参加一些聚会,肯定有两套或多套衣服吧,比如说有商务装(成套,一系列具体产品)、时尚装(成套,一系列具体产品),甚至对于一个家庭来说,可能有商务女装、商务男装、时尚女装、时尚男装,这些也都是成套的,即一系列具体产品。假设一种情况(现实中是不存在的,要不然,没法进入共产主义了,但有利于说明抽象工厂模式),在您的家中,某一个衣柜(具体工厂)只能存放某一种这样的衣服(成套,一系列具体产品),每次拿这种成套的衣服时也自然要从这个衣柜中取出了。用 OOP 的思想去理解,所有的衣柜(具体工厂)都是衣柜类的(抽象工厂)某一个,而每一件成套的衣服又包括具体的上衣(某一具体产品),裤子(某一具体产品),这些具体的上衣其实也都是上衣(抽象产品),具体的裤子也都是裤子(另一个抽象产品)。
优点:当一个产品族中的多个对象被设计成一起工作时,它能保证客户端始终只使用同一个产品族中的对象。
缺点:产品族扩展非常困难,要增加一个系列的某一产品,既要在抽象的 Creator 里加代码,又要在具体的里面加代码。
使用场景:
1、客户端(应用层)不依赖于产品类实例如何被创建、实现等细节
2、强调一系列相关的产品对象(属于同一产品族)一起使用创建对象需要大量重复的代码
3、提供一个产品类的库,所有的产品以同样的接口出现,从而使客户端不依赖于具体实现
注意事项:产品族难扩展,产品等级易扩展。

三、应用实例

在这里插入图片描述

1. AbstractFactory:

抽象工厂抽象基类,通过抽象基类获取指定工厂

public abstract class AbstractFactory {

    /**
     * 获取工厂A中的指定对象
     */
    public abstract HotQuestion getHotQuestionImpl(String flag);

    /**
     * 获取工厂B中的指定对象
     */
    public abstract ComplaintTwoRate getComplaintTwoRate(String flag);

}

2. FactoryProducer

创建一个工厂创造器/生成器类,通过传递要获取的工厂名称来获取工厂

@Component
@RequiredArgsConstructor
public class FactoryProducer {

    private final ComplaintFactory complaintFactory;
    private final HotQuestionFactory hotQuestionFactory;

    public AbstractFactory getFactory(String choice) {
        if ("ComplaintFactory".equalsIgnoreCase(choice)) {
            return complaintFactory;
        } else if ("HotQuestionFactory".equalsIgnoreCase(choice)) {
            return hotQuestionFactory;
        }
        return null;
    }

}

3. ComplaintFactory

工厂A,在此根据产品名称从工厂中获取指定的产品

@Component
@RequiredArgsConstructor
public class ComplaintFactory extends AbstractFactory {

    private final ComplaintTwoRateAndTwoRatioCity complaintAndTwoRatioCity;
    private final ComplaintTwoRateAndTwoRatioArea complaintAndTwoRatioArea;

    @Override
    public ComplaintTwoRate getComplaintTwoRate(String flag) {
        if ("city".equalsIgnoreCase(flag)) {
            return complaintAndTwoRatioCity;
        } else if ("area".equalsIgnoreCase(flag)) {
            return complaintAndTwoRatioArea;
        } else if ("street".equalsIgnoreCase(flag)) {
            return complaintAndTwoRatioCity;
        }
        return null;
    }

    @Override
    public HotQuestion getHotQuestionImpl(String flag) {
        return null;
    }
}

4. ComplaintTwoRate

为工厂A的产品创建一个接口。此类代表所有工厂A的产品,用于方法函数返回对象。

在抽象工厂模式中,接口是负责创建一个相关对象的工厂,不需要显式指定它们的类。每个生成的工厂都能按照工厂模式提供对象。

public interface ComplaintTwoRate {

    /**
     * 从数据库获取数据
     */
    List<Object> getWriteData(ExportRequest request);

    /**
     * 将别名和数填充到BigExcelWriter中(一个sheet页数据)
     */
    void addAliasAndData(BigExcelWriter writer, List<Object> tList);

}

5. ComplaintTwoRateAndTwoRatioCity

工厂A的产品A。

@Component
@RequiredArgsConstructor
public class ComplaintTwoRateAndTwoRatioCity implements ComplaintTwoRate {

    private final IExportExcelService exportExcelService;
    private final ExportExcelUtil<Object> exportExcelUtil;

    @Override
    public List<Object> getWriteData(ExportRequest request) {
        return new ArrayList<>(exportExcelService.getGoverComplaintTwoRateCityList(request));
    }

    @Override
    public void addAliasAndData(BigExcelWriter writer, List<Object> tList) {
    	...
    }
    
}

6. ComplaintTwoRateAndTwoRatioArea

工厂A的产品B。

@Component
@RequiredArgsConstructor
public class ComplaintTwoRateAndTwoRatioArea implements ComplaintTwoRate {

    private final ExportExcelUtil<Object> exportExcelUtil;

    @Override
    public List<Object> getWriteData(ExportRequest request) {
        return null;
    }

    @Override
    public void addAliasAndData(BigExcelWriter writer, List<Object> tList) {
    	...
    }

}

7. HotQuestionFactory

工厂B,在此根据产品名称从工厂中获取指定的产品

@Component
@RequiredArgsConstructor
public class HotQuestionFactory extends AbstractFactory {

    private final HotQuestionImplCity hotQuestionImplA;

    @Override
    public ComplaintTwoRate getComplaintTwoRate(String flag) {
        return null;
    }

    @Override
    public HotQuestion getHotQuestionImpl(String flag) {
        if ("A".equalsIgnoreCase(flag)) {
            return hotQuestionImplA;
        } else if ("B".equalsIgnoreCase(flag)) {
            return hotQuestionImplA;
        } else if ("C".equalsIgnoreCase(flag)) {
            return hotQuestionImplA;
        }
        return null;
    }
}

8. HotQuestion

为工厂B的产品创建一个接口。此类代表所有工厂B的产品,用于方法函数返回对象。(作用同ComplaintTwoRate)

public interface HotQuestion {

    /**
     * 从数据库获取数据
     */
    List<Object> getWriteData(ExportRequest request);

    /**
     * 将别名和数填充到BigExcelWriter中(一个sheet页数据)
     */
    void addAliasAndData(BigExcelWriter writer, List<Object> tList);
    
}

9. HotQuestionImplCity

工厂B的产品C

@Component
@RequiredArgsConstructor
public class HotQuestionImplCity implements HotQuestion {

    private final ExportExcelUtil<Object> exportExcelUtil;

    @Override
    public List<Object> getWriteData(ExportRequest request) {
        return null;
    }

    @Override
    public void addAliasAndData(BigExcelWriter writer, List<Object> tList) {
        ...
    }

}

10. HotQuestionImplArea

工厂C的产品D

@Component
@RequiredArgsConstructor
public class HotQuestionImplArea implements HotQuestion {

    private final ExportExcelUtil<Object> exportExcelUtil;

    @Override
    public List<Object> getWriteData(ExportRequest request) {
        return null;
    }

    @Override
    public void addAliasAndData(BigExcelWriter writer, List<Object> tList) {
        ...
    }

}

11. FileExportController

使用 FileExportController 来获取 AbstractFactory,通过传递名称来获取实体类的对象。

//获取工厂
AbstractFactory fileExportFactory = factoryProducer.getFactory("ComplaintFactory");
//获取产品
ComplaintTwoRate complaintTwoRateCity = fileExportFactory.getComplaintTwoRate("city");
// 调用方法-填充字段名和数据
complaintTwoRateCity.addAliasAndData(...);

四、总结

1. 根据需求和数据规律确定使用哪种设计模式,该情景属于在创建对象的时候不同其他操作(导出excel)相同,所以需要的是创建型设计模式,考虑数据规律可以使用抽象工厂设计模式

2. 为什么不使用工厂模式而使用抽象工厂模式?

简单工厂模式有一个问题就是,类的创建依赖工厂类,也就是说,如果想要拓展程序,必须对工厂类进行修改。
抽象工厂模式中我们可以定义实现不止一个接口,一个工厂也可以生成不止一个产品类,抽象工厂模式较好的实现了“开放-封闭”原则,是三个模式中较为抽象,并具一般性的模式。

本例中数据种类为产品族,某一数据为产品。
综上抽象工厂模式更合适此情景。

五、所遇问题

  1. 类比较多容易混乱,所以对类名设计严格遵守阿里编码规范中的对类名涉及要求:

【推荐】如果模块、接口、类、方法使用了设计模式,在命名时需体现出具体模式。
说明:将设计模式体现在名字中,有利于阅读者快速理解架构设计理念。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

一杯糖不加咖啡

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

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

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

打赏作者

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

抵扣说明:

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

余额充值