zl程序教程

您现在的位置是:首页 >  其他

当前栏目

原来这么多年都用错了工厂模式

2023-02-25 18:22:18 时间

1. 设计模式我会啊,还用你教?

受益于Spring的广泛应用,大家对单例模式和工厂模式耳熟能详。

设计模式归纳的有23种,

可以划分为包括创建型、结构型、行为型三大类。


除了简单的那两种,其他的各种设计模式到底在什么场合使用呢。

本系列将给大家举出案例,带大家一步步的了解和深入。

三种模式的理解难度,应该是递进的,创建型最容易。

这些知识我们会整理在公主号thisjava里

因此先从最简单的开始。


创建型模式共有五类,如下:

序号

名称

英文

1

工厂方法

factory method

2

抽象工厂

abstract factory

3

单例

singleton

4

建造者

builder

5

原型

prototype method

因为这里面会大量涉及到面向对象的知识,首先从基础部分讲起。

2. 面向对象三要素

OO的三要素:封装、继承、多态,这几个内容我们简要说一下。

2.1 封装

封装最常见,我们有三个关键词:

public 
protected 
private

用于修饰属性和方法。

其中,public是公开的,对外和对内皆可用;

private是仅对内使用的。

这就是最基本的封装。

那问题来了,protected是干啥的?


protected是一个双重身份,首先他不对外,仅对内部使用。

看起来很像private。

但是,子类可以使用父类的protected属性或方法。

因此,如果没有继承的概念,protected就相当于private。

下面讲讲继承。

2.2 继承

继承也不难,万物继承于Object

集合继承于Collection

I/O流继承于Closeable

线程继承于Runnable

标准的JDK给我们做了一个很好的示范

继承给我们解决了两个重要功能:

  1. 继承父类,就可以继承它已经具备的能力
  2. 可以采用增加子类的方式,扩充新功能

由于父类只能有一个,

因此如果需要继承多个父类,只能采用多层方式,

例如 B extends A, 然后 C extends B,

这种继承体验很差。

因此凡是多重继承,一般都采用接口继承的方式。

这里面有包括接口、抽象类、重载、覆盖等知识点,以及多态性的知识,大家自行学习,不再赘述。

写程序的时候,推荐的是先写接口,再写实现

我们的项目结构往往是这样的:

class DemoController implements IDemoFeign

class DemoServiceImpl implements IDemoService

class DemoDaoHibernate implements IDemoDao

特别是新人,常常问到,我直接写class不行啊?

非要多写个interface有啥用啊?

为了分层而分层吗?

你需要理解一个关键知识点:里氏替换原则

2.3 里氏替换

里氏替换对继承关系做了限制

要求子类可以继承父类的功能,但不能改变父类的功能。

这就对子类覆盖重写父类方法下了禁令。

里氏替换不是万能的,它强调的是对兼容性和复用性的侧重。

代码里经常会写:

List list=new ArrayList();

这就是说,我只需要知道List接口的用法即可,

ArrayList只是具有特点的一个扩展实现而已,

并不影响使用者的理解,只要会用List接口即可。

这种 父类(接口)= new 子类() 的写法,广泛用于各种领域,

例如Spring的DIP,依赖倒置,往往都是建议定义接口优先,面向接口编程。

这就是可以解答前面的疑问,为什么我们代码里往往都是写

class DemoServiceImpl implements IDemoService

越来越多的人开始使用Slf4j,这也是一个接口编程的典型案例。

掌握这些基础后,开始学习最简单的工厂模式。

3. 工厂模式

3.1 简单工厂模式

简单的工厂其实没有太大的应用价值,

他只是符合工厂模式:对象不是new出来的,而是工厂类生产的,这一特点

例如:

public class FruitFactory{
    public Apple createApple(){
        //TODO 
    }

    public Banana createBanana(){
        //TODO 
    }

    public Fruit createFruit(String type){
        //TODO if ... else 
    }
}

或者可以采用静态方法,可以不要创建工厂对象

public class FruitFactory{
    public static Apple createApple(){
        //TODO 
    }

    public static Banana createBanana(){
        //TODO 
    }

    public static Fruit createFruit(String type){
        //TODO if ... else 
    }
}

