赞
踩
在前面的文章中,咱们主要介绍了设计模式的前三种,分别为单例模式、建造者模式以及原型模式,这几种都是属于创建型模式这个大类别下的设计模式,今天呢,我们继续讲解创建型模式中的最后一种,也就是设计模式系列的第四种,即工厂模式。
不知道大家对产品的生产过程会不会有所了解,就拿每个人的一日三餐来说,每个人都要吃饭,尤其对于一些不会做饭的朋友来说自己完成做饭是一件很难的挑战,因为自己花了一个上午或者一个下午费尽千辛万苦做完,最后却发现一点都不符合自己的口味,不得不重新再折腾一番,费时费力还不一定能达到自己的目标要求。
所以这个时候就会有了美团、饿了么等APP,上面入驻了大量的商家,这个时候我们可以将每个商家理解成一个简单的做饭工厂,对客户来说只需要根据自己的爱好以及口味在对应的商家下单对应的商品就可以了。对商家来说,做饭是个很复杂的步骤,它那里有专门负责做菜的厨师以及工具,主要负责将订单里面的饭菜生产好并对顾客提供服务,对客户而言,完全不需要考虑某个饭菜的制作细节,只需要将客户的请求告诉给商家就可以,省时省力最后拿到的饭菜也还不错,哪怕不满意,咱们重新换个商家再点就可以了。
类似的生活案例还有很多,比如说三星手机的越南代工厂、小米手机的无人工厂等等,它们的产品都是通过自己流水线以及工厂创建出来的。所以说呢编程都是来源于生活,将一些具体的生活案例进行了抽象化。
上面是生活的案例,在学习Java的过程中,同样不少地方就出现了工程模式的思想,比如Spring中Bean的加载和创建都是通过工厂模式的思想,还有就是MyBatis,相信大家有印象的话,在开发MyBatis时,其实最核心的操作数据的对象就是SqlSession,而这个对象的创建就是通过工厂模式的思想,大家不妨回忆一下,怎么得到一个SqlSession,是不是通过一个叫做SqlSessionFactory的工厂类,并且调用它的openSession()方法进而返回的一个SqlSession实例对象。还有就是RabbitMQ的连接对象也是通过工程模式的思想进行完成的。除此之外,还有很多都是通过工厂模式的思想来管理Bean对象的。所以说呢,学会工厂模式的思想,对咱们来说还是特别重要,必不可少的,在本篇文章中进行详细的讲解。
一、奶茶店案例
在正式讲解工厂模式之前,不妨通过一个简单的案例来引出工厂模式这种思想:
相信大家都喜欢喝奶茶,尤其是一些女同学,对奶茶更是爱不释手。大家都知道奶茶店的奶茶种类一般都有很多,不同的同学可以根据自己不同的爱好,下单自己最喜欢最符合口味的奶茶。比如下面有个需求,让设计一个奶茶店:
在奶茶店呢,奶茶店的每种奶茶都会有自己的名字,且每种奶茶都会有加糖、加冰以及加热等功能,比如现在有三种奶茶,分别为草莓奶茶、西瓜奶茶以及珍珠奶茶,每个顾客都可以根据自己的口味选择自己的奶茶进行下单。
如果此时让你来设计这个下单的流程,你会怎么来进行设计呢?我相信大家都特别容易想到使用继承的思想,将奶茶整体作为一个父类,将所有奶茶的特性抽取到的父类中,每种具体的奶茶进行继承以及重写就可以。那么我们根据继承的思想进行设计,具体代码如下:
创建奶茶父类:MilkTea
package com.ignorance.design.factory.entity;
public abstract class MilkTea {
public abstract String getName();
public void addIce(){
System.out.println("加冰...");
}
public void addSugar(){
System.out.println("加糖...");
}
public void addHot(){
System.out.println("加热...");
}
}
创建草莓奶茶子类:
package com.ignorance.design.factory.entity;
public class StrawberryMilkTea extends MilkTea {
@Override
public String getName() {
return "草莓奶茶";
}
}
创建西瓜奶茶子类:
package com.ignorance.design.factory.entity;
public class WaterMelonMilkTea extends MilkTea {
@Override
public String getName() {
return "西瓜奶茶";
}
}
创建珍珠奶茶子类:
package com.ignorance.design.factory.entity;
public class PearlMilkTea extends MilkTea{
@Override
public String getName() {
return "珍珠奶茶";
}
}
以上呢,我们将这四个类定义出来,相信有基础的同学一看就比较清楚,上面的代码并不难,定义了一个奶茶父类,将对应的加糖,加冰以及加热方法都抽取到父类,将getName()定义为一个抽象方法,因为每种奶茶都有自己的名字,每个子类只需要实现这个方法就可以了。
接下来呢,我们定义一个奶茶店类,并且定义一个order方法用于奶茶的下单:
package com.ignorance.design.factory.store;
import com.ignorance.design.factory.entity.MilkTea;
import com.ignorance.design.factory.entity.PearlMilkTea;
import com.ignorance.design.factory.entity.StrawberryMilkTea;
import com.ignorance.design.factory.entity.WaterMelonMilkTea;
public class MilkTeaStore {
public void order(String type){
MilkTea milkTea = null;
if ("草莓".equals(type)){
milkTea = new StrawberryMilkTea();
}else if("西瓜".equals(type)){
milkTea = new WaterMelonMilkTea();
}else if("珍珠".equals(type)){
milkTea = new PearlMilkTea();
}else {
throw new RuntimeException("【没有对应的奶茶】");
}
System.out.println(milkTea.getName());
milkTea.addHot();
milkTea.addIce();
milkTea.addSugar();
}
}
奶茶店MilkTeaStore这个类也很好理解,定义了一个order方法,根据type这个字段创建不同的奶茶。
这个时候进行测试:
package com.ignorance.design.factory.test;
import com.ignorance.design.factory.store.MilkTeaStore;
public class Customer {
public static void main(String[] args) {
MilkTeaStore milkTeaStore = new MilkTeaStore();
milkTeaStore.order("草莓");
System.out.println("=====================");
milkTeaStore.order("西瓜");
System.out.println("=====================");
milkTeaStore.order("珍珠");
}
}
以下是运行结果:
可以看出以上的运行是没问题的,也比较好理解。但是我们仔细想想就会存在很大风险以及问题。
因为奶茶店也是在不断更新产品或者舍弃产品的,比如app上线,由于珍珠奶茶不受欢迎,需要下架,这个时候咱们if判断里面的珍珠奶茶分支是不是需要删除,如果此时又上架了巧克力奶茶,咱们的代码除了新增奶茶品种类之外,是不是还需要在奶茶店下单方法中又需要将其维护在if分支中去。这样一来就会对咱们项目的维护和拓展带来很大的问题,违背了设计模式的开闭原则,也就是对拓展开放,对修改关闭的思想。
所以说呢,咱们的工厂模式就是在这种场景下被发明的。下面呢,我们针对上述案例的设计缺陷,一步一步使用工厂模式的思想进行优化,让咱们进一步体会和理解到设计模式这门技术的智慧与魅力。
二、简单工厂模式
简单工厂模式的思想还是很简单很好理解的,先不多讲,先看一下优化后的案例。
我们看出上述代码最大的缺陷就在于,奶茶店这个类和奶茶的创建依赖性以及耦合性太重了,后面一旦出现上架或者下架奶茶,这个类都要被进行修改。
在软件设计中,最大的忌讳就是将对象的创建和使用耦合在一起,后续一旦发生改变,就会给项目带来巨大的灾难,比如IOC,就是因为这个问题而横空出世,从而让软件开发引来了春天,让Java一直处于经久不衰的地位。
为了解决奶茶店和奶茶之间的耦合,我们需要引入一个新的类,也就是简单工厂类,把创建奶茶的代码交给工厂类去实现,对奶茶店这个类来说,完全不需要关注奶茶是怎么创建出来的,只需要根据业务调用工厂类去获取对应的目标奶茶对象,然后调用对应的业务方法即可。下面呢,编写一下简单工厂SimpleFactory类:
package com.ignorance.design.factory;
import com.ignorance.design.factory.entity.MilkTea;
import com.ignorance.design.factory.entity.PearlMilkTea;
import com.ignorance.design.factory.entity.StrawberryMilkTea;
import com.ignorance.design.factory.entity.WaterMelonMilkTea;
public class SimpleFactory {
public static MilkTea produceMilkTea(String type){
MilkTea milkTea = null;
if ("草莓".equals(type)){
milkTea = new StrawberryMilkTea();
}else if("西瓜".equals(type)){
milkTea = new WaterMelonMilkTea();
}else if("珍珠".equals(type)){
milkTea = new PearlMilkTea();
}else {
throw new RuntimeException("【没有对应的奶茶】");
}
return milkTea;
}
}
此时这个类没什么可讲的,因为它就是之前奶茶店的代码逻辑:
接下来了,需要对奶茶店这个类的方法进行改变。如下所示:
package com.ignorance.design.factory.store;
import com.ignorance.design.factory.SimpleFactory;
import com.ignorance.design.factory.entity.MilkTea;
import com.ignorance.design.factory.entity.PearlMilkTea;
import com.ignorance.design.factory.entity.StrawberryMilkTea;
import com.ignorance.design.factory.entity.WaterMelonMilkTea;
public class MilkTeaStore {
public void order(String type){
MilkTea milkTea = SimpleFactory.produceMilkTea(type);
System.out.println(milkTea.getName());
milkTea.addHot();
milkTea.addIce();
milkTea.addSugar();
}
}
接下来,我们测试一下。以下是运行的结果:
可以看出,依然没什么问题,这个时候将奶茶店和具体的奶茶进行了解耦,新增或者下架奶茶,奶茶的创建和使用完全做到了隔离,就再也不需要修改奶茶店的代码了,满足开闭原则。
但是细心的同学一下子就能发现,这个时候依赖性又从奶茶店转移到了工厂类,每次修改奶茶的时候,虽然奶茶店不需要动,但是奶茶工厂同样需要维护。
那怎么说它做到解耦了呢,其实奶茶店好比开发时的业务逻辑,一般会存在很多类,如果将所有的代码都耦合在一起,那后面一旦发生改变,修改的类信息肯定会是灾难。现在将这些耦合代码迁移到一个具体的工厂里面,其它的业务类,只需要调用它就可以,即便是发生改变,也只需要维护这个工厂类就可以了,在拓展上还是得到了很大的提升。就比如,有1000个类,像之前奶茶店耦合具体奶茶的情况,如果一旦发生改变,我这1000个类所有代码都将进行修改,如果我是仅仅调用这个工厂,就算将来发生改变,也只需要维护这一个工厂类即可,其他1000个引用类不需要进行改变。
三、工厂方法模式
简单工厂模式说了它的好处,一定程度上能够对奶茶店以及奶茶对象进行解耦,但是同样引入了新的耦合,就是将耦合迁移到工厂类以及奶茶具体对象了,所以为了解决这个问题,就又出现了工厂方法模式。
针对工厂方法模式,它是将工厂声明为一个抽象类,让每个工厂只生产固定的对象,比如草莓奶茶工厂只能生产草莓奶茶对象,西瓜奶茶只能生产西瓜奶茶对象。接下来通过代码改造来熟悉它这种思想。
1.创建抽象工厂接口:
package com.ignorance.design.factory;
import com.ignorance.design.factory.entity.MilkTea;
public interface AbstractFactory {
public MilkTea productMilkTea();
}
2.创建生产草莓奶茶的子工厂
package com.ignorance.design.factory;
import com.ignorance.design.factory.entity.MilkTea;
import com.ignorance.design.factory.entity.StrawberryMilkTea;
public class StrawberryFactory implements AbstractFactory {
@Override
public MilkTea productMilkTea() {
return new StrawberryMilkTea();
}
}
3.创建生产珍珠奶茶的子工厂
package com.ignorance.design.factory;
import com.ignorance.design.factory.entity.MilkTea;
import com.ignorance.design.factory.entity.PearlMilkTea;
public class PearlFactory implements AbstractFactory {
@Override
public MilkTea productMilkTea() {
return new PearlMilkTea();
}
}
4.创建生产西瓜奶茶的子工厂
package com.ignorance.design.factory;
import com.ignorance.design.factory.entity.MilkTea;
import com.ignorance.design.factory.entity.WaterMelonMilkTea;
public class WaterMelonFactory implements AbstractFactory {
@Override
public MilkTea productMilkTea() {
return new WaterMelonMilkTea();
}
}
5.奶茶店代码改造:
package com.ignorance.design.factory.store;
import com.ignorance.design.factory.StrawberryFactory;
import com.ignorance.design.factory.entity.MilkTea;
public class MilkTeaStore {
public void order(){
StrawberryFactory strawberryFactory = new StrawberryFactory();
MilkTea milkTea = strawberryFactory.productMilkTea();
milkTea.addHot();
milkTea.addIce();
milkTea.addSugar();
}
}
下面进行测试,运行结果如下图所示:
总结
在本篇文章中,咱们主要讲解了工厂模式,并且基于一个简单的奶茶店案例,使用工厂模式的思想进行了优化和改进。这种模式对来说还是比较重要的,尤其是在学习的大多数框架中,都对这种创建对象的设计模式使用的比较频繁。尤其对一些想钻研框架源码的的小伙伴来说学会它还是有一定必要的。
在本篇文章中,也把第一类创建型模式对应的四种设计模式讲完,在后续的文章中呢,也会陆陆续续把后面两类所对应的20种设计模式讲解完全,设计模式的学习需要一定的时间,这些简单的案例也只是对其思想有一定的了解,具体要把它使用到出神入化的地步还是需要钻研框架源码,才能感受到它所带来的魅力与作用。所以说,编程之路的学习尤其是一些设计性思想的过程是漫长的,但是只要不断的坚持以及积累,咱们就一定能够得到自己想要的收获。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。