老铁们,我们今天来说说工厂模式。
0. 前言
一般来讲,工厂模式分3种,分别是:简单工厂模式、工厂方法模式以及抽象工厂模式。由于这几种容易混淆,我们放在一起聊聊。
其实,任何可以产生对象的方法或者类,都可以叫做工厂。换句话说,工厂模式就是把以前通过new产生对象的方式变为通过工厂取得。
为什么要有工厂模式,直接通过new创建对象不行吗?
我们说,也可以。但是,把创建对象的过程抽取出来,能够灵活控制对象生产过程,例如在其中增加一些单例控制,或者权限、日志之类的其他处理,这些需求都可以交给工厂去实现,方便解耦。
当然了,有人说了,我这只需要简单new出来一个对象,其他也用不到,那可以不用设计模式。总之,设计模式是让系统变得更简单,而不更是复杂。
1. 简单工厂模式
我们暂且从一个简单的类开始吧。
现在有个简单的Bread类。
Bread.java
public class Bread{
void eat() {
System.out.println("冷风在北京 eat bread");
}
}
那现在要吃面包,怎么办?没问题,new一个Bread对象,然后调用eat方法。
Client.java
public class Client {
public static void main(String[] args) {
Bread b = new Bread();
b.eat();
}
}
有一天面包吃腻了,想吃饺子,那我们就建个“饺子”对象。
Dumplings.java
public class Dumplings{
void eat() {
System.out.println("冷风在北京 eat Dumplings");
}
}
然后再修改客户端:
Client.java
public class Client {
public static void main(String[] args) {
Dumplings d = new Dumplings();
d.eat();
}
}
假设到某一天,饺子也吃腻了,想吃.........。你会发现,客户端每次都需要改代码,new出来相应的类。那能不能 “坐享其成”,给工厂传个参数,或者调用一个工厂方法,就能生成想要的对象。
工厂模式就是来解决这个问题的。
我们先抽象出一个Food类,Bread和Dumplings去继承。
Food.java
public abstract class Food {
abstract void eat();
}
Bread.java
public class Bread extends Food{
@Override
void eat() {
System.out.println("冷风在北京 eat bread");
}
}
Dumplings.java
public class Dumplings extends Food{
@Override
void eat() {
System.out.println("冷风在北京 eat Dumplings");
}
}
此时,再创建一个简单工厂类,去生产我们需要的具体产品。
SimpleFoodFactory.java
public class SimpleFoodFactory {
//简单工厂, 通过传入参数或者直接用方法来区分,以实现不同的对象返回.可扩展性不太好,如果要吃点别的.
Food makeBread(){
//doSth();其他处理
return new Bread();
}
Food makeDumplings(){
//doSth();其他处理
return new Dumplings();
}
}
客户端的调用修改为:
Client.java
public class Client {
public static void main(String[] args) {
SimpleFoodFactory ff = new SimpleFoodFactory();
Food f = ff.makeDumplings();
f.eat();
}
}
这样,只需要去调用SimpleFoodFactory的makeBread或者makeDumplings方法,就能取到需要的对象了。
小结一下。
简单工厂其实就是将各个对象的创建过程封装了起来,需要对象时,直接调用方法去拿。
简单工厂模式的要素:
-
一个抽象类或者接口(Food)
-
几个具体的实现类(Bread、Dumplings)
-
一个简单工厂类(SimpleFoodFactory)
优点:将创建对象的逻辑封装起来,便于解耦。
缺点:扩展性不好,当增加其他具体的Food类型时,需要修改SimpleFoodFactory类,增加makeXXX方法。
2. 工厂方法模式
鉴于简单工厂不好扩展的问题,就出现了工厂方法模式。该模式下,我们不再像简单工厂那样去传参数或者调用方法去创建不同的对象,而是将每一个具体的产品都创建一个工厂,即面包有面包的工厂,饺子有饺子的工厂。
BreadFactory.java
public class BreadFactory {
//工厂方法;将具体的产品交给具体的工厂来实现,扩展性变好
Bread makeBread(){
return new Bread();
}
}
DumplingsFactory.java
public class DumplingsFactory {
//工厂方法;将具体的产品交给具体的工厂来实现,扩展性变好
Dumplings makeDumplings(){
return new Dumplings();
}
}
此时,调用方修改为:
//Food f1 = new BreadFactory().makeBread();
Food f2 = new DumplingsFactory().makeDumplings();
f2.eat();
这样,扩展起来就方便多了,如果增加Cookie,只需要增加Cookie类和CookieFactory即可,而不像简单工厂模式,需要修改工厂类了。
工厂模式的要素:
-
一个抽象类或者接口(Food)
-
几个具体的实现类(Bread、Dumplings)
-
多个具体工厂类(DumplingsFactory)
如果需要扩展时,只需要新建具体产品和具体工厂类即可。
3. 抽象工厂模式
上面说的都是具体的工厂类,那为什么还需要抽象工厂呢?
我们来考虑这样一个问题,现在不光是吃面包和饺子的问题了,吃完了还得喝点啥。像Food一样,我们先扩展出一个Drink抽象类。
Drink.java
public abstract class Drink {
abstract void drink();
}
CocaCola.java
public class CocaCola extends Drink{
@Override
void drink() {
System.out.println("冷风在北京 drink cocacola");
}
}
Soup.java
public class Soup extends Drink{
@Override
void drink() {
System.out.println("冷风在北京 drink Soup");
}
}
这时,在工厂模式的基础上,我们再向上抽象一层,抽象出抽象工厂类。抽象工厂类包括了eat()和dirnk()方法。
AbstractFactory.java
public abstract class AbstractFactory {
abstract Food makeFood();
abstract Drink makeDrink();
}
现在有个美国人和中国人,那美国人的习惯和中国人肯定不一样。 我们建2个具体工厂,来继承抽象工厂。
AmericanFactory.java
public class AmericanFactory extends AbstractFactory{
@Override
Food makeFood() {
return new Bread();
}
@Override
Drink makeDrink() {
return new CocaCola();
}
}
ChineseFactory.java
public class ChineseFactory extends AbstractFactory{
@Override
Food makeFood() {
return new Dumplings();
}
@Override
Drink makeDrink() {
return new Soup();
}
}
类图就变成了这个样子。
所以说,顾名思义,抽象工厂可以理解为只生产抽象的一组产品(吃的、喝的)。下面有具体的产品工厂去继承抽象工厂,生产更具体的一系列产品。美国工厂生产可乐和面包,中国工厂生产饺子和汤。假设后面又出现了印度工厂(假设印度人吃咖喱,喝水实在不知道阿三喝啥了),我们只需要三个具体类:
-
IndianFactory extends AbstractFactory
-
Curry extends Food
-
Water extends Drink
相比于前两种模式,抽象工厂模式增加了抽象工厂这一个要素,共有四个角色:
-
抽象工厂(吃的、喝的)
-
具体工厂 (中国工厂,美国工厂等)
-
抽象产品 (吃的,喝的)
-
具体产品 (面包,可乐,饺子,汤)
看得出来,抽象工厂是从一个产品组的角度去看问题的,而工厂方法和简单工厂只关注于具体产品本身。即抽象工厂要和多类产品打交道,而工厂方法和简单工厂只和具体某个产品联系。
总结
我们说,上述三种模式,没有哪个最好,只有谁更适合什么场景。
-
简单工厂模式
优点:将创建对象的逻辑封装起来,便于解耦。
缺点:不方便扩展。当需要一个新的类型时,需要修改工厂类。
适用场景:业务简单,产品较少,且不易变化的场景。
-
工厂方法模式
优点:方便扩展,每增加一个具体类,只需要增加工厂即可。
缺点:当产品较多时,增加代码较多。
适用场景:产品有限扩展的情况。
-
抽象工厂
优点:相比前两种,更适宜去管理一系列的产品,在产品系列方向比较容易扩展。
应用场景:产品系列管理。
最后,我们再来理解一下这三种模式。
简单工厂模式首先将创建对象的过程封装了起来,其中可以增加一些过程上的控制,例如单例、权限之类的。客户端不用关心对象的创建过程,只需要上送要创建的对象名称即可。但是由于简单工厂扩展时需要修改工厂类,所以又出现了工厂模式。
在工厂模式下,为了便于扩展,所以将每个具体的产品都建一个工厂类。
而出于对产品组合管理的需求,又出现了抽象工厂模式。
抽象工厂的关注点在于产品组合(吃的+喝的),更像是去做一件完整的事情,将诸多产品去有机组织起来。而工厂方法和简单工厂比较简单,只用来生产具体产品。
好了,工厂模式就先分享到这儿,希望能对各位小伙伴有帮助。