【23种设计模式专题】二 工厂模式
程序猿学社的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{ @Override public 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{ @Override public void buy() { System.out.println("社长给汤圆购买肉干"); } } class Foot implements PetShop{ @Override public 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 { @Override public void buy() { System.out.println("社长给汤圆购买肉干"); } } class Foot implements PetShop { @Override public void buy() { System.out.println("社长给汤圆购买猫粮"); } } interface Factory{ PetShop food(); } class MeatFactory implements Factory{ @Override public PetShop food() { return new Meat(); } } class FootFactory implements Factory{ @Override public 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 { @Override public void buy() { System.out.println("社长给汤圆购买小鱼鱼"); } } class FishFactory implements Factory{ @Override public 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{ @Override public void eat() { System.out.println("给汤圆吃肉干"); } } class Fish implements Foot{ @Override public void eat() { System.out.println("给汤圆吃小鱼仔"); } } interface Toy{ void play(); } class CatTeaser implements Toy{ @Override public void play() { System.out.println("社长利用逗猫棒跟汤圆玩耍"); } } class Ball implements Toy{ @Override public void play() { System.out.println("汤圆一个人跟小球进行玩耍"); } } /** * 食物工厂代码 */ interface FootFactory{ public Foot proFoot(); } class MeatFactory implements FootFactory{ @Override public Foot proFoot() { return new Meat(); } } class FishFactory implements FootFactory{ @Override public Foot proFoot() { return new Fish(); } } /** * 玩具工厂 */ interface ToyFactory{ public Toy proToy(); } class CatTeaserFactory implements ToyFactory{ @Override public Toy proToy() { return new CatTeaser(); } } class BallFactory implements ToyFactory{ @Override public 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 { @Override public void eat() { System.out.println("给汤圆吃肉干"); } } class Fish implements Foot { @Override public void eat() { System.out.println("给汤圆吃小鱼仔"); } } interface Toy{ void play(); } class CatTeaser implements Toy { @Override public void play() { System.out.println("社长利用逗猫棒跟汤圆玩耍"); } } class Ball implements Toy { @Override public void play() { System.out.println("汤圆一个人跟小球进行玩耍"); } } /** * 食物工厂代码 */ interface Factory{ public Foot proFoot(); public Toy proToy(); } class MeatAndCatTeaserFactory implements Factory { @Override public Foot proFoot() { return new Meat(); } @Override public Toy proToy() { return new CatTeaser(); } } class FishAndBallFactory implements Factory{ @Override public Foot proFoot() { return new Fish(); } @Override public Toy proToy() { return new Ball(); } }
- 工厂类由之前的6个,变为3个。减少了工厂类的臃肿
优点:
- 抽象工厂可以理解为简单工厂和工厂方法模式的一个汇总
- 对产品进行了抽象,适合一些维度有关联的(也就是说,有逻辑关系),例如,我举的这个例子,在工厂方法中,会有吃的工厂和玩的工厂,而在抽象工厂中,直接把这两个具体工厂直接抽象出来,合二为一。
缺点:
- 要求抽象的多个产品,之前有逻辑关系
- 后续需要生产喝的东西,需要修改抽象工厂,同时各个具体工厂也需要修改
隔壁老王: “社长,在抽象工厂模式中,吃和玩都是捆绑的关系,你这是捆绑销售,如果,我只想要实现吃,应该怎么办?”
社长: “如果只想实现吃,就可以使用工厂方法模式,应该根据具体问题,具体选择,使用那个模式。”
总结:
- 不管是简单工厂、工厂方法模式、抽象工厂模式,我们应该灵活运用,主要的目的,还是为了解耦,写出可扩展性、可读的代码。
原创不易,不要白嫖,觉得有用的社友,给我点赞,让更多的老铁看到这篇文章。
因技术能力有限,如文中有不合理的地方,希望各位大佬指出,在下方评论留言,谢谢,希望大家一起进步,一起成长。
作者:程序猿学社
原创公众号:『程序猿学社』,专注于java技术栈,分享java各个技术系列专题,以及各个技术点的面试题。
原创不易,转载请注明来源(注明:来源于公众号:程序猿学社, 作者:程序猿学社)。
- 点赞 9
- 收藏
- 分享
- 文章举报
- 23种设计模式之python实现--工厂方法
- 设计模式之创建型模式——简单工厂(又称为静态工厂不属于GOF23种设计模式以内)
- 23种设计模式之工厂方法模式
- 23种设计模式之工厂模式(Factory)
- 23种设计模式——工厂模式
- 23种java设计模式之工厂模式
- 学习23种设计模式之简单工厂模式
- 23种设计模式之-工厂模式
- 简单工厂模式——23种设计模式综合实例应用
- 23种设计模式01---工厂模式
- 23种设计模式之工厂模式(创建型,1 Factory,c++实现)
- [设计模式之禅读书笔记]009_23种设计模式三:抽象工厂方法
- 快速掌握23种设计模式(工厂,单例,原型)
- 23种设计模式(1)工厂方法模式
- Gof23种设计模式+简单工厂设计模式总结(一)
- 23种设计模式(2):工厂方法模式
- 设计模式(三十一)------23种设计模式(23):简单工厂模式
- JAVA的23种设计模式---工厂模式
- 23种设计模式之工厂模式
- Java23种设计模式:工厂模式(二)