程序猿学社的GitHub,欢迎Star
github技术专题
本文已记录到github

文章目录

  • 前言
  • 小故事
  • 传统方式
  • 简单工厂(第一种)
  • 工厂方法模式(第二种)
  • 抽象工厂模式(第三种)
    • 使用工厂方法模式
    • 使用抽象工厂实现

前言

通过上一篇文章,我们已经知道程序猿是有女(男)朋友,也知道如何保证只会有一个对象(男女朋友),本文我们来看一看工厂模式。

社长:“老王,我们开始继续23中设计模式之工厂模式”
隔壁老王: “社长,工厂模式有什么用,为什么要用工厂模式?”
社长: “我先买一个小关子,先来看一看传统的写法”

小故事

周末一大早,6点钟左右,我就被吵醒,然后一脸呆萌呆萌的看着我,原来是忘记给我家乖乖喂食物咯,看了一下存放猫粮的袋子,也弹尽粮绝咯,只能上宠物店去购买猫粮。他的名字叫汤圆

传统方式

通过代码,我们实现去宠物店购买猫粮的这样一个需求。

package com.cxyxs.designmode.factory;
/*** Description:* Author: 程序猿学社* Date:  2020/4/3 23:16* Modified By:*/
public interface PetShop {void buy();
}class  Meat implements  PetShop{@Overridepublic void buy() {System.out.println("社长给汤圆购买肉干");}
}/*** 提供者* =======================* 调用者*/
class  Test{public static void main(String[] args) {PetShop ps = new Meat();ps.buy();}
}


  • 这种写法违反了迪米特法则也就是最少知道原则,通俗的来讲,就是一个类对自己依赖的类知道的越少越好。也就是说,对于被依赖的类来说,无论逻辑多么复杂,都尽量地的将逻辑封装在类的内部,对外除了提供的public方法,不对外泄漏任何信息。
  • 结合生活实际理解,例如我这里是去购买肉干,我有必要知道这个肉是如何生产出来的吗
  • 实际上也违反勒开闭原则,对扩展开放(提供者),对修改关闭(调用者)
    例如我们把类Meat改为Meat1,我们可以发现,我们需要修改两个地方,一个是提供者,还有一个是调用者,两边都要改动,这就违反了对修改关闭这一条,也没有实现程序的一个解耦。

隔壁老王: “社长,你也说了,上面这种方式,提供者一变,调用者就得跟着变,在项目开发过程中,如何也出现这样的问题,那大家还能不能愉快的玩耍咯”
社长:“别急,我们可以通过简单工厂来实现”

简单工厂(第一种)

从设计模式的类型上来说,简单工厂模式是属于创建型模式,又叫做静态工厂方法(Static Factory Method)模式,但不属于23种GOF设计模式之一。简单工厂模式是由一个工厂对象决定创建出哪一种产品类的实例

肉干毕竟不能当饭吃,所以,还得买猫粮,肉干只能当零食吃。所以这时候我们的需求又发生了改变。

  • 不清楚这个图如何画的,可以百度查一下UML类图
package com.cxyxs.designmode.factory.simple;/*** Description:宠物店* Author: 程序猿学社* Date:  2020/4/3 23:16* Modified By:*/
public interface PetShop {void buy();
}class  Meat implements  PetShop{@Overridepublic void buy() {System.out.println("社长给汤圆购买肉干");}
}class  Foot implements  PetShop{@Overridepublic void buy() {System.out.println("社长给汤圆购买猫粮");}
}class  CatFootFacoty{public  static  PetShop buy(String name){switch (name){case "猫粮":return  new Foot();case "肉干":return  new Meat();}return  null;}
}/*** 提供者代码* =======================* 调用端代码*/
class  Test{public static void main(String[] args) {PetShop ps = CatFootFacoty.buy("肉干");PetShop ps1 = CatFootFacoty.buy("猫粮");ps.buy();ps1.buy();}
}


好处:

  • 在这里借助了一个工厂类CatFootFacoty,实现解耦,我们不用关心肉干和猫粮是生产生产的,只需要告诉工厂,我需要这两样东西。由工厂统一管理。
  • 产品很少的情况下,可以实现简单工厂模式

缺点:

  • 如果产品一多,工厂类就会变得很臃肿,不利于维护,同时他违反咯开闭原则,开闭原则很重要的一点,当软件需要变化时,尽量通过扩展软件实体的行为来实现变化,而不是通过修改已有代码来实现变化。
  • 例如我现在需要新增一个购买鱼的产品需求,需要新增一个鱼类,再修改工厂类,类会变得越来越多,如果上百个产品,这就意味着上百个类,增加了程序的复杂度。

工厂方法模式(第二种)

