工厂模式(FactoryPattern)
工厂模式是一种常见的创建型的设计模式,是将对象的创建交给所谓的“工厂”进行,降低了客户端与创建的对象的耦合,也就是解耦。
工厂模式又分为简单工厂模式(SimpleFactoryPattern)、工厂方法模式(FactoryMethodPattern)和抽象工厂模式(AbstractFactoryPattern)。
如何使用工厂模式,用农民养殖的道理告诉你。
作为一名生活在农村的勤劳农民,如果想吃鸡的话,一般的话都是自己家饲养的,这就好比我们需要某个类的实例就必须用new Object()
创建出来。
虽然这样也比较简单,但是有时我们需要的多了,自己家饲养的鸡可就不够了,而且有时我们并不一定要吃鸡,还要吃鸭或者吃鹅,这时候就比较麻烦了。
相关的代码就需要修改,凡是和创建对象相关的代码片段都要修改,这是个巨大的工作量。
那么简单工厂模式就出场了。
简单工厂模式(SimpleFactoryPattern)
作为农民的你很有经济头脑,在农村想吃鸡直接在家饲养就可以了,但是生活在城市里的人们可没有条件进行直接饲养,于是你想把鸡卖给城里人。
于是你在家开始了养殖之路。
首先你养的是家禽(Fowl
),具体的有鸡(Chicken
)和鸭(Duck
)。
Fowl.java
public interface Fowl {
void yell();
}
Chicken.java
public class Chicken implements Fowl {
@Override
public void yell() {
System.out.println("我是一只鸡");
}
}
Duck.java
public class Duck implements Fowl {
@Override
public void yell() {
System.out.println("我是一只鸭");
}
}
作为在家开展养殖副业的你,发展的肯定顺风顺水。但是之前养殖都是小打小闹,在自家院子里还能养,随着规模的扩大,就需开一个养殖厂进行机械化养殖,这样方便管理而且更高效。
MyFactory.java
public class MyFactory {
public static Fowl getFowl(String name) {
Fowl fowl = null;
if (name.equals("Chicken"))
fowl = new Chicken();
else if (name.equals("Duck"))
fowl = new Duck();
return fowl;
}
}
通过这个养殖厂,养殖鸡和鸭就方便多了,直接告诉工厂想要什么样的家禽,工厂就直接给你提供完成的产品。
Client.java
public class Client {
public static void main(String[] args) {
Chicken chicken = (Chicken) MyFactory.getFowl("Chicken");
chicken.yell();
Duck duck = (Duck) MyFactory.getFowl("Duck");
duck.yell();
}
}
这样和我们创建对象也一样,直接告诉工厂我们想要什么样的对象,工厂直接就返回我们想要的对象。
我是一只鸡
我是一只鸭
UML图:
整个系统主要包括以下角色:
Fowl
-抽象产品Chicken
和Duck
-具体产品MyFactory
-工厂
优点:
- 通过工厂创建对象,我们可以免于直接接触对象创建;这就像之前家庭作坊式的养殖,任何事情都是自己亲自做,现在有养殖场了,如果你想要什么,直接吩咐下去工厂里的工人就给解决了,多省事啊。
- 对于一些配置参数比较多的对象创建,可以将具体对象的创建在工厂内完成,我们只需记住与之对应的代号或者参数即可;比如家庭作坊养殖时,饲料需要各种饲料搭配,这都要自己去干,现在是养殖场而且是机械化的养殖场,喂什么饲料直接从饲料器里出来,直接养出吃某种饲料的鸡或鸭。
- 配置参数的较多的对象在传入参数时,可以采用配置文件的形式,这样就可以在不改变代码的情况下,更换创建的对象;就好比工厂拿到配置文件里的饲料配方,给你提供吃这种饲料的鸡或者鸭。
到目前为止,作为一个将家庭作坊养殖发展到小型机械化养殖场的农民企业家,你似乎感觉还不错,但是你的能力实在是太强了,规模又扩大。但是,你发现在一个养殖场里既养鸡又养鸭,会产生很多不便,鸡和鸭的饲养方式不一样、生活习性不一样。之后又想养鹅,这时候你的养殖场真的是不堪重负啊,隐患严重啊。
总结一下缺点:
- 工厂类中集中所有产品的逻辑,一旦有一环节出现问题,整个系统都有可能出现问题。
- 如果要增加一个新产品,就需要修改现有的工厂逻辑,增加了系统的复杂度。
- 当增加的产品越来越多时,整个系统的逻辑会过于复杂,不利于扩展和维护。
工厂方法模式(FactoryMethodPattern)
在上一节中,农民企业家的你遇到了发展的难题-怎么在扩大规模的同时解决效率问题。
这时候你灵光一闪,你想到了可以将鸡和鸭分开养殖,就是建立两个养殖场,如果以后还要再养殖其他种类的家禽,直接再建个养殖场,这样就避免了整个系统的不稳定以及难扩展和维护。
现在你决定在原来养鸡和鸭的基础上,再养鹅。
Goose.java
public class Goose extends Fowl {
@Override
public void yell() {
System.out.println("我是一只鹅");
}
}
但是现在你不能再像之前那样讲所有的家禽都饲养在一个养殖厂里,你需根据不同的种类建立响应的养殖场。
首先建立个养殖场的模式,即一个养殖场的接口或者抽象类。
Factory.java
public interface Factory {
Fowl createFowl();
}
然后根据养殖的种类建立响应的工厂。
ChickenFactory.java
public class ChickenFactory implements Factory {
@Override
public Fowl createFowl() {
return new Chicken();
}
}
DuckFactory.java
public class DuckFactory implements Factory {
@Override
public Fowl createFowl() {
return new Duck();
}
}
GooseFactory.java
public class GooseFactory implements Factory {
@Override
public Fowl createFowl() {
return new Goose();
}
}
现在各个工厂已经建立了,你的客户想要什么家禽,直接去相应的养殖场去拿。
Client.java
public class Client {
public static void main(String[] args) {
Factory chickenFactory = new ChickenFactory();
Chicken chicken = (Chicken) chickenFactory.createFowl();
chicken.yell();
Factory duckFactory = new DuckFactory();
Duck duck = (Duck) duckFactory.createFowl();
duck.yell();
Factory gooseFactory = new GooseFactory();
Goose goose = (Goose) gooseFactory.createFowl();
goose.yell();
}
}
UML图:
整个系统主要包括以下角色:
Fowl
-抽象产品Chicken
、Duck
和Goose
-具体产品Factory
-抽象工厂ChickenFactory.java
、DuckFactory.java
和GooseFactory.java
-具体工厂
优点:
- 对象的创建交给相应的工厂,完全不需要知道创建的过程;想要哪种家禽直接去相应的养殖场去取,没有必要理会家禽是怎么饲养的。
- 每个产品在生产过程中有着不同的创建逻辑,因此可以在各自的工厂内部实现;每种家禽的饲养技术不一样,因此在各自的养殖场中可以根据特点进行改变。
- 以后每增加一个种类,只需增加一个具体产品和相应的具体工厂。
虽然现在已经解决养殖规模扩大带来的问题,但是立志要作为一名成功的企业家,必须要有远见的眼光。这种模式还是有些问题:
- 由于每增加一个种类要增加一个具体产品和相应的具体工厂,虽然这样避免了工厂内部逻辑随着产品增加复杂度增加的问题,但是产品种类多了整个系统的类的数量也增加了,会给系统带来负担。
- 由于考虑到系统的可扩展性,需要引入抽象层,在客户端代码中均使用抽象层进行定义,增加了系统的抽象性和理解难度,且在实现时可能需要用到DOM、反射等技术,增加了系统的实现难度。
抽象工厂模式(AbstractFactoryPattern)
通过工厂方法模式,你解决了之前的问题,但是创业的过程是艰辛的,你有遇到问题了。
你的家禽养殖场越办越好,赚的钱越来越多,你想着要把养殖场的规模在扩大一些,于是你又开办了养猪场和养牛场,而且还想在隔壁镇上复制你的商业模式。
但是你的养殖规模扩大了,可是你发现你的利润降低了,于是你仔细研究了以下你的管理模式,发现两个问题:
- 开办一个新的养殖场需要各种审批手续,耗时太长。
- 你打算养殖一个品种,就开办一个养殖场,虽然这样能够方便每个养殖场的管理,但是所有养殖场管理和之间的配合很麻烦。
其实这就是上节提到的工厂方法模式的弊端。
经过你的深思熟虑,于是你做出了深化改革的决定。首先将目前所有的产品分成两个系列,家禽(Fowl
)和家畜(Livestock
),然后将所有养殖场整合成一个大的养殖场,建立一个养殖场的管理模板(AbstractFactory
)。
Livestock.java
public abstract class Livestock {
public abstract void yell();
}
Pig.java
public class Pig extends Livestock {
@Override
public void yell() {
System.out.println("我是一头猪");
}
}
Cattle.java
public class Cattle extends Livestock {
@Override
public void yell() {
System.out.println("我是一头牛");
}
}
AbstractFactory.java
public interface AbstractFactory {
Fowl createFowl();
Livestock createLivestock();
}
抽象工厂AbstractFactory
设计这样让你感觉到似乎不合理,因为一个大型养殖场里不可能养殖一种家禽和家畜,要怎样设计才能更合理呢,于是你又灵光一闪。
AbstractFactory.java
public interface AbstractFactory {
Fowl createFowl(String name);
Livestock createLivestock(String name);
}
设计成这样,你是不是感觉很熟悉啊,对的这就是简单工厂模式。加入你在每个工厂里面一类产品只生产一种,原来的设计完全可以胜任,但是你要在工厂里面一类产品要生产多种,可以结合原来的简单工厂模式。
MyFactory.java
public class MyFactory implements AbstractFactory {
@Override
public Fowl createFowl(String name) {
Fowl fowl = null;
if (name.equals("chicken")) fowl = new Chicken();
else if (name.equals("duck")) fowl = new Duck();
else if (name.equals("goose")) fowl = new Goose();
return fowl;
}
@Override
public Livestock createLivestock(String name) {
Livestock livestock = null;
if (name.equals("pig")) livestock = new Pig();
else if (name.equals("cattle")) livestock = new Cattle();
return livestock;
}
}
Client.java
public class Client {
public static void main(String[] args) {
AbstractFactory myFactory = new MyFactory();
Chicken chicken = (Chicken) myFactory.createFowl("chicken");
chicken.yell();
Duck duck = (Duck) myFactory.createFowl("duck");
duck.yell();
Cattle cattle = (Cattle) myFactory.createLivestock("cattle");
cattle.yell();
}
}
现在你可以把你的商业模式开到任何一个镇上了。
UML图:
整个系统主要包括以下角色:
Fowl
和Livestock
-抽象产品Chicken
、Duck
、Goose
、Cattle
和Pig
-具体产品AbstractFactory
-抽象工厂MyFactory.java
-具体工厂
目前这种模式确实不算是典型抽象工厂模式,而是通过简单工厂模式扩展的抽象工厂模式。
总结
我们梳理一下这三种工厂模式之间的关系。
简单工厂模式与工厂方法模式:
- 对象的创建都是在具体工厂中进行的,但是工厂方法模式的具体工厂是通过接口实现或者类继承,工厂具有可扩展性;
- 同时工厂方法模式中的一个具体工厂只能创建一个对象,任何一个对象的创建逻辑的更改不会影响到其他对象的创建;
- 如果工厂方法模式在创建对象时结合简单工厂模式,可以创建一类对象的多个对象。
工厂方法模式与抽象工厂模式:
- 在工厂方法模式中每个具体工厂只能创建一个对象,而在抽象工厂模式中每个具体工厂可以创建多种对象;
- 如果抽象工厂模式在创建对象时结合简单工厂模式,可以创建多类多个对象。
三者之间的关系: