一、工厂办法模式介绍

1. 解决的问题

通过接口的抉择,解决在不同条件下创立不同实例的问题。

2. 定义

工厂办法模式又称工厂模式,是一种创立型设计模式,其在父类中提供一个创建对象的办法,容许子类决定实例化对象的类型。

简略来说就是为了提供代码的扩展性,屏蔽每一个性能类的具体实现逻辑,让内部仅晓得调用即可。这也是去掉多个 if else 的形式,当然也存在一些毛病,如须要实现的类过多,如何保护,升高开发成本。但这些问题能够通过与其余设计模式联合应用,逐渐升高影响。

二、 工厂办法模式优缺点

1. 长处

  1. 能够防止创建者和具体产品之间的严密耦合。
  2. 繁多职责准则:能够将产品创立代码放在程序的繁多地位,从而使代码更容易保护。
  3. 开闭准则:无需更改现有代码,就能够在程序中引入新的产品类型。

2. 毛病

  1. 利用工厂办法模式须要引入许多新的子类,代码可能会因而变得更简单。最好的状况使将该模式引入创建者类的现有层次结构中。

三、 工厂办法模式利用实例:文章、视频、直播内容输入生产场景

1. 实例场景

现在信息飞速发展的时代,文章、视频、直播各种模式的内容输入充斥着咱们的生存,接下来,咱们以UP公布文章、视频、发动直播的创立过程来作为模仿场景。

2. 用一坨坨代码实现

不思考后续减少其余内容的扩展性,仅应答以后需要,目前的实例场景应用 if else 判断调用不同内容生产服务类即可满足需要。

