工厂制造细节无须知——大佬对于工厂方法模式全方面的讲解
yuyutoo 2024-10-15 17:01 16 浏览 0 评论
1 需要了解工厂制造细节吗?
时间:3月19日22点 地点:小菜、大鸟住所的客厅 人物:小菜、大鸟
小菜来找大鸟,说:"简单工厂模式真是很好用呀,我最近好几处代码都用到了该模式。"
"哦!"大鸟淡淡地说道,"简单工厂只是最基本的创建实例相关的设计模式。但真实情况中,有更多复杂的情况需要处理。简单工厂生成实例的类,知道了太多的细节,这就导致这个类很容易出现难维护、灵活性差问题,让人感觉到了不好的味道。"
"知道很多细节不太好吗?"
"现实中,我们要想过好生活,是不太需要也不太可能知道所有细节的。比如说,我们知道猪长什么样子,也知道红烧肉很好吃,但一头猪是通过怎么样的过程变成红烧肉的呢?养殖、运输、屠宰、销售过程的批发、零售,还有饭店或家里的烹饪过程,对我们来说,都是不需要去了解的。我们去饭店吃饭,只要点了红烧肉,过一会儿它就被送出来了,好吃就行了,你说对不对?"
"将来如果能生产出这样一台机器,送进去是猪,出来就是红烧肉,那就好了。"
"嘿嘿!这样的机器我不知道是否生产得出来。不过,整个过程也算是一种封装吧。在我们的程序中,确实存在封装实例创建过程的模式——工厂方法模式,这个模式可以让创建实例的过程封装到工厂类中,避免耦合。它与简单工厂模式是一个体系的,你可以去研究一下。"
2 简单工厂模式实现
"大鸟!我研究了工厂方法模式,但还是不太理解它和简单工厂的区别,感觉还不如简单工厂方便,为什么要用这个模式?到底这个模式的精髓在哪里?"
"那你先把简单工厂模式和工厂方法模式的典型实现说给我听听。"
"哦,简单工厂模式实现是这样的:首先简单工厂模式,若以我写的计算器为例,结构图如下。"
"工厂类是这样写的。"
"客户端的应用。"
3 工厂方法模式实现
"那么如果是换成工厂方法模式来写这个计算器,你能写吗?""当然是可以,就是因为我写出来了,才感觉好像工厂方法没什么好处!
计算器的工厂方法模式实现的结构图是这样的。"
"先构建一个工厂接口。"
"然后加减乘除各建一个具体工厂去实现这个接口。"
"在OperationFactory类中,可以是这样的。"
"对呀,写得很好。"大鸟说,"工厂方法模式就是这样写的,你有什么问题?"
4 简单工厂vs.工厂方法
"怪就怪在这里呀,以前我们不是说过,如果我现在需要增加其他运算,比如求x的n次方(x n),或者求a为底数b的对数(logab),这些功能的增加,在简单工厂里,我是先去加求x的n次方的指数运算类,然后去更改OperationFactory类,当中加'Case'语句来做判断。现在用了工厂方法,加指数运算类没问题,去改OperationFactory类的分支也没问题,但又增加了一个指数工厂类,这不等于不但没有降低难度,反而增加类,把复杂性增加了吗?为什么要这样?"
"问得好。简单工厂模式的最大优点在于工厂类中包含必要的逻辑判断,根据客户端的选择条件动态实例化相关的类,对于客户端来说,去除了与具体产品的依赖。就像你的计算器,让客户端不用管该用哪个类的实例,只需要把'+'给工厂,工厂自动就给出了相应的实例,客户端只要去做运算就可以了,不同的实例会实现不同的运算。但问题也就在这里,如你所说,如果要加一个'求x的n次方(xn)'的功能,我们需要给OperationFactory类的方法里加'Case'的分支条件。目前来看,这个OperationFactory类,承载了太多功能,这可不是好办法。这就等于说,我们不但对扩展开放了,对修改也开放了,这样就违背了什么原则?"
"哦,是的,违背的是开放-封闭原则。"
"对,也就是说,我们加减乘除运算的部分已经相当成熟了,但是因为增加新的功能,就要去改已经很成熟的类代码,这就好比很多鸡蛋放在了一个篮子里,这是很危险的。"
"那么工厂方法模式,就可以解决这个问题吗?我感觉我本来是4个运算类,1个工厂类,共5个类,现在多出了4个运算工厂类和1个工厂接口,问题依然没有解决。"
"哈哈,那是因为你没有真的理解工厂方法。举一个例子,我们公司本来只有一家工厂,生产四种不同的产品。后来发展得特别好,需要增加新的两种产品放在另一个地方开设新的工厂。新的工厂不应该影响原有工厂的正常工作,你说怎么办?"
"新工厂建在别的地方,应该不影响原有的工厂运作,最多就是建好后,总公司那里再增加一些协调管理部门就好了。"
"说得非常好。就编程来说,我们应该尽量将长的代码分派切割成小段,再将每一小段'封装'起来,减少每段代码之间的耦合,这样风险就分散了,需要修改或扩展的难度就降低了。加减乘除四个类算是一个工厂的产品,不妨叫它们(基础运算工厂)类,现在增加指数、对数运算类,如果算是另一种工厂的两种产品,不妨称它为'高级运算工厂'类,你觉得有必要去影响原有的基础运算工厂运作吗?"
"哦!我明白你的意思了。并不是要去创建加法工厂、减法工厂这样的类,而是将加减乘除用一个基础工厂来创建,现在增加了新的产品,又不想影响原有的工厂代码,于是就扩展一个新的工厂来处理。我马上改。"
"下面是原有的工厂结构,加减乘除运算已经非常稳定,尽量不要去改变它们。"
"增加了一种新的工厂和两种新的运算类(以后可以扩展更多的高级运算,比如正余弦、正余切等),不要影响原有的代码和运作。"
"增加两个运算类。"
"工厂接口不变。"
"基础运算工厂类,此类已经比较成熟稳定,实现后应该封装到位,不建议轻易修改。"
"高级运算工厂类,也许还有扩展产品的可能。"
"左侧新的OperationFactory类与右侧原来的OperationFactory类对比。"
"你或许会发现,新的OperationFactory类已经不存在运算子类实例化的代码了。也就是说,在这个代码里,全部是接口与具体工厂类,并不存在具体的实现,与原来的OperationFactory类对比,实例化的过程延迟到了工厂子类中。"大鸟说道,"不过新的OperationFactory类依然存在'坏味道',当增加新的运算子类时,它本身也是需要更改的,这个先放在一边,以后可以解决。"
"Perfect!我明白了。这就是前面提到的针对接口编程,不要对实现编程吧?"
"是的。我们来看工厂方法的定义。注意关键词——延迟到子类。"
工厂方法模式(Factory Method),定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。[DP]
"我们讲过,既然这个工厂类与分支耦合,那么我就对它下手,根据依赖倒转原则,我们把工厂类抽象出一个接口,这个接口只有一个方法,就是创建抽象产品的工厂方法。然后,所有的要生产具体类的工厂,就去实现这个接口,这样,一个简单工厂模式的工厂类,变成了一个工厂抽象接口具体生成对象的工厂。每个工厂可以有多个不同产品,而工厂之间,又是相对隔离封装状态。这样过去已经比较完善的工厂和产品体系,就不需要再去改动它们,而另外需要变更的代码,完全可以通过扩展来变化,这就完全符合了开放-封闭原则的精神。"
"哦,工厂方法从这个角度讲,的确要比简单工厂模式来得强。"
"严格来说,是一种升级。当只有一个工厂时,就是简单工作模式,当有多个工厂时,就是工厂方法模式。类似由一维进化成了二维,更强大了。"
5 商场收银程序再再升级
大鸟:"还记得我们前面不断改进过的商场收银程序吗?最后一版是增加了装饰模式。"
小菜:"我记得。后面已经改得非常漂亮了,能适应各种变化。"
大鸟:"那你看看下面这段代码有优化的空间吗?"
小菜:"以前不觉得,现在发现确实是太多的new实例了,尤其是5和6,装饰模式的使用,在这个CashContext类中,显得特别的复杂。"
大鸟:"是的,那么学完了工厂方法模式,你有没有改进的想法?"
小菜:"我想想。你的意思是创建几个工厂类来处理这些new?"
大鸟:"问题是,你打算创建几个工厂类呢?"
小菜:"从上面的代码来看,我感觉至少应该有原价销售类、打折类、满减返利类、先打折再满减类和先满减再打折类,一共五个工厂类。"
大鸟:"尝试抽象一下,能不能合并一部分呢?"
小菜:"我想想。好像原价类,可以想象成打折的参数为1的打折类。但打折与满减返利好像合并不了。"
大鸟:"它俩是合并不了,但先打折后满减类能不能涵盖它们俩?"
小菜:"我懂了。如果有'先打折后满减类'存在,那它应该有三个初始化参数:折扣值、满减条件、满减返利值,那么打折类,其实就是满减返利值条件为0的情况,另外满减类,就相当于折扣参数为1的情况。"
大鸟:"非常好!所以最终只会抽象成几个工厂类?"
小菜:"那就只需要'先打折再满减'和'先满减再打折'两个工厂类了。"
大鸟:"赶紧去实现一下吧。"
6 简单工厂+策略+装饰+工厂方法
小菜:"我的实现方法,首先原有的ISale、CashSuper、CashNormal、CashReturn、CashRebate等类都不变。""增加IFactory接口。"
"增加实现IFactory接口的两个类,'先打折再满减'类和'先满减再打折'类,其中红框部分代码为装饰模式的实现。"
"有了上面的这些准备后,CashContext类就简单多了,它针对的是ISale接口、IFactory接口编程,然后两个工厂类,对于各个打折满减算法CashSuper、CashNormal、CashReturn、CashRebate等具体类一无所知。实现了松耦合的目的。"
大鸟向小菜竖起了大拇指。
小菜:"我感觉工厂方法克服了简单工厂违背开放-封闭原则的缺点,又保持了封装对象创建过程的优点。"
大鸟:"说得好,它们都是集中封装了对象的创建,使得要更换对象时,不需要做大的改动就可实现,降低了客户程序与产品对象的耦合。工厂方法模式是简单工厂模式的进一步抽象和推广。由于使用了多态性,工厂方法模式保持了简单工厂模式的优点,而且克服了它的缺点。就像生活中,凡是在基层工作过的人都知道,具体事情做得越多,越容易犯错误。相反,如果做官做得高了,说出的话就会比较抽象、笼统,很多时候犯错误的可能性反而就越来越小了。"
小菜:"工厂方法模式是不是本质就是对获取对象过程的抽象?"
大鸟:"说得非常对,就是这样。工厂方法的好处有这么几条:第一,对于复杂的参数的构造对象,可以很好地对外层屏蔽代码的复杂性,注意是指创建新实例的构造对象。比如说我们用了'先打折再满减'类工厂,其实就屏蔽了装饰模式的一部分代码,让CashContext不再需要了解装饰的过程。第二,很好的解耦能力。这点刚才你也说了,这就是针对接口在编程。当我们要修改具体实现层的代码时,上层代码完全不了解实现层的情况,因此并不会影响到上层代码的调用,这就达到了解耦的目的。"
小菜:"对了。你说这还不是最佳的做法?那应该如何做呢?还有就是这样还是没有避免修改客户端的代码呀?"
大鸟:"哈,之前我就提到过,利用'反射'可以解决避免分支判断的问题。不过今天还是不急,等以后再谈。"
小菜:"好的好的。你饿了吗?我们要不去撸串吃点夜宵?"
大鸟:"走!去吃封装羊肉去。"
相关推荐
- Mysql和Oracle实现序列自增(oracle创建序列的sql)
-
Mysql和Oracle实现序列自增/*ORACLE设置自增序列oracle本身不支持如mysql的AUTO_INCREMENT自增方式,我们可以用序列加触发器的形式实现,假如有一个表T_WORKM...
- 关于Oracle数据库12c 新特性总结(oracle数据库19c与12c)
-
概述今天主要简单介绍一下Oracle12c的一些新特性,仅供参考。参考:http://docs.oracle.com/database/121/NEWFT/chapter12102.htm#NEWFT...
- MySQL CREATE TABLE 简单设计模板交流
-
推荐用MySQL8.0(2018/4/19发布,开发者说同比5.7快2倍)或同类型以上版本....
- mysql学习9:创建数据库(mysql5.5创建数据库)
-
前言:我也是在学习过程中,不对的地方请谅解showdatabases;#查看数据库表createdatabasename...
- MySQL面试题-CREATE TABLE AS 与CREATE TABLE LIKE的区别
-
执行"CREATETABLE新表ASSELECT*FROM原表;"后,新表与原表的字段一致,但主键、索引不会复制到新表,会把原表的表记录复制到新表。...
- Nike Dunk High Volt 和 Bright Spruce 预计将于 12 月推出
-
在街上看到的PandaDunk的超载可能让一些球鞋迷们望而却步,但Dunk的浪潮仍然强劲,看不到尽头。我们看到的很多版本都是为女性和儿童制作的,这种新配色为后者引入了一种令人耳目一新的新选择,而...
- 美国多功能舰载雷达及美国海军舰载多功能雷达系统技术介绍
-
多功能雷达AN/SPY-1的特性和技术能力,该雷达已经在美国海军服役了30多年,其修改-AN/SPY-1A、AN/SPY-1B(V)、AN/SPY-1D、AN/SPY-1D(V),以及雷神...
- 汽车音响怎么玩,安装技术知识(汽车音响怎么玩,安装技术知识视频)
-
全面分析汽车音响使用或安装技术常识一:主机是大多数人最熟习的音响器材,有关主机的各种性能及规格,也是耳熟能详的事,以下是一些在使用或安装时,比较需要注意的事项:LOUDNESS:几年前的主机,此按...
- 【推荐】ProAc Response系列扬声器逐个看
-
有考牌(公认好声音)扬声器之称ProAcTablette小音箱,相信不少音响发烧友都曾经,或者现在依然持有,正当大家逐渐掌握Tablette的摆位设定与器材配搭之后,下一步就会考虑升级至表现更全...
- #本站首晒# 漂洋过海来看你 — BLACK&DECKER 百得 BDH2000L无绳吸尘器 开箱
-
作者:初吻给了烟sco混迹张大妈时日不短了,手没少剁。家里有了汪星人,吸尘器使用频率相当高,偶尔零星打扫用卧式的实在麻烦(汪星人:你这分明是找借口,我掉毛是满屋子都有,铲屎君都是用卧式满屋子吸的,你...
- 专题|一个品牌一件产品(英国篇)之Quested(罗杰之声)
-
Quested(罗杰之声)代表产品:Q212FS品牌介绍Quested(罗杰之声)是录音监听领域的传奇品牌,由英国录音师RogerQuested于1985年创立。在成立Quested之前,Roger...
- 常用半导体中英对照表(建议收藏)(半导体英文术语)
-
作为一个源自国外的技术,半导体产业涉及许多英文术语。加之从业者很多都有海外经历或习惯于用英文表达相关技术和工艺节点,这就导致许多英文术语翻译成中文后,仍有不少人照应不上或不知如何翻译。为此,我们整理了...
- Fyne Audio F502SP 2.5音路低音反射式落地音箱评测
-
FyneAudio的F500系列,有新成员了!不过,新成员不是新的款式,却是根据原有款式提出特别版。特别版产品在原有型号后标注了SP字样,意思是SpecialProduction。Fyne一共推出...
- 有哪些免费的内存数据库(In-Memory Database)
-
以下是一些常见的免费的内存数据库:1.Redis:Redis是一个开源的内存数据库,它支持多种数据结构,如字符串、哈希表、列表、集合和有序集合。Redis提供了快速的读写操作,并且支持持久化数据到磁...
- RazorSQL Mac版(SQL数据库查询工具)
-
RazorSQLMac特别版是一款看似简单实则功能非常出色的SQL数据库查询、编辑、浏览和管理工具。RazorSQLformac特别版可以帮你管理多个数据库,支持主流的30多种数据库,包括Ca...
你 发表评论:
欢迎- 一周热门
-
-
前端面试:iframe 的优缺点? iframe有那些缺点
-
带斜线的表头制作好了,如何填充内容?这几种方法你更喜欢哪个?
-
漫学笔记之PHP.ini常用的配置信息
-
其实模版网站在开发工作中很重要,推荐几个参考站给大家
-
推荐7个模板代码和其他游戏源码下载的网址
-
[干货] JAVA - JVM - 2 内存两分 [干货]+java+-+jvm+-+2+内存两分吗
-
正在学习使用python搭建自动化测试框架?这个系统包你可能会用到
-
织梦(Dedecms)建站教程 织梦建站详细步骤
-
【开源分享】2024PHP在线客服系统源码(搭建教程+终身使用)
-
2024PHP在线客服系统源码+完全开源 带详细搭建教程
-
- 最近发表
-
- Mysql和Oracle实现序列自增(oracle创建序列的sql)
- 关于Oracle数据库12c 新特性总结(oracle数据库19c与12c)
- MySQL CREATE TABLE 简单设计模板交流
- mysql学习9:创建数据库(mysql5.5创建数据库)
- MySQL面试题-CREATE TABLE AS 与CREATE TABLE LIKE的区别
- Nike Dunk High Volt 和 Bright Spruce 预计将于 12 月推出
- 美国多功能舰载雷达及美国海军舰载多功能雷达系统技术介绍
- 汽车音响怎么玩,安装技术知识(汽车音响怎么玩,安装技术知识视频)
- 【推荐】ProAc Response系列扬声器逐个看
- #本站首晒# 漂洋过海来看你 — BLACK&DECKER 百得 BDH2000L无绳吸尘器 开箱
- 标签列表
-
- mybatis plus (70)
- scheduledtask (71)
- css滚动条 (60)
- java学生成绩管理系统 (59)
- 结构体数组 (69)
- databasemetadata (64)
- javastatic (68)
- jsp实用教程 (53)
- fontawesome (57)
- widget开发 (57)
- vb net教程 (62)
- hibernate 教程 (63)
- case语句 (57)
- svn连接 (74)
- directoryindex (69)
- session timeout (58)
- textbox换行 (67)
- extension_dir (64)
- linearlayout (58)
- vba高级教程 (75)
- iframe用法 (58)
- sqlparameter (59)
- trim函数 (59)
- flex布局 (63)
- contextloaderlistener (56)