我们这个例子,虽有其形,但意义不大,这个工厂太开放了,没有任何约束


工厂真正要解决的是产品出口一致性,以及产品之间的相关性。

一个工厂,又生产铁锅,又生产水果,还生产铅笔,这像话吗?

写这种自己都不知道干什么的工厂,

就是程序员给自己挖坑造bug。

所以我们要做有意义的工厂。

解决方式很简单:

1.通过规范编码,使简单工厂满足使用价值

2.通过规范工厂,使工厂的产品明确

3.2 工厂方法模式

场景:我们需要开发一个发送消息的功能

首先定义接口:

interface IMessage{
    void send(String text);
}

假设,我们的系统支持 短信通知、微信通知 两种模式,

那自然会有:

public class SmsMessage implements IMessage{
    public void send(String text){
        //TODO 短信通知
    }
}

public class WechatMessage implements IMessage{
    public void send(String text){
        //TODO 微信通知
    }
}

这些我们可以认为是具体的功能实现,划分在组件库中。

对于高级程序员,并不在乎这些东西怎么实现的,只要能满足功能就行了。


然后,将他们写在不同的工厂里

public interface IMessageFactory{
    IMessage getInstance();
}

public SmsMessageFactory implements IMessageFactory{
    public IMessage getInstance(){
        return new SmsMessage();
    }
}

public WechatMessageFactory implements IMessageFactory{
    public IMessage getInstance(){
        return new WechatMessage();
    }
}

所以这种方式,将选择权交给了工厂类。

你不要让我选用哪个具体的产品,我也选不来,直接告诉我工厂就行。

想象一个场景,太太想买一辆车,你会如何选择?

列出一堆参数,让她选吗?其实没那么复杂

她只会说:老公,我们买一辆奔驰怎么样?

或者说:老公,我闺蜜买了一辆宝马的车,我觉得很好看。

这选的不是车,而是工厂。


这种模式,适合于关注点在工厂的选择,而不是产品本身的情况。

比如我需要接入短信平台,你给我建3个工厂类,移动、联通、电信。

作为用的人,我只要选择厂家,无需关注短信平台这个细节。

3.3 抽象工厂模式

抽象工厂模式用的较少,主要应对复杂的工厂和对象的组合情况。

作为不同的工厂,其中的产品具有共性,或者类似,

则可以用抽象工厂来对工厂再做一次抽象。

一般的项目里,用不到这种场景。

目前比较被大家熟知的例子,就是Spring的BeanFactory

先将产品归类分组,再将其抽象成工厂类。


短信运营商的例子不太好讲,因为国内就只有三巨头,

最多3个工厂类就能解决。

还是之前买车的例子,设想一个场景,

现在选择的范围不仅仅是宝马,奔驰,

还有劳斯莱斯、宾利、迈巴赫、法拉利、兰博基尼、迈凯伦、阿斯顿马丁、布加迪……

你作为一个程序员,该怎么写呢?

每来一个品牌,建一个工厂类?明显不现实,这得频繁修改代码。

所以这时候要对工厂再次抽象,原来我们的类是这样的:

奔驰车=奔驰厂.create();
宝马车=宝马厂.create();
宾利车=宾利厂.create();
劳斯莱斯车=劳斯莱斯厂.create();
迈巴赫车=迈巴赫厂.create();
……

现在我们可以找一下厂家的共性,比如按国家,或者按价格,或者按能源

这样我们可以构造如下几种分法:

方法一:按国家

GermanFactory: 生产德国车

AmericanFactory: 生产美国车

ItalianFactory: 生产意大利车

方法二:按能源

OilFactory:生产汽油车

ElecFactory:生产电车

还有更多的分法,这样就将工厂再次抽象了

以后太太问我买什么车,只需要说个大概,

买新能源吧,买德国车吧,就能概括了

这种模式下,工厂类就可以限制在很少的范围下

以解决工厂类太多的情况。


其实很多设计模式都是来自生活,

通过搭配一些常见的生活案例就很容易理解。

工厂模式是比较入门的设计模式

对产品的构造参数,要求越少越好,最好无参数构造。

如果参数较多,就需要选择其他模式了,

我们在后续的系列中会给大家一一介绍。