设计模式之工厂模式,但是宝可梦

devtools/2024/11/18 22:30:02/

前言

工作一年了,业务代码写太多,还是得自驱提升点技术。希望工作一年后写出来的能有更多自己的思考。

正文

工厂模式是一种创建型设计模式,主要的目的还是在创建一个对象时提供更灵活、更易扩展的机制。

简单工厂模式

情景模拟

小智到商店去买精灵球,精灵球的种类繁多。商店店员会在库房中根据小智的要求拿对应的精灵球。在这个过程中,作为买家,小智不需要关注精灵球放在库房的哪一个位置。

java">/**
* 精灵球可以查看成功率,实现类有普通球和超级球
**/
public interface Ball {void showDetails();
}class NormalBall implements Ball{@Overridepublic void showDetails() {System.out.println("This is a normal ball. 15% to catch");}
}class SuperBall implements Ball{@Overridepublic void showDetails() {System.out.println("This is a super ball. 25% to catch");}
}/*** 精灵球商店*/
public class BallShop {public static Ball getBall(int ballCode){if (ballCode == 0){// 去放普通球的地方...return new NormalBall();}if (ballCode == 1){// 去放超级球的地方...return new SuperBall();}return null;}
}

对比一下直接new创建类和用工厂模式创建类:

java">	Ball ball1 = new NormalBall();Ball ball2 = new SuperBall();Ball normalBall = BallShop.getBall(0);Ball superBall = BallShop.getBall(1);

有一个很直观的区别:我根本不需要关心new的是什么实现类,我只需要传入对应的参数,告诉工厂我需要什么实现类。更规范一点,可以把0,1等数替换为枚举类,这样作为调用方,我无需关注怎么new的,new的什么类,我只需要相信工厂类会返回给我想要的类。

举例

DateFormat是简单工厂模式的经典运用,获取实例对象时,我只需要在getTimeInstance()方法传入类提供的枚举类,我无需关注内部实现细节,传入不同参数,后续的具体实现也就不同。

java">		DateFormat timeInstance = DateFormat.getTimeInstance(DateFormat.FULL);String format1 = timeInstance.format(new Date());DateFormat timeInstance2 = DateFormat.getTimeInstance(DateFormat.DATE_FIELD);String format2 = timeInstance2.format(new Date());System.out.println(format1);  // 中国标准时间 上午12:37:33System.out.println(format2);  // 上午12:37

不足

简单工厂模式对于简单场景是很友好的,实现很简单,如果能确保业务不再扩展,简单工厂模式是很好的选择。然而如果业务有扩展,简单工厂模式的弊端就体现出来了。

如果现在我新增了一个新的球种类——大师球。现在商店里的店员都需要知道大师球放在哪个位置,加重了商店店员的逻辑(工厂内创建逻辑)

java">class MasterBall implements Ball{@Overridepublic void showDetails() {System.out.println("MasterBall!!. 100% to catch");}
}

对应的工厂类就要做相应的适配:

java">public class BallShop {public static Ball getBall(int ballCode){if (ballCode == 0){// 去放普通球的地方...return new NormalBall();}if (ballCode == 1){// 去放超级球的地方...return new SuperBall();}if (ballCode == 2){// 去放大师球的地方...return new MasterBall();}return null;}
}

新增一个if分支的同时打破了开闭原则(对扩展开放,对修改封闭),其次,工厂类涵盖了所有的创建逻辑,高内聚。
为了解决业务会有新增的情况,根据面向对象编程的原则,抽象!引入工厂方法模式

工厂方法模式

场景模拟

由于精灵球的种类不断增多,店员没办法记住每种球的位置(内聚太多逻辑),所以精灵球商店把店面分成了几个区域,每个区域售卖一种球,一个店员负责一个区域,这个店员只需要关注这个区域售卖的球在哪里。作为买家小智,他只需要知道他想买哪种球,然后去对应的分区,他仍然不需要关心球在哪里存放。

于是工厂类修改为:

java">public interface BallShopNew {Ball findBall();
}class NormalBallFactory implements BallShopNew{@Overridepublic Ball findBall() {return new NormalBall();}
}class SuperBallFactory implements BallShopNew{@Overridepublic Ball findBall() {return new SuperBall();}
}class MasterBallFactory implements BallShopNew{@Overridepublic Ball findBall() {return new MasterBall();}
}

使用方式:

java">public class FactoryDemo {public static void main(String[] args) {Ball masterBall = new MasterBallFactory().findBall();Ball normalBall = new NormalBallFactory().findBall();masterBall.showDetails();normalBall.showDetails();}
}

现在如果新推出了一种新的球,只需要开辟新的分区,聘请一个新店员(创建新的实现类),遵循了开闭原则。

不足

如果这个时候,产品维度发生了扩展,商店不止卖球了,还要卖贴纸。当产品变成复数,每个工厂就要进行相应的修改来支持新的产品,或者新增对应数量的新的工厂和实现类。系统中的类会变得极其多。抽象工厂模式用于解决复数产品的场景。

抽象工厂模式

抽象工厂模式首先将产品抽象:

java">public interface Label {void showColor();
}class NormalLabel implements Label{@Overridepublic void showColor() {System.out.println("红白配色");}
}class MasterLabel implements Label{@Overridepublic void showColor() {System.out.println("紫色配色");}
}