public void createContent(int type, Map<String, Object> dataMap) throws InvocationTargetException, IllegalAccessException {        //初始化        if (type == 1) {            Article result = new Article();        } else if (type == 2) {            Video result = new Video();        } else if (type == 3) {            Live result = new Live();        } else {            return;        }        result.setId(UUID.randomUUID().toString());        //转换dataMap数据        BeanUtils.populate(result, dataMap);        return result;}

仅思考业务性能,上述代码齐全满足需要,开发起来十分快,能够说是 if else 一把梭就完事了。

但如果后续存在迭代或扩大的需要,每次通过一次开发,都须要从新浏览这段代码,查看减少或删除的性能是否对其余类型产生影响,测试回归验证工夫长,须要全面验证。

理论场景中并不存在毫无变动的需要,即便在首次开发过程中,往往也会因为各种问题会导致需要存在变动,那么,尤其是测试过程中,往往咱们手中还会有其余的工作,一旦呈现变动,往往会面临开发工夫被压缩的状况,可想而知,if else 将有限累加,该段代码将越来越长,越来越难以浏览和保护。

3. 工厂模式优化代码

接下来应用工厂模式来对代码进行优化,退出了工厂模式后代码构造清晰、具备后续新增内容类别的扩展性。

3.1 工程构造
factory-pattern└─ src    ├─ main       │    └─ java    │    └─ org.design.pattern.factory    │        ├─ model    │        │  └─ Content.java       │       │  └─ content    │       │          ├─ Article.java      │       │          ├─ Live.java    │       │          └─ Video.java       │       ├─ service       │       │    └─ ContentService.java    │       |        └─impl    │       |           ├─ ArticleContentServiceImpl.java    │       |           ├─ LiveContentServiceImpl.java    │       |           └─ VideoContentServiceImpl.java    │       └─ ContentNum.java    │       └─ ContentServiceFactory.java    └─ test        └─ java            └─ org.design.pattern.factory.test                  └─ ContentServiceFactoryTest.java

从下面的工程构造来看,代码看上去不是清晰了,分层也更容易扩大,简直能够了解每个类的作用了。

3.2 代码实现

3.2.1 定义内容根底类
/** * 内容基类 */public class Content {    /**     * 内容id     */    private String id;    /**     * 用户id     */    private String userId;}

所有的内容都应该蕴含自身的内容 id 以及内容生产方即用户 id。

文章

/** * 文章 */public class Article extends Content {    /**     * 题目     */    private String title;    /**     * 内容     */    private String content;    /**     * 文章分区id     */    private int partitionId;    /**     * 文章分类     */    private String articleType;    /**     * 文章所属话题列表     */    private List<String> topics;}
3.2.2 内容服务接口
/** * 内容服务 */public interface ContentService {    /**     * 生产内容     * @param taskId 工作id     * @param userId 用户id     * @param dataMap 内容数据     * @return 内容     */    Content createContent(String taskId, String userId, Map<String, Object> dataMap) throws InvocationTargetException, IllegalAccessException;}
  • 内容服务的提供都通过实现此接口来解决,以保障最终入参出参的对立。
  • 接口入参:工作 id、用户 id、内容数据 map(用于匹配不同的内容属性)。

文章内容服务实现类

/** * 文章内容服务实现类 */public class ArticleContentServiceImpl implements ContentService {    private final Logger log = LoggerFactory.getLogger(ArticleContentServiceImpl.class);    @Override    public Content createContent(String taskId, String userId, Map<String, Object> dataMap) throws InvocationTargetException, IllegalAccessException {        log.info("task {} => start", taskId);        log.info("task {} => article data is {}", taskId, JSON.toJSON(dataMap));        //初始化        Article result = new Article();        result.setId(UUID.randomUUID().toString());        result.setUserId(userId);        //转换dataMap数据        BeanUtils.populate(result, dataMap);        log.info("task {} => create article success, id:{}, title:{}", taskId, result.getId(), result.getTitle());        return result;    }}
3.2.3 创立内容服务工厂

内容枚举类

/** * 内容枚举类 */public enum ContentNum {    ARTICLE("article"),    VIDEO("video"),    LIVE("live");    ContentNum(String type) {        this.type = type;    }    /**     * 内容类型     */    private String type;}

内容服务工厂

/** * 内容服务工厂 */public class ContentServiceFactory {    private static final Map<String, ContentService> factoryMap = new ConcurrentHashMap<String, ContentService>();    static {        factoryMap.put(ContentNum.ARTICLE.getType(), new ArticleContentServiceImpl());        factoryMap.put(ContentNum.VIDEO.getType(), new VideoContentServiceImpl());        factoryMap.put(ContentNum.LIVE.getType(), new LiveContentServiceImpl());    }    /**     * 获取内容服务     * @param type 内容类型     * @return 内容服务实例     */    public ContentService getContentService(String type) {        return factoryMap.get(type);    }}

咱们定义了一个枚举类来反对各种类型内容的定义,此外在内容服务工厂类中通过 map 的模式来配置各类内容服务的抉择,只须要调用内容服务工厂的 getContentService() ,传入类型,即可获取相应的内容服务实现类,不须要关怀内容服务的具体实现。

3.3 测试验证

3.3.1 编写测试类
public class ContentServiceFactoryTest {    private final ContentServiceFactory contentServiceFactory = new ContentServiceFactory();    @Test    public void createArticle() throws InvocationTargetException, IllegalAccessException {        String taskId = UUID.randomUUID().toString();        String userId = "yiyufxst";        Map<String, Object> dataMap = new HashMap<>();        dataMap.put("title", "初学设计模式:实战工厂模式");        dataMap.put("content", "文章:「文章、视频、直播内容输入生产场景」");        dataMap.put("partitionId", 1);        dataMap.put("articleType", "技术");        dataMap.put("topics", new ArrayList<String>(){            {                this.add("Java");                this.add("设计模式");            }        });        this.contentServiceFactory.getContentService(ContentNum.ARTICLE.getType()).createContent(taskId, userId, dataMap);    }    @Test    public void createVideo() throws InvocationTargetException, IllegalAccessException {        String taskId = UUID.randomUUID().toString();        String userId = "yiyufxst";        Map<String, Object> dataMap = new HashMap<>();        dataMap.put("title", "初学设计模式:实战工厂模式");        dataMap.put("introduction", "视频:「文章、视频、直播多种内容输入生产场景」");        dataMap.put("videoUrl", "//yiyufxst.me/av/1");        dataMap.put("coverUrl", "//yiyufxst.me/images/1");        dataMap.put("partitionId", 1);        this.contentServiceFactory.getContentService(ContentNum.VIDEO.getType()).createContent(taskId, userId, dataMap);    }    @Test    public void createLive() throws InvocationTargetException, IllegalAccessException {        String taskId = UUID.randomUUID().toString();        String userId = "yiyufxst";        Map<String, Object> dataMap = new HashMap<>();        dataMap.put("title", "初学设计模式:实战工厂模式");        dataMap.put("introduction", "直播:「文章、视频、直播多种内容输入生产场景」");        dataMap.put("roomId", 1);        dataMap.put("coverUrl", "//yiyufxst.me/images/2");        dataMap.put("partitionId", 1);        this.contentServiceFactory.getContentService(ContentNum.LIVE.getType()).createContent(taskId, userId, dataMap);    }}
3.3.2 后果
17:39:02.236 [main] INFO  o.d.p.f.s.i.VideoContentServiceImpl - task e4c3be6f-b023-49ed-9643-272d5bb58d2b => start17:39:02.288 [main] INFO  o.d.p.f.s.i.VideoContentServiceImpl - task e4c3be6f-b023-49ed-9643-272d5bb58d2b => video data is {"coverUrl":"//yiyufxst.me/images/1","videoUrl":"//yiyufxst.me/av/1","title":"初学设计模式:实战工厂模式","partitionId":1,"introduction":"视频:「文章、视频、直播多种内容输入生产场景」"}17:39:02.321 [main] INFO  o.d.p.f.s.i.VideoContentServiceImpl - task e4c3be6f-b023-49ed-9643-272d5bb58d2b => create video success, id:df8b1b07-7adf-4f67-98c5-df96ec27e081, title:初学设计模式:实战工厂模式17:39:02.322 [main] INFO  o.d.p.f.s.i.LiveContentServiceImpl - task e41e3d1d-af41-4e16-8bfb-545018a9f196 => start17:39:02.322 [main] INFO  o.d.p.f.s.i.LiveContentServiceImpl - task e41e3d1d-af41-4e16-8bfb-545018a9f196 => live data is {"coverUrl":"//yiyufxst.me/images/2","title":"初学设计模式:实战工厂模式","partitionId":1,"introduction":"直播:「文章、视频、直播多种内容输入生产场景」","roomId":1}17:39:02.323 [main] INFO  o.d.p.f.s.i.LiveContentServiceImpl - task e41e3d1d-af41-4e16-8bfb-545018a9f196 => create live success, id:4e7a9bad-f3be-43f0-9385-d1016220c797, title:初学设计模式:实战工厂模式17:39:02.324 [main] INFO  o.d.p.f.s.i.ArticleContentServiceImpl - task 6d7884b9-493d-4d31-8f08-cace7bc61f2c => start17:39:02.326 [main] INFO  o.d.p.f.s.i.ArticleContentServiceImpl - task 6d7884b9-493d-4d31-8f08-cace7bc61f2c => article data is {"title":"初学设计模式:实战工厂模式","partitionId":1,"articleType":"技术","topics":["Java","设计模式"],"content":"文章:「文章、视频、直播内容输入生产场景」"}17:39:02.330 [main] INFO  o.d.p.f.s.i.ArticleContentServiceImpl - task 6d7884b9-493d-4d31-8f08-cace7bc61f2c => create article success, id:a526b893-a237-4685-9234-a196e834fe7f, title:初学设计模式:实战工厂模式Process finished with exit code 0

运行后果失常,在满足产品业务需要的同时,晋升了代码的可浏览性和扩展性,有利于后续的拓展和保护。

四、工厂办法模式构造

依据实例能够总结初一个工厂办法的模式结构图:

  1. 产品(Product)定义产品的根本条件。对于所有由创建者及其子类构建的对象,根底父类是通用的。
  2. 具体产品(Concrete Products)是产品的不同子类。
  3. 创建者(Creator)类申明产品对象的工厂办法。该办法的返回对象类型必须与产品根底父类匹配。
  4. 具体创建者(Concrete Creators)将实现创建者的办法,使其返回不同类型的产品。

留神

  1. 产品(Product)和创建者(Creator)既可为父类,要求子类重写办法,也可为接口,要求子类实现办法,只需满足约定由具体创建者来创立相应的产品即为工厂办法模式。
  2. 每次调用工厂办法不肯定都会创立新的实例,工厂办法也能够返回缓存、对象池或其余起源的已有对象。

设计模式并不难学,其自身就是多年教训提炼出的开发指导思想,关键在于多加练习,带着应用设计模式的思维区优化代码,就能构建出更正当的代码。

源码地址:https://github.com/yiyufxst/d...

参考资料:
小博哥重学设计模式:https://github.com/fuzhengwei...
深刻设计模式:https://refactoringguru.cn/de...