一生猿,一世猿。 —— 猿医生·yys
一、前言
工厂模式可分为 简单工厂模式、工厂方法模式、抽象工厂模式。
工厂模式主要目的:封装创建过程逻辑,只对结果负责。
为解决猿友阅读时所产生的身心倦怠,在此,我会将 代码案例选为 番茄修仙小说《星辰变》中的功法秘籍选择。
望缓解各位猿友的专注力,小生却以努力了... 请不要吝啬您的赞,点个赞吧 ~
在此记录下,分享给大家。
二、简单工厂模式
简单工厂模式,又叫做静态工厂方法 (StaticFactory Method) 模式,是指由一个工厂对象决定创建出哪一种产品类的实例,但
它不属于 GOF 23 种设计模式。简单工厂适用于工厂类负责创建的对象较少的场景,且客户端只需要传入工厂类的参数,对于如
何创建对象的逻辑不需要关心。
接下来看代码案例,我们以前言中提到的 “番茄写作的修仙小说《星辰变》为例 ”,是否看过不重要,重要的是保持专注的看
下去,层层递进理解三种工厂模式的关联以及演变,步入正题,小说主角“秦羽”,我们称之为“小羽”。
小羽天生无法修炼内功。为了得到父亲的重视关注,他毅然选择了修炼痛苦艰难的外功,经过不懈的努力,终于达到了 先天
大圆满境界,此时面临着功法选择的问题,摆在眼前的有两种功法可选,一种不完善残缺的《星辰变功法》却也是最适合自己的
的功法,另一种是常人普遍修炼的功法《普通修仙功法》,选择权在大家手中哦...
接下来,我们可以定义一个 修仙功法标准的 IMartialArts 接口:
/**
* 功法秘籍
* Interface
* @author yys
*/
public interface IMartialArts {
/**
* 修炼功法
*/
void practice();
}
创建普通修仙功法 NormalArts 实现类:
/**
* 普通修仙功法
* @author yys
*/
public class NormalArts implements IMartialArts {
public void practice() {
System.out.println("金丹期->元婴期->洞虚期->空冥期->渡劫期->大成期->天仙期->金仙期->玄仙期->神人期->天尊,注定不凡。");
}
}
创建星辰变功法功法 XingChenBianArts 实现类:
/**
* 星辰变功法
* @author yys
*/
public class XingChenBianArts implements IMartialArts {
public void practice() {
System.out.println("星云->流星->星核->行星->渡劫->恒星->暗星->黑洞->原点->乾坤之境->宇宙,唯我独尊。");
}
}
创建 MartialArtsFactory 功法秘籍简单工厂 :
/**
* 功法秘籍简单工厂
* @author yys
*/
public class MartialArtsFactory {
public static IMartialArts create(Class<? extends IMartialArts> clazz) {
if(null != clazz) {
try {
return clazz.newInstance();
} catch (Exception e) {
e.printStackTrace();
}
}
return null;
}
}
简单工厂模式 - 测试类及测试结果:
/**
* 简单(静态)工厂模式
* Test
* @author yys
*/
public class SimpleFactoryTest {
/*
* 缺点:
* 1、工厂类的职责相对过重,不易于扩展过于复杂的产品结构
*
* 应用场景:
* 1、适用于工厂类负责创建的对象较少的场景
*/
public static void main(String[] args) {
MartialArtsFactory artsFactory = new MartialArtsFactory();
IMartialArts iMartialArts = artsFactory.create(XingChenBianArts.class);
// IMartialArts iMartialArts = artsFactory.create(NormalArts.class);
iMartialArts.practice();
}
}
简单工厂模式总结:
1、由工厂决定实例化哪种产品类的实例
2、适用于工厂类负责创建的对象较少的场景
缺点:
1、工厂类的职责过重
这时会发现简单工厂模式的一个弊端,工厂类的职责过重(有点类似万能工厂),且需要客户端传递参数,套用本文代码
案例来说,每一种修仙功法修炼达到极致的修仙者们,必定是穷极一生去修炼去参悟,而不会兼修别的修仙功法,可想而知
结果必定走火入魔,而每一种修仙功法对应的修仙者们自然而然分成不同的功法派系,小羽在普通修仙功法派系必然不会寻
得星辰变功法,反之亦然。这样,小羽想要修炼什么修仙功法,则去哪个功法派系(工厂)去寻得。而对应工厂模式也一样,
根据单一职责我们将职能继续拆分,专人做专事,我们将由工厂决定创建哪个类的实例,改为由工厂接口的实现类来决定实
例化哪个类,通俗一点:让类的实例化延迟到子类中执行(子类对应某个功法派系),就这样....演变出了 工厂方法模式 ...
哈哈,觉得像那么回事,能感觉到作者确实付出努力的各位博友们,点个免费的赞吧,动动小手吧~
三、工厂方法模式
工厂方法模式 是指定义一个创建对象的接口,但让实现这个接口的类来决定实例化哪个类,工厂方法让类的实例化推迟到子
类中进行。在工厂方法模式中用户只需要关心所需产品对应的工厂,无须关心创建细节,而且加入新的产品符合开闭原则。
接下来,我们定义一个 修仙功法的 IMartialArtsFactory 顶层工厂接口:
/**
* 功法秘籍顶层工厂
* @author yys
*/
public interface IMartialArtsFactory {
IMartialArts create();
}
创建 NormalArtsFactory 普通修仙功法工厂 :
/**
* 功法秘籍-普通修仙功法工厂
* @author yys
*/
public class NormalArtsFactory implements IMartialArtsFactory {
public IMartialArts create() {
return new NormalArts();
}
}
创建 XingChenBianArtsFactory 星辰变功法工厂 :
/**
* 功法秘籍-星辰变功法工厂
* @author yys
*/
public class XingChenBianArtsFactory implements IMartialArtsFactory {
public IMartialArts create() {
return new XingChenBianArts();
}
}
简单工厂模式 - 测试类及测试结果:
/**
* 工厂方法模式
* Test
* @author yys
*/
public class FactoryMethodTest {
/*
* 缺点:
* 1、类的个数容易过多,增加复杂度
* 2、增加了系统的抽象性和理解难度
*
* 应用场景:
* 1、创建对象需要大量重复的代码
* 2、客户端(应用层)不依赖于产品类实例如何被创建、实现等细节
* 3、一个类通过其子类来指定创建哪个对象
*/
public static void main(String[] args) {
// // 星辰变功法工厂
// IMartialArtsFactory factory = new XingChenBianArtsFactory();
// IMartialArts martialArts = factory.create();
// martialArts.practice();
// 普通修仙功法工厂
IMartialArtsFactory factory = new NormalArtsFactory();
IMartialArts martialArts = factory.create();
martialArts.practice();
}
}
工厂方法模式总结:
1、由工厂接口的实现类来决定实例化哪个类,让类的实例化延迟到子类中执行
2、只限于同一产品等级结构的产品 (产品等级结构概念在抽象工厂模式中会讲解)
缺点:
1、在增加新的产品时,也必须增加新的工厂类,工厂方法的工厂个数过多,会带来额外的开销
我们继续说,再将来某天,如果产品升级需要增加一个产品类 (eg:其他修仙功法),我们并不需要修改任何的源代码,
只需再创建一个其他修仙功法工厂并实现顶层工厂即可,乍一看我们发现,工厂方法模式似乎很完美 (nice),其实不然,大
家仔细观察工厂方法模式就会发现,如果我们实现的产品接口有多个,不同的产品接口有对应的产品族(产品族概念在抽象
工厂模式中会讲解),针对这种情况我们可以采用抽象工厂模式。
四、抽象工厂模式
抽象工厂模式 是指提供一个创建一系列相关或相互依赖对象的接口,无须指定他们具体的类。客户端(应用层)不依赖于产
品类实例如何被创建、实现等细节,强调的是一系列相关的产品对象(属于同一产品族)一起使用创建对象需要大量重复的代码。
需要提供一个产品类的库,所有的产品以同样的接口出现,从而使客户端不依赖于具体实现。
再上述工厂方法模式总结提到 :产品族、产品等级结构两个概念,下面来简单阐述下:
图画的不好,莫见怪哈 ~
从图中可以看到矩形和圆形两种图形,相同颜色的就代表一个产品族,相同形状的代表一个产品等级结构。
结合代码案列来说,上图中黄色矩形代表 “修炼” 星辰变功法,黄色圆形代表 “完善创造”星辰变功法,黄色的一排都属于星辰变功
法,都是星辰变功法这个产品族。再看右侧的圆形,黄色圆形代表 “完善创造”星辰变功法,粉红色圆形代表 “完善创造”普通修仙功法,都是 “完善创造” 功法,这都属于同一个产品等级结构。
如图所示,最左侧的房子图案代表我们具体的工厂,结合代码案例,有星辰变功法工厂,普通修仙功法工厂,而每个功法工厂都可以 “修炼功法”、“完善创造功法”。
这样,我们产品等级结构中需定义两个产品接口,完善创建功法 IArtsCreate 接口、修炼功法 IArtsPractice 接口:
/**
* 完善/创造功法
* @author yys
*/
public interface IArtsCreate {
/**
* 功法完善
*/
void improve();
}
/**
* 修炼功法
* @author yys
*/
public interface IArtsPractice {
/**
* 打坐修炼
*/
void practice();
}
创建 IMartialArtsFactory 功法秘籍抽象工厂 :
/**
* 功法秘籍抽象工厂
* 抽象工厂是用户的主入口,在Spring中应用得最为广泛的一种设计模式,易于扩展
* @author yys
*/
public abstract class IMartialArtsFactory {
// 创建 功法完善
abstract IArtsImprove createArtsImprove();
// 创建 修炼功法
abstract IArtsPractice createArtsPractice();
}
接下来 创建 星辰变功法产品族相关产品及工厂:
产品等级一:星辰变功法 “修炼功法”类:
/**
* 星辰变功法 - 修炼
* @author yys
*/
public class XingChenBianArtsPractice implements IArtsPractice {
public void practice() {
System.out.println("星云->流星->星核->行星->渡劫->恒星->暗星->黑洞->原点->乾坤之境->宇宙,唯我独尊。");
}
}
产品等级二:星辰变功法 “完善创造功法”类:
/**
* 星辰变功法 - 完善/创造
* @author yys
*/
public class XingChenBianArtsImprove implements IArtsImprove {
public void improve() {
System.out.println("星辰变功法乃未完成功法,那么...就由我继续创造。。问鼎宇宙。");
}
}
星辰变功法 XingChenBianArtsFactory 具体工厂:
/**
* 星辰变功法工厂
* @author yys
*/
public class XingChenBianArtsFactory extends IMartialArtsFactory {
public IArtsImprove createArtsImprove() {
return new XingChenBianArtsImprove();
}
public IArtsPractice createArtsPractice() {
return new XingChenBianArtsPractice();
}
}
接下来 创建 普通修仙功法产品族相关产品及工厂:
产品等级一:普通修仙功法 “修炼功法”类:
/**
* 普通修仙功法 - 修炼
* @author yys
*/
public class NormalArtsPractice implements IArtsPractice {
public void practice() {
System.out.println("金丹期->元婴期->洞虚期->空冥期->渡劫期->大成期->天仙期->金仙期->玄仙期->神人期->天尊,注定不凡。");
}
}
产品等级二:普通修仙功法 “完善创造功法”类:
/**
* 普通修仙功法 - 完善/创造
* @author yys
*/
public class NormalArtsImprove implements IArtsImprove {
public void improve() {
System.out.println("普通修仙功法完善....");
}
}
普通修仙功法 NormalArtsFactory 具体工厂:
/**
* 普通修仙功法工厂
* @author yys
*/
public class NormalArtsFactory extends IMartialArtsFactory {
public IArtsImprove createArtsImprove() {
return new NormalArtsImprove();
}
public IArtsPractice createArtsPractice() {
return new NormalArtsPractice();
}
}
抽象工厂模式 - 测试类及测试结果:
/**
* 抽象工厂模式
* Test
* @author yys
*/
public class AbstractFactoryTest {
/*
* 缺点:
* 1、规定了所有可能被创建的产品集合,产品族中扩展新的产品困难,需要修改抽象工厂的接口
* 2、增加了系统的抽象性和理解难度
*/
public static void main(String[] args) {
// // 普通修仙功法
// IMartialArtsFactory factory = new NormalArtsFactory();
// IArtsPractice artsPractice = factory.createArtsPractice();
// artsPractice.practice();
// IArtsImprove artsImprove = factory.createArtsImprove();
// artsImprove.improve();
// 星辰变功法
IMartialArtsFactory factory = new XingChenBianArtsFactory();
IArtsPractice artsPractice = factory.createArtsPractice();
artsPractice.practice(); // 修炼功法
IArtsImprove artsImprove = factory.createArtsImprove();
artsImprove.improve(); // 完善创造功法
}
}
抽象工厂模式总结:
1、提供一个创建一系列相关或相互依赖对象的接口,无须指定他们具体的类
(相关或相互依赖:不同产品等级结构中功能相关联的产品组成的家族)
2、工厂方法模式升级版,针对于多种产品等级结构的产品
缺点:
1、增加产品族扩展简单,而增加产品等级结构扩展困难 (需修改抽象工厂,不符合开闭原则)
抽象工厂模式缺点中提到 “增加产品族扩展简单,而增加产品等级结构扩展困难 ”,我们结合代码案例来说:
比如我们增加一个 “其他修仙功法” 产品族 ,我们只需增加对应的工厂,以及每个产品增加 其他修仙功法对应的修炼功法、完善功法的方法即可,完全符合 “开闭原则”。
如果增加一个 “传授功法”的产品等级,就需要增加 传授功法 接口,星辰变传授功法类,普通修仙功法传授功法类,同时还需要修改功法秘籍抽象工厂,星辰变功法工厂,普通修仙功法工厂,麻烦且违背了开闭原则。
所以,世上并无绝对完美的东西,何时需思其用。
友情提示 - 个人总结 - 抽象工厂定义及创建顺序:
1、确定产品族和产品等级;
2、定义产品等级对应产品接口,一个产品等级对应一个接口;
3、定义抽象工厂,一般为产品族的总标准(eg:大众汽车产品族 / 尼桑汽车产品族,抽象工厂为 :汽车抽象工厂);
4、定义产品族相关产品类及具体工厂;
五、三种工厂模式对比总结
简单工厂模式 (Simple Factory Pattern),又叫静态工厂模式 (StaticFactory Method):
1、一个 工厂 来决定创建哪一种产品类的实例,工厂方法为静态方法,它不属于 GOF 23 种设计模式;
2、只适用于工厂类负责创建的对象较少的场景;
3、工厂类的职责过重且需要传递参数,违反单一职责原则,故需升级为 工厂方法模式。
工厂方法模式(Factory Method Pattern):
1、由工厂接口的实现类来决定实例化哪个类,让类的实例化延迟到子类中执行;
2、只限于同一产品等级结构的产品 ,故需升级为 抽象工厂模式。
抽象工厂模式(Factory Method Pattern):
1、提供一个创建一系列相关或相互依赖对象的接口,无须指定他们具体的类;
2、定义抽象(abstract)工厂,一个产品等级定义一个产品接口;
3、工厂方法模式升级版,针对于多种产品等级结构的产品 。
此工厂模式博文写到这里就结束了,很感谢各位博友的阅读,相信或多或少可以为您答疑解惑。
最后,还是一样 ~ 哈哈,各位博友辛苦下,点个免费的赞吧,动动小手吧 ~
后续继续为大家更新设计模式相关博文。
Now ~ ~ ~写到这里,就写完了,如果有幸帮助到你,请记得关注我,共同一起见证我们的成长。