工厂设计模式
微信公众号:新时代程序猿
关注可了解更多的JAVA,PYTHON,ANDROID教程及开发技术。
问题或建议,请公众号留言;
如果你觉得文章对你有帮助,欢迎赞赏
概述
今天逛某乎,发现程序猿也有阶级了,以下摘自某乎某段落:
10K程序员: 类结构图和时序图?是什么鬼,需求来了直接撸啊!
20K程序员:偶尔画画这些鬼东西,用处明显不大!
30K程序员:写代码之前,肯定先画好类结构图和时序图啊,这样编码会更轻松。
40K程序员:我的主要职责就是要把框架架构好,类结构图和时序图几乎是必须的!
看完之后觉得自己更菜了哈哈哈
标题:程序员:我终于知道工厂模式是什么了
目录
- 什么是工厂模式?
- 为什么使用工厂模式?
- 工厂模式可以解决什么问题?
- 工厂模式有哪几种分类?
- 工厂模式的优缺点?
- 什么时候使用工厂模式?
什么是工厂模式?
工厂模式(Factory Pattern)是 Java 中最常用的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,也就是说一个产品是怎么创建的我们无需关心,只需要告诉他我们想要什么产品,给我们创建出来就好,(比如我们买车,只要告诉工厂我们想要奥迪A8,工厂直接生产出一辆奥迪A8汽车即可,无需知道创建的过程),并且是通过使用一个共同的接口来指向新创建的对象。
简单的讲就是用工厂方法代替了new的操作,在通俗点就是说,你new一个对象的时候直接调用工厂方法就行了,在编程时,需要定义一个工厂接口,由不同的的子类去实现,再定一个具体工厂类,定义一个产生实例的方法,我们通过这个方法来获得实例就行了。可能有的小伙伴说那我直接new不就行了,干嘛还非得这么麻烦使用工厂模式呢?
所以这就引出了我们的下面一个问题。
为什么使用工厂模式?
为什么要使用工厂模式呢?看很多网上资料的介绍,感觉都是多此一举,没有必要专门再加个工厂类去创建对象,直接new不是更方便吗?还是那句话,10k的程序员可能不关心,40k的程序员一定熟练掌握~下面我们通过一个例子来开始新的篇章:
我们来看一个汽车的项目,要便于汽车种类的扩展,要便于维护
1.汽车的种类很多,比如:奥迪A4,奥迪A6,奥迪A8
2.汽车的制作过程:
- 准备零件
- 生产汽车
- 售前检查
- 汽车出库
3.完成汽车店的订购功能
举个简单的例子:在没有工厂方法之前,专卖店出售汽车时都要在方法里面new 对象,下面我们来看一下不用工厂模式是怎么写的吧~
public OrderCar() {
Car car = null;
String type;
do {
//获取订单汽车型号
type = getOrderType();
if ("audiA4".equals(type)) {
car = new AudiA4();
car.setType("audiA4");
} else if ("audiA6".equals(type)) {
car = new AudiA6();
car.setType("audiA6");
} else if ("audiA8".equals(type)) {
car = new AudiA8();
car.setType("audiA8");
} else {
break;
}
//准备汽车零件
car.PrepareParts();
//组装汽车
car.createCar();
//检查汽车
car.checkCar();
//汽车出库
car.EXWarehouse();
}
while (true);
}
private String getOrderType() {
BufferedReader strin = new BufferedReader(new InputStreamReader(System.in));
System.out.println("input car type:");
String carType = null;
try {
carType = strin.readLine();
} catch (IOException e) {
e.printStackTrace();
return null;
}
return carType;
}
这里我创建了AudiA4,AudiA6,AudiA8三个不同型号的奥迪对象,getOrderType()是通过键盘输入获取你想要的汽车型号来判断生产什么型号汽车。
看看,这样的设计有什么缺点?
如果哪天新增加了奥迪A10,奥迪A11,完蛋了,我们需要对这个类进行修改了!这完全违背了开闭原则!
其实这这一点还不太要紧,关键这是一家店,如果是千千万万的专卖店,让你修改,怕不怕?
于是,我们对流程进行优化,封装变化,把创建汽车对象的步骤封装起来,不让4S去创建汽车了,直接由汽车工厂来创建,4s店想要什么车直接告诉汽车工厂,汽车工厂就生产什么汽车,这样就减少了耦合,把那个该死的总是改变的地方封装起来!
工厂模式有哪几种分类?
我们知道Java里边共有23种设计模式而工厂模式就有三种,它们分别是简单工厂模式(并不在23中模式之中),工厂方法模式以及抽象工厂模式,其中我们通常所说的工厂模式指的是工厂方法模式,工厂方法模式是日常开发中使用频率最高的一种设计模式,甚至在Android的源码中也是随处可见。下面首先来看下简单工厂模式!
简单工厂模式
我们还是以上面的汽车工厂为例,现在做一下修改,建造一个汽车工厂来创建汽车对象!
public class CarFactory {
public Car createCar(String type) {
Car car = null;
if (type.equals("AudiA4")) {
car = new AudiA4();
} else if (type.equals("AudiA6")) {
car = new AudiA6();
} else if (type.equals("AudiA8")) {
car = new AudiA8();
}
car.setType(type);
// 售前检查
car.prepareParts();
// 组装汽车
car.createCar();
// 检查汽车
car.checkCar();
// 汽车出库
car.EXWarehouse();
return car;
}
}
这里是根据客户的选择来创建不同的汽车型号,比如我想要一辆奥迪A6,那么给type赋值为“AudiA6”,工厂收到type后直接 new一个 AudiA6()给我们,如果想要其他类型的也是相同的步骤。
public class Store {
public static void main(String[] args) {
CarFactory carFactory = new CarFactory();
Car audiA6 = carFactory.createCar("AudiA6");
}
}
输出:
准备奥迪A6汽车零件
AudiA6 createCar
AudiA6 checkCar
AudiA6 EXWarehouse
现在,你随便加汽车类型,所有事情都由我这个简单汽车工厂来做,对于专卖店来说,再也不用头痛这些事了!
不过简单方法模式缺点也很明显,
如果工厂类集中了所有的创建逻辑,当有复杂的多层次等级结构时,所有的业务逻辑都在这个工厂类中实现,这个类会变的异常复杂。
什么时候它不能工作了,整个系统都会受到影响。
不符合开闭原则,一旦添加新的对象,就不得不对新的产品进行修改。
工厂方法模式
为了解决简单工厂模式的缺点,于是产生了工厂方法模式。工厂方法模式中将对象实例化延迟到子类中完成,创建对象只需要添加新的子类工厂就可以。
现在要满足如下需求,奥迪公司决定在中国和德国建立汽车工厂,在中国生产国产奥迪,在德国生产进口奥迪。
先对简单工厂进行改装
public class CarFactory {
public Car createCar(String type,String area) {
Car car = null;
if ("imports".equals(area)){
if ("AduiA4".equals(type)) {
car = new ImportsAduiA4();
} else if ("AduiA6".equals(type)) {
car = new ImportsAudiA6();
} else if ("AduiA8".equals(type)) {
car = new ImportsAudiA8();
}
} else if("domestics".equals(area)){
if ("AduiA4".equals(type)) {
car = new DomesticsAduiA4();
} else if ("AduiA6".equals(type)) {
car = new DomesticsAudiA6();
} else if ("AduiA8".equals(type)) {
car = new DomesticsAudiA8();
}
}
return car;
}
}
问题很明显
当奥迪公司决定在美国再开一家工厂,那又要对这个类进行修改,如果添加新的产品还是要修改。
这是一个高度依赖工厂类,本工厂依赖于所有的汽车产品类型,这是不符合依赖倒置原则的——要依赖抽象,不要依赖具体类。无论是高层组件(汽车工厂)还是低层组件(各类型汽车)都要依赖抽象。
因此我们需要改进工厂方法,让其依赖汽车接口而不是所有的奥迪汽车。
public abstract class AbstractCarFactory {
public abstract Car createCar(String type);
}
首先创建一个抽象汽车工厂类,就一个创建汽车的方法,只不过是抽象的,接着我们要创建两个子类工厂继承它。ImportsCarFactory 进口汽车工厂和DomesticsCarFactory 国产汽车工厂。
/**
* 德国进口汽车工厂
*/
public class GermanyImportsCarFactory extends AbstractCarFactory{
private Car car;
@Override
public Car createCar(String type) {
if ("AduiA4".equals(type)) {
car = new ImportsAduiA4();
} else if ("AduiA6".equals(type)) {
car = new ImportsAudiA6();
} else if ("AduiA8".equals(type)) {
car = new ImportsAudiA8();
}
car.setType("AudiA6");
// 售前检查
car.prepareParts();
// 组装汽车
car.createCar();
// 检查汽车
car.checkCar();
// 汽车出库
car.EXWarehouse();
return car;
}
}
我们在进口汽车工厂里面根据type来创建进口的奥迪车,国产汽车工厂也是一样。
/**
* 国产汽车工厂
*/
public class DomesticsCarFactory extends AbstractCarFactory{
Car car;
@Override
public Car createCar(String type) {
if ("AduiA4".equals(type)) {
car = new DomesticsAduiA4();
} else if ("AduiA6".equals(type)) {
car = new DomesticsAudiA6();
} else if ("AduiA8".equals(type)) {
car = new DomesticsAudiA8();
}
car.setType(type);
// 售前检查
car.prepareParts();
// 组装汽车
car.createCar();
// 检查汽车
car.checkCar();
// 汽车出库
car.EXWarehouse();
return car;
}
}
public class CarStore {
public static void main(String[] args) {
DomesticsCarFactory domesticsCarFactory = new DomesticsCarFactory();
Car aduiA6 = domesticsCarFactory.createCar("AduiA6");
GermanyImportsCarFactory importsCarFactory = new GermanyImportsCarFactory();
Car aduiA8 = importsCarFactory.createCar("AduiA8");
}
}
输出:
准备国产奥迪A6汽车零件
AduiA6 createCar
AduiA6 checkCar
AduiA6 EXWarehouse
准备进口奥迪A8汽车零件
AduiA8 createCar
AduiA8 checkCar
AduiA8 EXWarehouse
这就是工厂方法模式,无论是高层组件还是低层组件都是依赖于抽象的Car接口,当我们需要进行扩展时,例如创建美国工厂,只需要创建一个新的 AmericaImportsCarFactory 工厂类继承抽象工厂类就可以,并且不用修改之前的代码。
工厂方法模式也就是汽车工厂是个父类,有生产汽车AbstractCarFactory这个接口。
进口汽车工厂,国产汽车工厂继承它,可以分别生产国产汽车,德国进口汽车。
生产国产汽车还是进口汽车不再由之前的 area 参数决定(详细请看上面代码),而是创建汽车工厂时,由汽车工厂决定。
抽象工厂模式
好了,现在该公司不仅生产汽车了,同时还要生产飞机,这时候该怎么办呢?
抽象工厂模式可以解决这个问题,
也就是创建一个汽车厂商,这是个父类,有生产汽车,生产飞机两个接口。 国产工厂,德国工厂继承它,可以分别生产国产汽车+国产飞机,和德国进口汽车+德国进口飞机。
创建工厂时,如果创建的是国产工厂。 后续工厂生产汽车,则生产国产汽车,工厂生产飞机则生产国产飞机。
定义:
提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们具体的类。( 在抽象工厂模式中,每一个具体工厂都提供了多个工厂方法用于产生多种不同类型的对象) 抽象工厂和工厂方法一样可以划分为4大部分:
AbstractFactory(抽象工厂)声明了一组用于创建对象的方法,注意是一组。
ConcreteFactory(具体工厂):它实现了在抽象工厂中声明的创建对象的方法,生成一组具体对象。
AbstractProduct(抽象产品):它为每种对象声明接口,在其中声明了对象所具有的业务方法。ConcreteProduct(具体产品):它定义具体工厂生产的具体对象。下面还是先看一个具体实例。
/**
* 抽象工厂
*/
public abstract class AbstractCarFactory {
public abstract Car createCar(String type);
public abstract Plane createPlane();
}
/**
* AbstractProduct 抽象产品
*/
public abstract class Plane {
//准备零件
public abstract void prepareParts();
//生产飞机
public void createPlane(){
System.out.println("createPlane");
}
//售前检查
public void checkCar(){
System.out.println("checkPlane");
}
//出库
public void EXWarehouse(){
System.out.println("EXWarehouse");
}
}
/**
* 具体工厂1:进口汽车工厂
*/
public class GermanyImportsCarFactory extends AbstractCarFactory {
private Car car;
@Override
public Car createCar(String type) {
if ("AduiA4".equals(type)) {
car = new ImportsAduiA4();
} else if ("AduiA6".equals(type)) {
car = new ImportsAudiA6();
} else if ("AduiA8".equals(type)) {
car = new ImportsAudiA8();
}
car.setType("AudiA6");
// 售前检查
car.prepareParts();
// 组装汽车
car.createCar();
// 检查汽车
car.checkCar();
// 汽车出库
car.EXWarehouse();
return car;
}
@Override
public Plane createPlane() {
Plane plane = new GermanyImportsPlane375();
plane.prepareParts();
plane.createPlane();
plane.checkCar();
plane.EXWarehouse();
return plane;
}
}
/**
* 具体工厂2:国产汽车工厂
*/
public class DomesticsCarFactory extends AbstractCarFactory {
Car car;
@Override
public Car createCar(String type) {
if ("AduiA4".equals(type)) {
car = new DomesticsAduiA4();
} else if ("AduiA6".equals(type)) {
car = new DomesticsAudiA6();
} else if ("AduiA8".equals(type)) {
car = new DomesticsAudiA8();
}
car.setType("AudiA6");
// 售前检查
car.prepareParts();
// 组装汽车
car.createCar();
// 检查汽车
car.checkCar();
// 汽车出库
car.EXWarehouse();
return car;
}
@Override
public Plane createPlane() {
Plane plane = new DomesticsPlane375();
plane.prepareParts();
plane.createPlane();
plane.checkCar();
plane.EXWarehouse();
return plane;
}
}
public class Store {
public static void main(String[] args) {
DomesticsCarFactory domesticsCarFactory = new DomesticsCarFactory();
Car aduiA6 = domesticsCarFactory.createCar("AduiA6");
GermanyImportsCarFactory importsCarFactory = new GermanyImportsCarFactory();
Car aduiA8 = importsCarFactory.createCar("AduiA8");
//测试新加的飞机工厂
Plane domesticsCarFactoryPlane = domesticsCarFactory.createPlane();
Plane importsCarFactoryPlane = importsCarFactory.createPlane();
}
}
输出:
准备国产奥迪A6汽车零件
AudiA6 createCar
AudiA6 checkCar
AudiA6 EXWarehouse
准备进口奥迪A8汽车零件
AudiA6 createCar
AudiA6 checkCar
AudiA6 EXWarehouse
准备国产375飞机零件
createPlane
checkPlane
EXWarehouse
准备进口375飞机零件
createPlane
checkPlane
EXWarehouse
看看现在不仅能造汽车,我还能造飞机,是不是很简单呢?
这里我就举了一个375型号飞机的例子,其他的型号如果相加就创建对应的类比如275型号,并且继承抽象产品Plane就ok了~
工厂模式可以解决什么问题?
- 解耦:把对象的创建和使用的过程分开
- 降低代码重复:如果创建某个对象的过程都很复杂,需要一定的代码量,而且很多地方都要用到,那么就会有很多的重复代码。
- 降低维护成本:由于创建过程都由工厂统一管理,所以发生业务逻辑变化,不需要找到所有需要创建某个对象的地方去逐个修正,只需要在工厂里修改即可,降低维护成本。
工厂模式的优缺点?
优点:
更符合开-闭原则:新增一种产品时,只需要增加相应的具体产品类和相应的工厂子类即可。
符合单一职责原则:每个具体工厂类只负责创建对应的产品。
符合依赖倒置原则:无论底层组件还是高层组件都依赖于抽象而不是具体实现。
缺点:
添加新的产品时,除了产品类外,还要提供与之对应的具体工厂实现,系统类的个数将成倍增加,在一定程度上增加了系统的复杂度;同时,有更多的类需要编译和运行,会给系统带来一些额外的开销。
虽然保证了工厂方法内的对修改关闭,但对于使用工厂方法的类,如果要更换另外一种产品,仍然需要修改实例化的具体工厂类。(例如一家专卖店不卖国产,改卖进口了,那就得对店铺进行修改)
一个具体工厂只能创建一种类型的具体产品,进口工厂无法创建国产产品,国产工厂也不能创建进口汽车。
什么时候使用工厂模式?
有时候,构造对象确实只要简单的new就可以了,但另一些时候,构造对象的过程就比较复杂,比如要有很多条件判断啊,参数选择啊之类的,这时候把它们交给调用者去做就有些不太负责和不合理。好比我(调用者)需要一架纸飞机,行,你给我张纸,我自己折,问题不大,这就好比是简单的new个对象出来。可是如果我要架真飞机,要我造不累死我?当然得交给飞机工厂去做啦。这就是工厂模式做的事。
再通俗一点来说:
当你发现自己的代码,写成这样的,你也许就应该使用工厂了:
IFoo obj;
if (someCondition) {
obj = new RegularFoo();
} else if (otherCondition) {
obj = new SpecialFoo();
} else {
obj = new DefaultFoo();
}
总结(面试重点)
工厂方法模式:
一个抽象产品类,可以派生出多个具体产品类。
一个抽象工厂类,可以派生出多个具体工厂类。
每个具体工厂类只能创建一个具体产品类的实例。
抽象工厂模式:
多个抽象产品类,每个抽象产品类可以派生出多个具体产品类。
一个抽象工厂类,可以派生出多个具体工厂类。
每个具体工厂类可以创建多个具体产品类的实例。
区别:
工厂方法模式只有一个抽象产品类,而抽象工厂模式有多个。
工厂方法模式的具体工厂类只能创建一个具体产品类的实例,而抽象工厂模式可以创建多个。
本文对工厂方法模式进行了简单描述。如果有写的不对的地方欢迎指教~
评论区