跟简单工厂相比,工厂方法模式是简单工厂的plus版本,越来越流程化。不像简单工厂一个工厂,既生产肉干,又生产猫粮等等,工厂方法模式,就是把具体生产的工作,交给具体的工厂来负责,例如,生产肉干的是一个工厂,生产猫粮的又是一个工厂。

  • 所以工厂方法模式符合开闭原则

社长: “我们刚刚已经了解到简单工厂一些优缺点,其中很重要的一点就是不符合开闭原则,我们来看一看工厂方法模式”
社长: “既然工厂方法模式是简单工厂的plus版本,我们直接把简单工厂的代码拿过来,新创建一个包methodmodel,老王,有没有跟上我的节奏”
隔壁老王: “欧了,已经搞定咯,如何改造成工厂方法模式*
社长: "话不多说,先看看类图,再看代码 "

  • 这个类图代码写完后,可以自动生成,在idea中,右击

    再把对应的类拖进来就可以自动生成,
package com.cxyxs.designmode.factory.methodmodel;/*** Description:宠物店* Author:程序猿学社* Date:  2020/4/3 23:16* Modified By:*/
public interface PetShop {void buy();
}class  Meat implements PetShop {@Overridepublic void buy() {System.out.println("社长给汤圆购买肉干");}
}class  Foot implements PetShop {@Overridepublic void buy() {System.out.println("社长给汤圆购买猫粮");}
}interface  Factory{PetShop food();
}class   MeatFactory implements  Factory{@Overridepublic PetShop food() {return new Meat();}
}class  FootFactory implements  Factory{@Overridepublic PetShop food() {return new Foot();}
}/*** 提供者代码* =======================* 调用端代码*/
class  Test{public static void main(String[] args) {Factory factory = new MeatFactory();FootFactory footFactory = new FootFactory();PetShop food = factory.food();PetShop food1 = footFactory.food();food.buy();food1.buy();}
}


隔壁老王: “社长,你这代码,我没有看出什么好处来,只知道你这代码越来越复杂,绕的头都晕咯。”
社长: “老王,别急呀,我们之前已经知道简单工厂是不符合开闭原则的,也就是说,尽量不要在已经的代码上进行修改,应该进行扩展”
社长: “我家的汤圆现在已经不满足于肉干和猫粮咯,需要吃鱼,我们看看,我们如何在现有的代码上进行改动”

/*** 扩展吃鱼的部分开头*/
class  Fish implements PetShop {@Overridepublic void buy() {System.out.println("社长给汤圆购买小鱼鱼");}
}
class FishFactory implements  Factory{@Overridepublic PetShop food() {return new Fish() ;}
}/*** 提供者代码* =======================* 调用端代码*/
class  Test{public static void main(String[] args) {Factory factory = new MeatFactory();FootFactory footFactory = new FootFactory();Factory fishFactory = new FishFactory();PetShop food = factory.food();PetShop food1 = footFactory.food();PetShop food2 = fishFactory.food();food.buy();food1.buy();food2.buy();}
}
  • 扩展产品,具体工厂也跟着扩展,不需要修改以前的代码,遵守了开闭原则。

好处:

  • 提供者修改代码后,调用者是不知道的,迪米特法则,也就是最少知道原则。
  • 在简单工厂上做咯优化,扩展产品,不需要修改以前的代码,只需要扩展一个产品和一个具体工厂即可

隔壁老王: “就拿你上面main方法里面的MeatFactory举例,假设MeatFactory类变为MeatFactory123,还是需要修改提供者和调用者两边的代码”
社长 “工厂名称有一套规范,只需要提供者保证尽量不改动类名就行,不然就是一个死循环,看看mybatis工厂类,版本变动,工厂名也不会变动。mybatis开发就相当于提供者,我们使用人员,就相当于调用者,如果mybatis工厂名变动,我们开发也不知道,这体验是不是很不好。所以,这个问题,不用担心,都有规范的”

抽象工厂模式(第三种)

社长: “我们之前只是很简单的实现吃,猫还会吃、睡等行为(多个产品等级)。产品等级一多,工厂类就会变得越来越臃肿”

使用工厂方法模式

  • 需要使用到12个类,工厂的涉及类就有6个
