工厂模式定义
工厂模式的客观定义:定义一个创建产品对象的工厂接口,将产品对象的实际创建工作推迟到具体子工厂类当中。
这满足创建型模式中所要求的“创建与使用相分离”的特点。简单来说就是实现了创建者和调用者的分离,其核心本质是实例化对象,用工厂方法代替new操作。
实际上,任何可以产生对象的类或者方法都可以称为工厂。
工厂模式分为三类:
-
简单工厂模式
-
工厂方法模式
-
抽象工厂模式
实际上简单工厂并不在GoF的23种设计模式之中,但很多资料和教学中都习惯把简单工厂作为工厂的一种入门设计模式来讲解。
简单工厂(静态工厂)
- 简单工厂(SimpleFactory):负责实现创建所有实例的内部逻辑。工厂类的创建产品类的方法可以被外界直接调用,创建所需的产品对象。(静态工厂就是单例模式的简单工厂。)
理论知识比较难理解,我直接进行代码讲解。我先设计一个场景,比如我打算出行使用交通工具:
- 首先创建一个产品类,比如汽车,他有
go()
方法:
public class Car {
public void go() {
System.out.println("Car go");
}
}
- 创建主程序来运行我们的汽车类:
public class Main {
public static void main(String[] args) {
Car car = new Car();
car.go();
}
}
运行结果:
- 那么如果我想添加产品类,比如飞机、自行车,同样他们也有自己的
go()
方法:
public class Plane {
public void go() {
System.out.println("Plane go");
}
}
public class Bike {
public void go() {
System.out.println("Bike go");
}
}
在我的主方法中加入对象:
public class Main {
public static void main(String[] args) {
Car car = new Car();
car.go();
// Plane plane = new Plane();
// plane.go();
// Bike bike = new Bike();
// bike.go();
}
}
因为我只能同时使用一种产品,如果我想换产品来使用,就必须在主方法中调用不同的产品对象。这样就得不停的根据需求来修改主程序代码。那怎么解决这个问题呢,下面就开始用到工厂的思想:
- 创建一个接口
Action
,他有交通工具的行为go()
方法:
public interface Action {
void go();
}
并让所有的交通工具都实现这个接口,如:
public class Car implements Action{
public void go() {System.out.println("Car go");}
}
- 那我们的主程序就可以改为只需要用
Action
来调用使用到的产品类的go()
方法就好:
public class Main {
public static void main(String[] args) {
Action a = new Car();
// Action a = new Plane();
// Action a = new Bike();
a.go();
}
}
通过这种方式可以把修改产品对象写进配置文件中,这段主程序就无需再变动。但对象仍是被new出来的,若是想添加各种业务功能此方法仍不方便。那么我们来创建一个工厂来负责这些产品生产:
public class SimpleFactory {
public Car createCar(){
return new Car();
}
public Plane createPlane(){
return new Plane();
}
public Bike createBike() {
return new Bike();
}
}
然后我们的主程序就可以改写为:
public class Main {
public static void main(String[] args) {
Action a = new SimpleFactory()
.createCar();
// .createPlane()
// .createBike()
a.go();
}
}
这样我们只需要改写SimpleFactory
工厂对象的creat*()
方法就可以改变使用到的产品了。这就是一个简单工厂。
通过以上代码我们不难看出,简单工厂内每个产品类型的方法体内容必须得写死,同时若是想新添加一种产品就必须得再写一个方法。这就是简单工厂的缺点: 可扩展性低
工厂方法模式(Factory Method)
- 工厂方法(FactoryMethod)模式是类的创建模式,其用意是定义一个创建产品对象的工厂接口,将实际创建工作推迟到子类中。其实质是“定义一个创建对象的接口,但让实现这个接口的类来决定实例化哪个类。工厂方法让类的实例化推迟到子类中进行。”
工厂方法模式是简单工厂模式的进一步抽象和推广。由于使用了多态性,工厂方法模式保持了简单工厂模式的优点,而且克服了它的缺点。
通过理论知识我我们可以知道,工厂方法模式解决了简单工厂的缺点。于是我还是使用上面的情景,通过代码修改进行讲解:
- 创建汽车工厂
CarFactory
,它拥有creat()
生产方法,并返回汽车类:
public class CarFactory {
public Car creat() {
System.out.println("creat a car!");
return new Car();
}
}
- 主程序改写:
public class Main {
public static void main(String[] args) {
Action a = new CarFactory().creat();
a.go();
}
}
运行结果:
同理别的交通工具产品类都可以添加自己的生产工厂类,这样就可以随意的扩展交通工具产品类了,并且可以任意定制生产过程。
抽象工厂模式(Abstract Factory)
- 抽象工厂(Abstract Factory)为一个产品家族提供了统一的创建接口。当需要这个产品家族的某一系列的时候,可以从抽象工厂中选出相对系的系列来创建一个具体的工厂类别。
还是通过代码来讲解:
- 首先我们重新想一种场景,我出行需要用到汽车
Car
:
public class Car {
public void go() {
System.out.println("Car go");
}
}
但我也要同时使用其他类型的产品比如Phone
public class Phone {
public void used() {
System.out.println("phone call");
}
}
并且我是打算去公司Company
工作的:
public class Company {
public void todo() {
System.out.println("to work");
}
}
- 下班后我想骑自行车
Bike
并带着一本书Book
去公园Park
去看书放松:
public class Bike {
public void go() {
System.out.println("Bike go");
}
}
public class Book {
public void used() {
System.out.println("read book");
}
}
public class Park {
public void todo() {
System.out.println("to relax");
}
}
- 同理我还想假期坐飞机
Plane
去山西Shanxi
旅游,飞机上不能用手机我们就只能用MP3Mp3
听音乐:
public class Plane {
public void go() {
System.out.println("Plane go");
}
}
public class Mp3 {
public void used() {
System.out.println("MP3 listen");
}
}
public class Shanxi {
public void todo() {
System.out.println("to travel");
}
}
通过上述情景可以根据我出行的情况来把产品归为3个产品族:
交通工具 | 携带产品 | 目的地 |
---|---|---|
Car | Phone | Company |
Bike | Book | Park |
Plane | Mp3 | Shanxi |
我要根据一族产品来写主程序如:
public class Main {
public static void main(String[] args) {
Car car = new Car();
car.go();
Phone phone = new Phone();
phone.used();
Company company = new Company();
company.todo();
}
}
其运行结果如下:
若是想要更换一个产品族就得把主方法中的代码全部改掉,接下来使用抽象工厂来使产品族可以变得灵活扩展:
- 首先将产品分成三类:出行方式
ToGo
、携带产品ToWith
、目的地ToWhere
并分别写出其抽象类和抽象方法如:
public abstract class ToGo {
abstract void go();
}
并让这三个类别的产品分别继承其抽象类,如:
public class Car extends ToGo {
public void go() {
System.out.println("Car go");
}
}
- 创建一个抽象类工厂
AbstractFactory
,使其拥有产品一族的抽象生产方法:
public abstract class AbstractFactory {
abstract ToGo creatToGo();
abstract ToWith creatToWith();
abstract ToWhere creatToWhere();
}
- 分别根据这三个产品族创建使用场景:
Workday
、AfterWork
、Holiday
,继承AbstractFactory
抽象工厂并重写其方法,根据场景来分别选择产品的返回值如:
public class Workday extends AbstractFactory{
ToGo creatToGo() {
return new Car();
}
ToWith creatToWith() {
return new Phone();
}
ToWhere creatToWhere() {
return new Company();
}
}
- 编写主程序:
public class Main {
public static void main(String[] args) {
AbstractFactory factory = new Workday();
// 以下代码无需修改
ToGo toGo = factory.creatToGo();
toGo.go();
ToWith toWith = factory.creatToWith();
toWith.used();
ToWhere toWhere = factory.creatToWhere();
toWhere.todo();
}
}
运行结果:
若是想更换产品族只需要更换抽象工厂所使用的场景就可以,主程序中的大量代码无需修改,产品族这样就完成可以随意扩展。
这里的代码举例使用的是abstract,同样可以使用interface。对于开发严谨来说实物类名词用abstract,修饰类形容词使用interface。
三个工厂的区别及优缺点
- 简单工厂:简单易理解,将生产工厂内产品方法写死不易扩展;
- 工厂方法:每个产品对应一个自己的工厂,可以无限扩展产品,但主程序不易更换产品族;
- 抽象工厂:一次生产一族产品,更换或扩展产品族方便,但不易扩展单一产品。
通过以上对比可以看出每个工厂方法都各有优缺点,那想要实现一个完美工厂就需要用到SpringIOC——bean工厂。