JAVA设计模式之工厂模式
概念:
在面向对象编程中, 最通常的方法是一个new操作符产生一个对象实例,new操作符就是用来构造对象实例的。但是在一些情况下, new操作符直接生成对象会带来一些问题。举例来说, 许多类型对象的创造需要一系列的步骤: 你可能需要计算或取得对象的初始设置; 选择生成哪个子对象实例; 或在生成你需要的对象之前必须先生成一些辅助功能的对象。 在这些情况,新对象的建立就是一个 “过程”,不仅是一个操作,像一部大机器中的一个齿轮传动。
分类:
简单工厂模式:客户有一个工厂来帮他创建汽车,想要什么车,这个工厂就可以建。比如想要宝马。工厂就创建宝马,要奥迪就创建奥迪,即工厂可以创建产品。不满足开闭原则,当添加新车时,需要修改原类用代码。
工厂方法模式时代:对简单工厂模式的改进,符合开闭原则。通过增加不同工厂类来创建产品:单独分出来多个具体的工厂。每个具体工厂创建一种车。即具体工厂类只能创建一个具体产品。但是汽车工厂还是个抽象,具体汽车工厂继承它。你需要什么车,需要指定某个具体的工厂才能生产车出来。
抽象工厂模式时代:其创建的是一个产品族,例如高端车和低端车,两个产品族。高端车有高端的发动机,轮胎,内饰等组成,需要不同的去生产。但是用户可以直观的直接拿到一辆高端车,不用关心具体的发动机,轮胎等创建。
这就是工厂模式。
分类
工厂模式主要是为创建对象提供过渡接口,以便将创建对象的具体过程屏蔽隔离起来,达到提高灵活性的目的。
工厂模式可以分为三类:
- 简单工厂模式(Simple Factory)
- 工厂方法模式(Factory Method)
- 抽象工厂模式(Abstract Factory)
这三种模式从上到下逐步抽象,并且更具一般性。
GOF在《设计模式》一书中将工厂模式分为两类:工厂方法模式(Factory Method)与抽象工厂模式(Abstract Factory)。
将简单工厂模式(Simple Factory)看为工厂方法模式的一种特例,两者归为一类。
区别
工厂方法模式:
一个抽象产品类,可以派生出多个具体产品类。
一个抽象工厂类,可以派生出多个具体工厂类。
每个具体工厂类只能创建一个具体产品类的实例。
抽象工厂模式:
多个抽象产品类,每个抽象产品类可以派生出多个具体产品类。
一个抽象工厂类,可以派生出多个具体工厂类。
每个具体工厂类可以创建多个具体产品类的实例。
区别:
工厂方法模式只有一个抽象产品类,而抽象工厂模式有多个。
工厂方法模式的具体工厂类只能创建一个具体产品类的实例,而抽象工厂模式可以创建多个。
两者皆可。
简单工厂模式:
建立一个工厂(一个函数或一个类方法)来制造新的对象。
简单工厂模式又称静态工厂方法模式。重命名上就可以看出这个模式一定很简单。它存在的目的很简单:定义一个用于创建对象的接口。
工厂类:
public class SimpleFactory {
// 第一种方式
public static Car createCar(String type) {
if ("audi".equals(type)) {
return new Audi();
} else if ("bengchi".equals(type)) {
return new Bengchi();
} else {
return null;
}
}
// 第二种方式
public static Car createAudi() {
return new Audi();
}
public static Car createBengchi() {
return new Bengchi();
}
}
产品类:
public interface Car {
public void run();
}
public class Audi implements Car {
@Override
public void run() {
System.out.println("audi running");
}
}
public class Bengchi implements Car {
@Override
public void run() {
System.out.println("bengchi running");
}
}
客户类:
public class Main {
public static void main(String[] args) {
Bengchi bengchi = (Bengchi) SimpleFactory.createCar("bengchi");
Audi audi = (Audi) SimpleFactory.createAudi();
audi.run();
bengchi.run();
}
}
从开闭原则(对扩展开放;对修改封闭)上来分析下简单工厂模式。当客户不再满足现有的车型号的时候,想要一种速度快的新型车,只要这种车符合抽象产品制定的合同,那么只要通知工厂类知道就可以被客户使用了。所以对产品部分来说,它是符合开闭原则的;但是工厂部分好像不太理想,因为每增加一种新型车,都要在工厂类中增加相应的创建业务逻辑(createCar方法需要新增case),这显然是违背开闭原则的。
工厂方法模式
工厂方法模式去掉了简单工厂模式中工厂方法的静态属性,使得它可以被子类继承。这样在简单工厂模式里集中在工厂方法上的压力可以由工厂方法模式里不同的工厂子类来分担。
工厂方法模式组成:
- 抽象工厂角色: 这是工厂方法模式的核心,它与应用程序无关。是具体工厂角色必须实现的接口或者必须继承的父类。在java中它由抽象类或者接口来实现。
- 具体工厂角色:它含有和具体业务逻辑有关的代码。由应用程序调用以创建对应的具体产品的对象。
- 抽象产品角色:它是具体产品继承的父类或者是实现的接口。在java中一般有抽象类或者接口来实现。
具体产品角色:具体工厂角色所创建的对象就是此角色的实例。在java中由具体的类来实现。
工厂方法模式使用继承自抽象工厂角色的多个子类来代替简单工厂模式中的“上帝类”。正如上面所说,这样便分担了对象承受的压力;而且这样使得结构变得灵活 起来——当有新的产品产生时,只要按照抽象产品角色、抽象工厂角色提供的合同来生成,那么就可以被客户使用,而不必去修改任何已有 的代码。可以看出工厂角色的结构也是符合开闭原则的!
工厂类:
public interface CarFactory {
Car createCar();
}
public class AudiFactory implements CarFactory {
@Override
public Car createCar() {
return new Audi();
}
}
public class BengchiFactory implements CarFactory {
@Override
public Car createCar() {
return new Bengchi();
}
}
客户类:
public class Main {
public static void main(String[] args) {
Audi audi = (Audi) new AudiFactory().createCar();
Bengchi bengchi = (Bengchi) new BengchiFactory().createCar();
audi.run();
bengchi.run();
}
}
抽象工厂模式
抽象工厂模式的起源
下面引用一段抽象工厂模式的起源:
抽象工厂模式的起源或者最早的应用,是用于创建分属于不同操作系统的视窗构建。比如:命令按键(Button)与文字框(Text)都是视窗构建,在UNIX操作系统的视窗环境和Windows操作系统的视窗环境中,这两个构建有不同的本地实现,它们的细节有所不同。
在每一个操作系统中,都有一个视窗构建组成的构建家族。在这里就是Button和Text组成的产品族。而每一个视窗构件都构成自己的等级结构,由一个抽象角色给出抽象的功能描述,而由具体子类给出不同操作系统下的具体实现。
可以发现在上面的产品类图中,有两个产品的等级结构,分别是Button等级结构和Text等级结构。同时有两个产品族,也就是UNIX产品族和Windows产品族。UNIX产品族由UNIX Button和UNIX Text产品构成;而Windows产品族由Windows Button和Windows Text产品构成。
系统对产品对象的创建需求由一个工程的等级结构满足,其中有两个具体工程角色,即UnixFactory和WindowsFactory。UnixFactory对象负责创建Unix产品族中的产品,而WindowsFactory对象负责创建Windows产品族中的产品。这就是抽象工厂模式的应用,抽象工厂模式的解决方案如下图:
显然,一个系统只能够在某一个操作系统的视窗环境下运行,而不能同时在不同的操作系统上运行。所以,系统实际上只能消费属于同一个产品族的产品。
在现代的应用中,抽象工厂模式的使用范围已经大大扩大了,不再要求系统只能消费某一个产品族了。
代码:
工厂类:
public interface CarFactory {
Engine createEngine();
Seat createSeat();
Tyre createTyre();
}
public class LowCarFactory implements CarFactory{
@Override
public Engine createEngine() {
return new LowEngine();
}
@Override
public Seat createSeat() {
return new LowSeat();
}
@Override
public Tyre createTyre() {
return new LowTyre();
}
}
public class LuxuryCarFactory implements CarFactory{
@Override
public Engine createEngine() {
return new LuxuryEngine();
}
@Override
public Seat createSeat() {
return new LuxurySeat();
}
@Override
public Tyre createTyre() {
return new LuxuryTyre();
}
}
产品类:
public interface Engine {
void run();
void start();
}
class LuxuryEngine implements Engine {
@Override
public void run() {
System.out.println("LuxuryEngine");
}
@Override
public void start() {
}
}
class LowEngine implements Engine {
@Override
public void run() {
System.out.println("LowEngine");
}
@Override
public void start() {
}
}
public interface Tyre {
void revolve();
}
class LuxuryTyre implements Tyre {
@Override
public void revolve() {
System.out.println("LuxuryTyre");
}
}
class LowTyre implements Tyre {
@Override
public void revolve() {
System.out.println("LowTyre");
}
}
public interface Seat {
void message();
}
class LuxurySeat implements Seat {
@Override
public void message() {
System.out.println("LuxurySeat");
}
}
class LowSeat implements Seat {
@Override
public void message() {
System.out.println("LowSeat");
}
}
客户类:
public class Main {
public static void main(String[] args) {
LowCarFactory lowCarFactory = new LowCarFactory();
LuxuryCarFactory luxuryCarFactory = new LuxuryCarFactory();
}
}
总结:
无论是简单工厂模式,工厂方法模式,还是抽象工厂模式,他们都属于工厂模式,在形式和特点上也是极为相似的,他们的最终目的都是为了解耦。在使用时,我们不必去在意这个模式到底工厂方法模式还是抽象工厂模式,因为他们之间的演变常常是令人琢磨不透的。经常你会发现,明明使用的工厂方法模式,当新需求来临,稍加修改,加入了一个新方法后,由于类中的产品构成了不同等级结构中的产品族,它就变成抽象工厂模式了;而对于抽象工厂模式,当减少一个方法使的提供的产品不再构成产品族之后,它就演变成了工厂方法模式。
所以,在使用工厂模式时,只需要关心降低耦合度的目的是否达到了。