package com.cxyxs.designmode.factory.abstrastinterface;/*** Description:* Author: 程序猿学社* Date:  2020/4/4 18:20* Modified By:*/
public interface Foot {void eat();
}class  Meat implements  Foot{@Overridepublic void eat() {System.out.println("给汤圆吃肉干");}
}class  Fish  implements  Foot{@Overridepublic void eat() {System.out.println("给汤圆吃小鱼仔");}
}interface Toy{void play();
}class  CatTeaser implements Toy{@Overridepublic void play() {System.out.println("社长利用逗猫棒跟汤圆玩耍");}
}class Ball implements  Toy{@Overridepublic void play() {System.out.println("汤圆一个人跟小球进行玩耍");}
}/*** 食物工厂代码*/
interface FootFactory{public Foot proFoot();
}
class MeatFactory implements  FootFactory{@Overridepublic Foot proFoot() {return new Meat();}
}class FishFactory implements  FootFactory{@Overridepublic Foot proFoot() {return new Fish();}
}/*** 玩具工厂*/
interface ToyFactory{public Toy proToy();
}
class  CatTeaserFactory implements  ToyFactory{@Overridepublic Toy proToy() {return new CatTeaser();}
}class  BallFactory implements  ToyFactory{@Overridepublic Toy proToy() {return new Ball();}
}

使用抽象工厂实现

社长: “先看看类图,再根据类图实现对应的代码”

package com.cxyxs.designmode.factory.abstrastinterface.plus;/*** Description:* Author: 程序猿学社* Date:  2020/4/4 18:20* Modified By:*/
public interface Foot {void eat();
}class  Meat implements Foot {@Overridepublic void eat() {System.out.println("给汤圆吃肉干");}
}class  Fish  implements Foot {@Overridepublic void eat() {System.out.println("给汤圆吃小鱼仔");}
}interface Toy{void play();
}class  CatTeaser implements Toy {@Overridepublic void play() {System.out.println("社长利用逗猫棒跟汤圆玩耍");}
}class Ball implements Toy {@Overridepublic void play() {System.out.println("汤圆一个人跟小球进行玩耍");}
}/*** 食物工厂代码*/
interface Factory{public Foot proFoot();public Toy proToy();
}class MeatAndCatTeaserFactory implements Factory {@Overridepublic Foot proFoot() {return new Meat();}@Overridepublic Toy proToy() {return new CatTeaser();}
}class FishAndBallFactory implements  Factory{@Overridepublic Foot proFoot() {return new Fish();}@Overridepublic Toy proToy() {return new Ball();}
}
  • 工厂类由之前的6个,变为3个。减少了工厂类的臃肿

优点:

  • 抽象工厂可以理解为简单工厂和工厂方法模式的一个汇总
  • 对产品进行了抽象,适合一些维度有关联的(也就是说,有逻辑关系),例如,我举的这个例子,在工厂方法中,会有吃的工厂和玩的工厂,而在抽象工厂中,直接把这两个具体工厂直接抽象出来,合二为一。

缺点:

  • 要求抽象的多个产品,之前有逻辑关系
  • 后续需要生产喝的东西,需要修改抽象工厂,同时各个具体工厂也需要修改

隔壁老王: “社长,在抽象工厂模式中,吃和玩都是捆绑的关系,你这是捆绑销售,如果,我只想要实现吃,应该怎么办?”
社长: “如果只想实现吃,就可以使用工厂方法模式,应该根据具体问题,具体选择,使用那个模式。”

总结:

  • 不管是简单工厂、工厂方法模式、抽象工厂模式,我们应该灵活运用,主要的目的,还是为了解耦,写出可扩展性、可读的代码。

原创不易,不要白嫖,觉得有用的社友,给我点赞,让更多的老铁看到这篇文章。
因技术能力有限,如文中有不合理的地方,希望各位大佬指出,在下方评论留言,谢谢,希望大家一起进步,一起成长。

作者:程序猿学社
原创公众号:『程序猿学社』,专注于java技术栈,分享java各个技术系列专题,以及各个技术点的面试题。
原创不易,转载请注明来源(注明:来源于公众号:程序猿学社, 作者:程序猿学社)。

