禁止私下搞对象,对象统一由工厂发放!
血汗工厂的由来
大家在写代码的时候都知道要注意解耦、增加复用性,但是偶尔也许会ctrl+c
,ctrl+v
大法来覆写代码片,包括Intellij IDEA编辑器发现有重复代码片也有用黄色波浪线来提醒。然而网上的教程都是如何教你去掉该死的黄色波浪线,1️⃣0️⃣🐭弟弟行为。实际上出现这种问题,你更应该去关注你的代码设计的是否合理,是否符合开闭原则,有共性的地方能否提取出来?
工厂模式一共有三种,其中简单工厂模式
是比较特殊的实现,首先它违背了开闭原则,根据工厂类的静态方法通过if...else...
或者switch...case...
来判断分支,一旦需要增减改分支都要去改代码。
工厂模式是适用性最广也是应用最多的一种工厂模式,该模式强调,每一个对象都有一个对应管理的工厂(你的对象其实是工具人)。
工厂模式
抽象类的解释
一般我们需要对象时候,通常的做法是new
一个对象,工厂模式则是强调由具体的工厂来生产一个对象给你使用。
首先最好解释一下抽象类
,我当初就是一直不太能理解,可能对于很多初学者来说也是。简单来说,抽象类像是一个模板,比如说Apple的MacBook Pro产品线,基本上历年迭代一次。而最新2019年MacBook Pro产品线中,包含着几款配置不同的产品。这些产品的屏幕、键盘、CPU、内存、金属一体外壳是抽象出来的共同特征,每一款具体的产品都离不开这些属性。而不同型号的产品又存在差异化和卖点,比如15inch和13inch的屏幕,低中高配的CPU、显卡、存储,特有的touch bar和触控ID等等。
为了方便举例,我们理想化的认为这些不同型号产品的诞生都是由一个MacBook Pro模具
从抽象到具体的过程,针对不同需求的人群差异化的结果。抽象类也是如此,它通过类的继承可以有不同版本的实现,不同版本都会做相应的增删改。
具体实现
什么时候用工厂模式比较好?它能解决什么问题?
其实我觉得就一句话,降低耦合度和批量化生产。
因为工厂模式是针对单一产品簇的对象,比如一类膨化食品、一类手机、一类blazer。这些产品不去麻烦客户而交给工厂去处理,之后产品大面积出现问题返厂或者迭代更新也都是各个工厂的事。
这里有4个关于工厂模式的角色概念,我用下图表示了他们之间的关系。
- 抽象工厂(Abstract Factory)角色:是工厂方法模式的核心,与应用程序无关。任何在模式中创建的对象的工厂类必须实现这个接口。
- 具体工厂(Concrete Factory)角色:这是实现抽象工厂接口的具体工厂类,包含与应用程序密切相关的逻辑,并且受到应用程序调用以创建某一种产品对象。
- 抽象产品(AbstractProduct)角色:工厂方法模式所创建的对象的超类型,也就是产品对象的共同父类或共同拥有的接口。
- 具体产品(Concrete Product)角色:这个角色实现了抽象产品角色所定义的接口。某具体产品有专门的具体工厂创建,它们之间往往一一对应。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
|
public interface MacBookFactory {
AbstractMacBookProduct createMacBook(); }
|
MacBook的抽象工厂类,用于描述所有具体型号生产的MacBook工厂的抽象基类。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
|
@Setter(value = AccessLevel.PUBLIC) @Getter(value = AccessLevel.PUBLIC) @ToString @AllArgsConstructor @NoArgsConstructor public abstract class AbstractMacBookProduct { private String sn; private String type; private String display; private String keyboard; private String weight; private Double price;
public abstract String printSlogan(); }
|
一类MacBook产品的抽象产品,差异化的产品配置。
抽象二兄弟的实现比较简单,其中抽象工厂接口定义的是生产MacBook
的方法,就像是和各个工厂之间签了一份略有差别的合同,那么在具体生产的工厂中需要严格按照这份合同执行。抽象产品类则是体现is-a
关系,更像是把这类产品的雏形给雕琢出来的模具,是具体产品的爹,具体工厂按照合同去生产合规的产品。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
|
public class MacBook13Factory implements MacBookFactory { @Override public AbstractMacBookProduct createMacBook() { MacBook13Product macBook13 = new MacBook13Product(); macBook13.setSn(RandomUtil.getStr()); macBook13.setType("MacBook Pro 13-inch"); macBook13.setDisplay("13-inch"); macBook13.setKeyboard("new keyboard"); macBook13.setPrice(999D); macBook13.setWeight("88kg");
macBook13.setGameGiftBag("《坦克大战乔碧萝》"); return macBook13; } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
|
@Setter(value = AccessLevel.PUBLIC) @Getter(value = AccessLevel.PUBLIC) @ToString public class MacBook13Product extends AbstractMacBookProduct {
private String gameGiftBag;
@Override public String printSlogan() { return "This is your new MacBook 13-inch."; } }
|
13-inch MacBook的具体工厂和他生产的具体产品13-inch MacBook13-inch MacBook具体工厂也按照
合同
和
模具
对MacBook进行批量生产组装加工,
MacBook13Product
通过
extend
的方式,完全继承了模具的属性和行为,其中《坦克大战乔碧萝》这个游戏礼包是该产品的特有属性。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
|
public class MacBook15Factory implements MacBookFactory { @Override public AbstractMacBookProduct createMacBook() { MacBook15Product macBook15 = new MacBook15Product(); macBook15.setSn(RandomUtil.getStr()); macBook15.setType("MacBook Pro 15-inch"); macBook15.setDisplay("15-inch"); macBook15.setKeyboard("new keyboard"); macBook15.setPrice(1999D); macBook15.setWeight("88kg");
macBook15.setCode("magnet:?xt=urn:btih:36AAB086D9AF39A323082CBAD452D6BDC42147D1"); return macBook15; } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
|
@Setter(value = AccessLevel.PUBLIC) @Getter(value = AccessLevel.PUBLIC) @ToString public class MacBook15Product extends AbstractMacBookProduct {
private String code;
@Override public String printSlogan() { return "This is your new MacBook 15-inch."; } }
|
15-inch MacBook的具体工厂和他生产的具体产品15-inch MacBook15-inch MacBook具体工厂也按照
合同
和
模具
对MacBook进行批量生产组装加工,
MacBook13Product
通过
extend
的方式,完全继承了模具的属性和行为,其中神秘代码是该产品的特有属性。
来测试一下这些代工厂996生产的产品到底合不合规:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| @Test public void createMacBook() {
MacBookFactory macBook13Factory = new MacBook13Factory();
AbstractMacBookProduct mac13WithMatthew = macBook13Factory.createMacBook();
MacBook13Product c = new MacBook13Product(); assertEquals(c.getClass(), mac13WithMatthew.getClass()); System.out.println(mac13WithMatthew.getClass()); System.out.println(mac13WithMatthew.printSlogan()); System.out.println(mac13WithMatthew); }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| @Test public void createMacBook() {
MacBookFactory macBook15Factory = new MacBook15Factory();
AbstractMacBookProduct mac15WithMatthew = macBook15Factory.createMacBook();
MacBook15Product c = new MacBook15Product(); assertEquals(c.getClass(), mac15WithMatthew.getClass()); System.out.println(mac15WithMatthew.getClass()); System.out.println(mac15WithMatthew.printSlogan()); System.out.println(mac15WithMatthew); }
|
System.out.println(mac13WithMatthew)
打印的结果只有gameGiftBag
是因为子类重写父类的toString()
方法,如果把子类(MacBook13Product
)的@ToString
注解去掉的话,就是默认继承的父类(AbstractMacBookProduct
)的toString()
方法了。
避免滥用
事实上,在SpringBoot中已经用到了不少设计模式,在单例模式那章讲过的Bean
就用到了单例模式和今天讲的工厂模式(很怀念第一次使用Spring框架手写第一个Bean的时候),模板方法(Template Method),Jdbctempldate
、Redistemplate
等等。但是切记一定不要过于拘泥与死板,为了设计模式而设计模式,忽略本身业务场景和实际情况。模式本身是对编程思想的扩展,我们在编写代码的时候还是要专注于业务本身,设计模式的初衷就是解决问题采用最优解,为了追求更高效率而诞生,保护需要加班的你。最靠谱的还是实践中慢慢总结,踩过的坑自己去总结、优化。