再在抽象工厂中引入抽象产品。

java">public interface BallLabelShop {Ball findBall();Label findLabel();
}class NormalBallLabelShop implements BallLabelShop{@Overridepublic Ball findBall() {return new NormalBall();}@Overridepublic Label findLabel() {return new NormalLabel();}
}class MasterBallLabelShop implements BallLabelShop{@Overridepublic Ball findBall() {return new MasterBall();}@Overridepublic Label findLabel() {return new MasterLabel();}
}

使用:

java">public class FactoryDemo {public static void main(String[] args) {MasterBallLabelShop masterBallLabelShop = new MasterBallLabelShop();Ball ball = masterBallLabelShop.findBall();Label label = masterBallLabelShop.findLabel();ball.showDetails();label.showColor();NormalBallLabelShop normalBallLabelShop = new NormalBallLabelShop();Ball ball2 = normalBallLabelShop.findBall();Label label2 = normalBallLabelShop.findLabel();ball2.showDetails();label2.showColor();}
}/**
MasterBall!!. 100% to catch
紫色配色
This is a normal ball. 15% to catch
红白配色
**/

这里可能就有读者会问,这不是和工厂方法模式差不多吗? 其实我的理解是抽象方法模式中,工厂的实现类是将以产品组合为单位的。
上面的例子,两个工厂分别是组合了NormalMaster两种类型,让同一工厂生产的是能配套的产品组合。回到工厂模式的维度,使用者只需要知道什么工厂会给他想要的产品组合。

总结

作为一个细分了三个种类的设计模式,到底该如何取舍?比起直接new一个对象,使用对应模式的好处到底在哪?

  • 简单工厂模式
    1. 根据传入的参数决定产出的对象,可以隐藏一些创建的细节
    2. 适用于需要根据条件创建不同对象的场景。
  • 工厂方法模式:
    1. 将简单工厂转化为抽象工厂的子类,每个子类负责相应对象的创建,将创建逻辑从简单工厂中解耦到各自实现类。
    2. 适用于要创建的对象会出现扩展的场景;或者是希望将创建逻辑分别封装在具体工厂类的场景。
  • 抽象工厂模式
    1. 抽象工厂提供接口,用于创建一系列相关或者相互依赖的对象。
    2. 适用于要创建复数种类的对象;或者是希望将创建逻辑封装在具体工厂类的场景。

http://www.ppmy.cn/devtools/135049.html

相关文章

蓝桥杯每日真题 - 第15天

题目:(钟表) 题目描述(13届 C&C B组B题) 解题思路: 理解钟表指针的运动: 秒针每分钟转一圈,即每秒转6度。 分针每小时转一圈,即每分钟转6度。 时针每12小时转一圈…

c++数字雨实现

‌数字雨‌是一种视觉效果,通常出现在黑客电影中,表现为屏幕上不断下落的数字和字符,营造出一种科技感和动态效果。‌12 数字雨的实现方法 ‌编程实现‌:可以使用C/C编程语言来实现数字雨效果。通过定义一个字符串数组&#xff0…

刘铁猛C#入门 024 类的声明,继承和访问控制

类声明的全貌 C#声明类的位置 声明既定义(C#与Java) 类的修饰符 最简单的类声明 类的访间控制 :默认internal 共性 public 和 internal 都是访问修饰符,用于定义一个类型的成员可以被谁访问。它们都可以用来声明类、结构、接口、枚举、字段、方法、…

Docker环境搭建Cloudreve网盘服务(附shell脚本一键搭建)

Docker搭建Cloudreve Cloudreve介绍: Cloudreve 是一个基于 ThinkPHP 框架构建的开源网盘系统,旨在帮助用户以较低的成本快速搭建起既能满足个人也能满足企业需求的网盘服务。Cloudreve 支持多种存储介质,包括但不限于本地存储、阿里云OSS、…

第8章 利用CSS制作导航菜单

8.1 水平顶部导航栏 水平莱单导航栏是网站设计中应用范围最广的导航设计,一般放置在页面的顶部。水平 导航适用性强,几乎所有类型的网站都可以使用,设计难度较低。 如果导航过于普通,无法容纳复杂的信息结构,就需要在…

Neo4j Desktop 和 Neo4j Community Edition 区别

Neo4j Desktop 和 Neo4j Community Edition 的主要区别在于它们的用途、功能以及安装和管理方式。以下是这两者的详细对比: 1. Neo4j Desktop Neo4j Desktop 是一个图形化的桌面应用程序,主要为开发人员和个人使用提供了一个便捷的环境来安装、管理和运…

Python 正则表达式使用指南

Python 正则表达式使用指南 正则表达式(Regular Expression, 简称 regex)是处理字符串和文本的强大工具。它使用特定的语法定义一组规则,通过这些规则可以对文本进行匹配、查找、替换等操作。Python 提供了 re 模块,使得正则表达…

vim教程

Vim是一款功能强大的文本编辑器,广泛应用于Linux系统。它不仅是一个编辑器,更是一个开发者必备的工具。下面将详细介绍Vim的基本操作、高级功能、插件管理和配置: Vim的安装 在Ubuntu/Debian上安装:sudo apt update sudo apt inst…