设计模式是一套被反复使用,多数人知晓,经过分类编目的,代码设计的总结,也可以说是前人的智慧结晶。学习设计模式能让我们对一些应用场景使用相同的套路达到很好的效果,我会不定时更新一些自己对设计模式的理解的文章,从定义,实现,应用场景来说说设计模式,今天我要说的对象是工厂模式
一:定义
什么是工厂?工厂模式是我们最常用的实例化对象模式了,是用工厂方法代替new操作的一种模式,工厂模式属于创建型模式:对象实例化的模式,用于解耦对象的实例化过程。简单的说工厂模式就是用来创建对象的,传统的对象创建方法是new,有了工厂模式之后,某些特定的对象我们不需要在每个使用的地方都使用new,而是把对象的创建放在一个或者一组统一的类中,从达到解耦的目的。
二:实现
从实现上来说,工厂模式分为三种,简单工厂模式,工厂模式,抽象工厂模式,简单工厂模式并不属于23中设计模式的范畴,因为它对解耦的实现不理想,同时不符合开不原则,不过简单工厂模式是工厂模式的一种简易实现,可以为学习工厂模式提供一个好的开端。在展示具体的代码之前,我们先来说说一个类的作用,我们提供一个类,无非是希望通过这个类来帮助我们来实现一些功能,而工厂模式中生产出来的类,都具有相同的功能,只不过这个功能的具体实现可能不一样,
1.简单工厂模式
简单工厂模式的设计思想非常简单,就像一个小型的工厂,能生产几种特定的产品,小工厂一般都很本分,不敢太冒进,只有在收到订单(接收到指令)的时候才敢生产产品,如果要扩大业务,生产新的产品,必须对工厂进行改造,小本生意,本来就没有太多冗余的空间。小工厂呢,通常是有了某种渠道,获得了某几种产品的生产权,然后才针对真几种产品创建了工厂,所以,在工厂之前,产品是确定的,而且产品的类型还不能区别太大,一般是某种大类产品下的几个分类,这样这个工厂的很多设备模具都可以共用,节约成本,下面我们就以代码的形式来体现工厂
首先,确认产品,确认产品之前,还要确认产品的类型,方便购买一些通用的设备我们的产品就是轮胎,每款轮胎上都有一些介绍信息,标识当前轮胎的种类,虽然轮胎的种类很多,功能不一,但是介绍信息的格式都一样
abstract class Tyre{
abstract void desc();
}
//这是奔驰的轮胎
class BenzTyre extends Tyre{
@Override
void desc() {
System.out.println("这是奔驰的轮胎");
}
}
//这是宝马的轮胎
class BMWTyre extends Tyre{
@Override
void desc() {
System.out.println("这是宝马的轮胎");
}
}
//这是奥迪的轮胎
class AudiTyre extends Tyre{
@Override
void desc() {
System.out.println("这是奥迪的轮胎");
}
}
既然已经确认了订单,那么接下来我们可以开始建设工厂了
class TyreFactory{
public static Tyre createTyre(String TyreName){
switch (TyreName){
case "Benz":
//接受到生产奔驰轮胎的指令
return new BenzTyre();
case "BMW":
//接受到生产宝马轮胎的指令
return new BMWTyre();
case "Audi":
//接受到生产生产奥迪轮胎的指令
return new AudiTyre();
default:
return null;
}
}
}
下面模拟客户下单,工厂生产轮胎,拿到轮胎后确认产品的过程
class Clients{
public static void main(String[] fdfd){
//工厂接到指令生产了一个奔驰的轮胎
Tyre tyre=TyreFactory.createTyre("Benz");
//客户拿到轮胎后看一下标签
tyre.desc();
//工厂接到指令生产了一个宝马的轮胎
tyre=TyreFactory.createTyre("BMW");
//客户拿到轮胎后看一下标签
tyre.desc();
//工厂接到指令生产了一个奥迪的轮胎
tyre=TyreFactory.createTyre("Audi");
//客户拿到轮胎后看一下标签
tyre.desc();
}
}
总结:简单工厂模式有一定的优点,对于客户来说,只需要下达一个指令,就可以获取到自己需要的产品,而不用去关心产品的生产过程。但也有很明显的缺点,就现在的工厂来说,只符合生产现有产品的要求,当有新产品添加的时候,必须对现在的工厂进行改造,改造必然需要拆装,拆装的过程有导致整个工厂倒塌的风险。在代码的角度来说,工厂模式符合里氏替代原则,在一定程度上解决了耦合问题,但是同时,它有违反了开闭原则。当一个新的产品需要被添加的时候,必须对工厂类进行改造。那么,是否有办法解决这个问题呢,答案当然是有的,那就是工厂模式
2.工厂模式
工厂模式是简单工厂模式的进阶版,我们知道,简单工厂模式的主要缺点是不符合开闭原则,当新产品被添加的时候,需要更改工厂类,所以,在工厂模式中,我们专门对工厂类进行了改造。为了生产新的产品不改造工厂类,我们抽象工厂类,也就是把工厂类变成一个抽象的对象,不进行产品的生产,把产品的生产过程交给它的子类进行,当有新的产品添加的时候,只需要添加新的子类即可,不涉及已有代码的更改。当然,这些子类,其实也是一个工程类,只不过是只针对某一种产品的工程类,下面展示代码抽象原有的工厂类,只定义应该生产的产品类型,而不再进行具体产品的生产
abstract class TyreFactory{
//提取子类必须实现的方法
abstract Tyre createTyre();
}
创造宝马轮胎厂,只生产宝马轮胎
class BMWTyreFactory extends TyreFactory{
//宝马轮胎厂生产宝马轮胎
@Override
Tyre createTyre() {
return new BMWTyre();
}
}
创造奔驰轮胎厂,只生产奔驰轮胎
class BenzTyreFactory extends TyreFactory{
//奔驰轮胎厂生产奔驰轮胎
@Override
Tyre createTyre() {
return new BenzTyre();
}
}
创造奥迪轮胎厂,只生产奥迪轮胎
class AudiTyreFactory extends TyreFactory{
//奥迪轮胎厂生产奥迪轮胎
@Override
Tyre createTyre() {
return new AudiTyre();
}
}
再看调用
class Clients{
public static void main(String[] fdfd){
//奔驰工厂生产了一个奔驰的轮胎
Tyre tyre=new BenzTyreFactory().createTyre();
//客户拿到轮胎后看一下标签
tyre.desc();
//宝马工厂生产了一个宝马的轮胎
tyre=new BMWTyreFactory().createTyre();
//客户拿到轮胎后看一下标签
tyre.desc();
//奥迪工厂生产了一个奥迪的轮胎
tyre=new AudiTyreFactory().createTyre();
//客户拿到轮胎后看一下标签
tyre.desc();
}
}
总结:工厂模式很好的解决了简单工厂模式不符合开闭原则的缺点,在工厂模式中新增一个产品,只需要增大产品类和对应的工厂类,不会涉及原有代码的更改,符合开闭原则,扩展不会对原有的功能产生影响,同样工厂模式也符合里氏替代原则。但是,工厂模式并不完美,我们注意到,工厂模式只能生产一个类别的产品。比如,我们上面的轮胎工厂,只能生产轮胎,如果想生产内胆呢,而且内胆和轮胎往往都是配套使用的,宝马的轮胎不可能配用奔驰的内胆,对于这种配套的产品(对象),为了不混淆,有没有一个好的解决方案呢,答案当然是有的,那就是抽象工厂模式
3.抽象工厂模式
抽象工厂模式最重要的思想就是引入了产品族的感念,可以理解为配套或者或有关联的一系列产品(对象),组成一个产品族,使用这个工厂不再生产单一的产品,而是生产一整套的产品,比如我们的轮胎工厂,直接生产配套的轮胎和内胆,而不用担心奔驰的轮胎用了宝马的内胆。那么,我们需要添加一个新的产品:内胆,由于奔驰,宝马,奥迪的内胆都不一样,所以要独立创建这三种车的内胆,并且要在原来顶层工厂类中添加一个生产内胆的抽象方法,而对应的这三种车的工厂类在生产轮胎的同时要配套的生产内胆
//创建内胆产品类
abstract class Innertube{
//内胆的标签,显示内胆的出身信息
abstract void innertubeDesc();
}
//奔驰内胆
class BenzInnertube extends Innertube{
@Override
void innertubeDesc() {
System.out.println("这是奔驰的内胆");
}
}
//宝马内胆
class BMWInnertube extends Innertube{
@Override
void innertubeDesc() {
System.out.println("这是宝马的内胆");
}
}
//奥迪内胆
class AudiInnertube extends Innertube{
@Override
void innertubeDesc() {
System.out.println("这是奥迪的内胆");
}
}
//工厂类的改变
abstract class Factory{
//生产轮胎的方法
abstract Tyre createTyre();
//生产内胆的方法
abstract Innertube createInnertube();
}
//奔驰
class BMWFactory extends Factory{
//宝马轮胎厂生产宝马轮胎
@Override
Tyre createTyre() {
return new BMWTyre();
}
//宝马轮胎厂生产宝马内胆
@Override
Innertube createInnertube() {
return new BMWInnertube();
}
}
//宝马
class BenzFactory extends Factory{
//奔驰轮胎厂生产奔驰轮胎
@Override
Tyre createTyre() {
return new BenzTyre();
}
//奔驰轮胎厂生产奔驰内胆
@Override
innertube createInnertube() {
return new BenzInnertube();
}
}
//奥迪
class AudiFactory extends Factory{
//奥迪轮胎厂生产奥迪轮胎
@Override
Tyre createTyre() {
return new AudiTyre();
}
//奥迪轮胎厂生产奥迪内胆
@Override
Innertube createInnertube() {
return new AudiInnertube();
}
}
//模拟客户下单
class Clients{
public static void main(String[] fdfd){
//向奔驰工厂下一个单,生产一套奔驰专用的轮胎和内胆
BenzFactory benzFactory=new BenzFactory();
//生产
Tyre tyre=benzFactory.createTyre();
Innertube innertube=benzFactory.createInnertube();
//客户拿到轮胎后看一下标签
tyre.desc();
innertube.innertubeDesc();
//向宝马工厂下一个单,生产一套宝马专用的轮胎和内胆
BMWFactory bmwFactory=new BMWFactory();
//生产
tyre=bmwFactory.createTyre();
innertube=bmwFactory.createInnertube();
//客户拿到轮胎后看一下标签
tyre.desc();
innertube.innertubeDesc();
//向奥迪工厂下一个单,生产一套奥迪专用的轮胎和内胆
AudiFactory audiTyreFactory=new AudiFactory();
//生产
tyre=audiTyreFactory.createTyre();
innertube=audiTyreFactory.createInnertube();
//客户拿到轮胎后看一下标签
tyre.desc();
innertube.innertubeDesc();
}
}
不难看出,在工厂模式的基础上,抽象工厂模式产品族感念的出现对于生产成套的产品(有联系的对象)提供了很大的便利,也符合开闭原则,里氏替代原则,不过也有它的缺陷:当在产品族(顶级工厂类)中需要添加一种新的产品的时候,所有的下级工厂都要进行修改。
三:应用场景
在什么场景下应该使用工厂模式
1.一个系统不应当依赖于产品类实例如何被创建、组合和表达的细节
2.这个系统的产品有多于一个的产品族,而系统只消费其中某一族的产品。
3.同属于同一个产品族的产品是在一起使用的,这一约束必须在系统的设计中体现出来。
4.系统提供一个产品类的库,所有的产品以同样的接口出现,从而使客户端不依赖于实现。