【23种设计模式专题】二 工厂模式相关推荐

  1. 23种设计模式之抽象工厂模式(Abstract Factory Pattern)

    前言:大家好,我是小威,24届毕业生,在一家满意的公司实习.本篇文章将23种设计模式中的抽象工厂模式,此篇文章为一天学习一个设计模式系列文章,后面会分享其他模式知识. 如果文章有什么需要改进的地方还请 ...

  2. 23种设计模式之简单工厂模式,工厂方法模式,抽象工厂模式详解

    工厂模式详解 1. 简单工厂模式 1.1 需求分析 1.2 使用传统方式实现 1.2.1 类图 1.2.2 代码实现 1.2.2.1 新建pizza抽象类 1.2.2.2 希腊披萨实现类 1.2.2. ...

  3. 备战面试日记(3.3) - (设计模式.23种设计模式之结构型模式)

    本人本科毕业,21届毕业生,一年工作经验,简历专业技能如下,现根据简历,并根据所学知识复习准备面试. 记录日期:2022.1.9 大部分知识点只做大致介绍,具体内容根据推荐博文链接进行详细复习. 文章 ...

  4. 备战面试日记(3.2) - (设计模式.23种设计模式之创建型模式)

    本人本科毕业,21届毕业生,一年工作经验,简历专业技能如下,现根据简历,并根据所学知识复习准备面试. 记录日期:2022.1.6 大部分知识点只做大致介绍,具体内容根据推荐博文链接进行详细复习. 文章 ...

  5. 备战面试日记(3.4) - (设计模式.23种设计模式之行为型模式)

    本人本科毕业,21届毕业生,一年工作经验,简历专业技能如下,现根据简历,并根据所学知识复习准备面试. 记录日期:2022.1.12 大部分知识点只做大致介绍,具体内容根据推荐博文链接进行详细复习. 文 ...

  6. 二十三种设计模式第二篇--工厂模式

    上篇我们了解了6条设计模式的准则,我相信如果你想了解设计模式,那么你迈出的第一步,我会将上一篇文档里边的6大准则进行一篇有关的代码展示,当然这是题外话了,本篇我们将重点围绕工厂模式进行讲解,天哪,23 ...

  7. 23种设计模式(二十三)访问者模式(阁瑞钛伦特软件-九耶实训)

    常说的设计模式是23种设计模式,分为3大类: 创建型模式5种:工厂方法.抽象工厂.单例.建造者.原型 结构型模式7种:适配器.代理.桥接.装饰者.外观.享元.组合 行为型模式11种:模板方法.解释器. ...

  8. 23种设计模式(二十二)状态模式(阁瑞钛伦特软件-九耶实训)

    常说的设计模式是23种设计模式,分为3大类: 创建型模式5种:工厂方法.抽象工厂.单例.建造者.原型 结构型模式7种:适配器.代理.桥接.装饰者.外观.享元.组合 行为型模式11种:模板方法.解释器. ...

  9. 虚无空间java下载_Java进阶篇设计模式之二 ----- 工厂模式

    前言 在上一篇中我们学习了单例模式,介绍了单例模式创建的几种方法以及最优的方法.本篇则介绍设计模式中的工厂模式,主要分为简单工厂模式.工厂方法和抽象工厂模式. 简单工厂模式 简单工厂模式是属于创建型模 ...

  10. JAVA开发的23种设计模式之 —  装饰器模式 代理器模式

    装饰器模式 概述 : 动态的给一个对象添加一些额外的职责,就增加功能来说,装饰器模式相比生成子类更加灵活 维基百科解释 : 通过使用修饰模式,可以在运行时扩充一个类的功能.原理是 :增加一个修饰类包裹 ...

最新文章

  1. Android Studio开发环境及第一个项目
  2. 图像识别:能在商业中如何运用
  3. 知其然,知其所以然:基于多任务学习的可解释推荐系统
  4. 团队冲刺(二)个人工作总结9
  5. 经典C语言程序100例之八五
  6. 感知算法论文(二)Pelee: A Real-Time Object Detection System on Mobile Devices(2018)译文
  7. 【每日一题】8月27日题目精讲 Is It A Tree?
  8. vue.js踩坑之组件参数检验与非props特性
  9. Ajax 加载数据 练习 自我有些迷糊了,写的大概请谅解 ^ _ ^
  10. Sybase数据库故障的修复方法
  11. POJ 2287 田忌赛马 贪心算法
  12. 浙大计算机学院陈越老师,浙江大学计算机科学与技术学院导师介绍 陈越
  13. php缓存memclear,PHP内存缓存加速功能memcached安装与用法
  14. 基于HTML的旋转立方体的实现
  15. Python实现草莓熊手拿风车和鲜花
  16. 安卓服务器维护,安卓服务器6月14日2时维护公告
  17. 强化学习-学习笔记11 | 解决高估问题
  18. 毕业设计-基于MATLAB的含噪语音信号降噪处理系统
  19. 第5节 树形计算亚式期权价格
  20. 机器学习与统计建模 —— 差异和联系

热门文章

  1. 社区运营 VS 征途(转)
  2. VS Code之爽到飞起的两个小技巧
  3. Error: Error while compiling statement: FAILED: SemanticException Unable to load data to destination
  4. 在Spring中注册组件时报错:The fully qualified name of the bean's class, except if it serves only as a parent d
  5. 苹果电脑数据丢失怎么恢复?
  6. 斯蒂文斯理工学院的计算机专业,斯蒂文斯理工学院计算机科学硕士专业入学要求及费用...
  7. linux计划任务at-crontab 与日志的管理使用
  8. 微信内防红短网址是怎么做出来的,微信域名防红原理
  9. 【智能路由器】ndpi深度报文分析之协议分析器
  10. 微信小程序自定义相机拍照,计算大小,以及上传