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

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

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

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

设计模式归纳的有23种,

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


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

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

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

因此先从最简单的开始。


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

序号 名称 英文
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:生产电车

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

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

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

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

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


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

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

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

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

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

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

相关文章
|
敏捷开发 前端开发 开发者
想要成为软件开发中的王者,需要明白的 21 条准则
想要成为软件开发中的王者,需要明白的 21 条准则
|
6月前
|
数据采集 Oracle 关系型数据库
我通过三个故事终于学明白了三种工厂模式
我通过三个故事终于学明白了三种工厂模式
73 0
|
4月前
|
搜索推荐 算法 网络协议
如何做好IT类的技术面试?
如何做好IT类的技术面试?
51 0
|
6月前
|
设计模式 缓存 算法
探索设计模式的魅力:创新思维与面试中的设计模式问题(超详细38题3万8字参考答案)
设计模式是在软件设计中经常出现的问题的解决方案。它是由经验丰富的开发人员在解决常见设计问题时总结出来的一套通用的解决方案,是一种对反复出现的设计问题的可重用的抽象描述。设计模式可以用于高质量的软件开发,它在软件开发过程中提供了一种复用可扩展性简化代码维护等方面的解决方案。设计模式通常包括以下几个要素:用来描述问题、解决方案和效果的简洁术语。描述了在何时使用该模式以及问题的背景和条件。
138 0
|
6月前
|
设计模式
二十三种设计模式全面解析-抽象工厂模式:创造无限可能的工厂之道
二十三种设计模式全面解析-抽象工厂模式:创造无限可能的工厂之道
|
6月前
|
前端开发 JavaScript 程序员
如何做好IT类的技术面试
如何做好IT类的技术面试
|
设计模式 架构师 Java
白活了!谷歌架构师10年心血汇成的《24种设计模式》,这才是正解
设计模式 设计模式(Design Pattern)是前辈们对代码开发经验的总结,是解决特定问题的一系列套路。它不是语法规定,而是一套用来提高代码可复用性、可维护性、可读性、稳健性以及安全性的解决方案。一看代码一团糟,那这人肯定不怎么样。 现在各种开源框架里满满都是设计模式,所以可以不用但是最好要懂,除非一辈子CRUD,不看框架。
|
数据采集 算法 C++
库调多了,都忘了最基础的概念-《单例模式VS状态码》
库调多了,都忘了最基础的概念-《单例模式VS状态码》
78 0
库调多了,都忘了最基础的概念-《单例模式VS状态码》
|
消息中间件 运维 Dubbo
源码阅读的方法、误区以及三种境界
源码阅读的方法、误区以及三种境界
|
程序员
工作这么多年,你能向新人解释清到底什么是面向对象编程吗?(上)
工作这么多年,你能向新人解释清到底什么是面向对象编程吗?(上)

相关实验场景

更多