今天来讲讲设计模式中的三种工厂模式——简单工厂模式、工厂模式和抽象工厂模式。
简单工厂模式
简单工厂模式并不属于GoF23种模式,简单工厂模式的定义为:简单工厂模式又称为静态工厂方法(Static Factory Method)模式,它属于类创建型模式。在简单工厂模式中,可以根据参数的不同返回不同类的实例,简单工厂模式专门定义了一个类来负责创建其他类的实例,被创建的实例通常都具有共同的父类。
简单工厂模式的结构图
我们通过这张结构图来了解简单工厂模式中的各个角色:
1、Factory(工厂角色)
工厂角色也就是工厂类,它是简单工厂模式的核心,它负责实现创建所有实例的内部逻辑。工厂类可以被外部直接调用,创建所需要的产品对象。工厂角色需要提供一个静态方法factoryMethod()(PS:命名无所谓,但是要有一个静态方法去实现工厂的职责),它返回一个抽象产品类Product,所有的具体产品类都是抽象产品类的子类。
2、Product(抽象产品角色)
抽象产品角色是简单工厂模式所创建的所有对象的父类,负责描述所有实例所共有的公共接口,它的引入能提高系统的灵活性,使得工厂类只需要提供一个工厂方法。抽象产品类可以是一个接口,也可以是一个抽象类。
3、ConcreteProduct(具体产品角色)
具体产品角色是简单工厂模式的创建目标,所有创建的对象都充当这个角色的某个具体类的实例。
简单工厂模式的实例
下面我们通过一个电视机工厂的实例来加深简单工厂模式的理解。
工厂类 TVFactory
public class TVFactory {
public static TV productTV(String brand) throws Exception{
if(brand.equalsIgnoreCase("Haier")){
System.out.println("TVFactory product HaierTV!");
return new HaierTV();
}
else if(brand.equalsIgnoreCase("Samsung")){
System.out.println("TVFactory product SamsungTV!");
return new SamsungTV();
}
else{
throw new Exception("There is no such brand!");
}
}
}
抽象产品类 TV
public interface TV {
public void play();
}
具体产品类HaierTV、SamsungTV
public class HaierTV implements TV{
@Override
public void play() {
System.out.println("HaierTV is playing!");
}
}
public class SamsungTV implements TV{
@Override
public void play() {
System.out.println("SamsungTV is playing!");
}
}
测试类TestMain
public class TestMain {
public static void main(String[] args) throws Exception {
TVFactory factory = new TVFactory();
TV tv1 = factory.productTV("haier");
tv1.play();
TV tv2 = factory.productTV("Samsung");
tv2.play();
TV tv3 = factory.productTV("HuaWei");
tv3.play();
}
}
让我们看看测试结果:
跟我们预期设想的是一样的,证明我们这个简单工厂模式实现成功了。
但是简单工厂模式有一个很大的缺点,当有新产品要加入系统中时,必须修改工厂类,加入必要的处理逻辑,这违背了“开闭原则”。为了解决这个问题,工厂模式就出现了!
工厂方法模式
工厂方法模式的定义为:工厂方法模式又称为工厂模式,也叫虚拟构造器模式或多态工厂模式,它属于类创建型模式。在工厂方法模式中,工厂父类负责定义创建产品对象的公共接口,而工厂子类则负责生成具体的产品对象,这样做的目的是将产品类的实例化延迟到工厂子类中完成,即通过工厂子类来确定究竟应该实例化哪一个具体产品类。
工厂方法模式的结构图
在工厂方法模式中的Product(抽象产品类)和ConcreteProduct(具体产品类)几乎没有改变,因此在这里不多加描述。主要的区别在于Factory(抽象工厂类)和ConcreteFactory(具体工厂类)。
1、Factory(抽象工厂类)
在抽象工厂类中,声明了工厂方法(Factory Method),用于返回一个产品。抽象工厂是工厂方法模式的核心,它与应用程序无关。任何在模式中创建对象的工厂类都必须实现该接口。
2、ConcreteFactory(具体工厂类)
具体工厂是抽象工厂的子类,实现了抽象工厂中定义的工厂方法,并可由客户调用,返回一个具体产品类的实例。在具体工厂类中包含与应用程序密切相关的逻辑,并且接受应用程序调用以创建产品对象。
工厂方法模式的实例
我们依旧通过上边的电视机工厂来讲解工厂方法模式。上边的简单工厂方法模式中只通过一个TVFactory来生产两种品牌的电视机,在工厂模式中,不同品牌的电视机我们通过不同的工厂来实现。
抽象工厂类TVFactory
public interface TVFactory {
public TV productTV();
}
具体工厂类HaierTVFactory、SamsungTVFactory
public class SamsungTVFactory implements TVFactory{
@Override
public TV productTV() {
System.out.println("SamsungTVFactory is producting");
return new SamsungTV();
}
}
public class HaierTVFactory implements TVFactory{
@Override
public TV productTV() {
System.out.println("HaierTVFactory is producting!");
return new HaierTV();
}
}
抽象产品类和具体产品类与简单工厂模式中一样,在此不做重复。
测试类TestMain
public class TestMain {
public static void main(String[] args) {
TVFactory factory = new HaierTVFactory();
TV tv = factory.productTV();
tv.play();
factory = new SamsungTVFactory();
tv = factory.productTV();
tv.play();
}
}
让我们看看测试结果:
与预期结果一致,工厂方法模式创建成功。
工厂模式的关键就是基于工厂角色和产品角色的多态性设计,它能够使工厂可以自主确定创建何种产品,而这个对象的细节则完全封装在具体工厂内部。同时也解决了简单工厂模式违背“开闭原则”的问题,当系统加入新产品时,无须修改抽象工厂和抽象产品提供的接口,只需要添加一个具体工厂和一个具体产品就能够实现,这样使得系统的可扩展性变得非常好。
但是工厂模式在添加新产品时需要编写新的具体产品类和具体工厂类,系统中类的个数将成对增加,当产品数量增加时,会大大增加系统的复杂度,有更多的类需要编译和运行,会给系统带来一些额外的开销。由于考虑到系统的可扩展性,需要引入抽象层,在客户端代码中均使用抽象层进行定义,增加了系统的抽象性和理解难度。
在以下几种情况我们可以使用工厂方法模式:
- 一个类不知道它所需要的对象的类。
- 一个类通过其子类来指定创建哪个对象。
- 将创建对象的任务委托给多个工厂子类中的某一个,客户端在使用时可以无须关心是哪一个工厂子类创建产品子类,需要时再动态指定,可将具体工厂类的类名存储在配置文件或者数据库中。
抽象工厂模式
抽象工厂模式是工厂方法模式的泛化版,工厂方法模式是一种特殊的抽象工厂模式。在工厂方法模式中,每一个具体工厂只能够产生一种具体产品,而在抽象工厂模式中,每一个具体工厂可以生产多个具体产品。
抽象工厂模式的定义为:提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们具体的类。抽象工厂模式又称为Kit模式,属于对象创建型模式。
抽象工厂模式结构图
抽象工厂模式中的角色跟工厂模式中的角色几乎一样,主要区别在于工厂方法的实现,因此我们直接通过实例来了解抽象工厂模式。
抽象工厂模式的实例
我们这次将上边的电视机工厂升级为电器工厂。
抽象工厂类 EFactory
public interface EFactory {
public Television productTelevision();
public AirConditioner productAirConditioner();
}
具体工厂类 HaierFactory、SamsungFactory
public class HaierFactory implements EFactory{
@Override
public Television productTelevision() {
System.out.println("HaierTelevision is producting!");
return new HaierTelevision();
}
@Override
public AirConditioner productAirConditioner() {
System.out.println("HaierAirConditioner is producting!");
return new HaierAirConditioner();
}
}
public class SamsungFactory implements EFactory{
@Override
public Television productTelevision() {
System.out.println("SamsungTelevision is producting!");
return new SamsungTelevision();
}
@Override
public AirConditioner productAirConditioner() {
System.out.println("SamsungAirConditioner is producting!");
return new SamsungAirConditioner();
}
}
抽象产品类Television、AirConditioner
public interface AirConditioner {
public void changeTemperature();
}
public interface Television {
public void play();
}
具体产品类(单举Haier的产品):
public class HaierTelevision implements Television{
@Override
public void play() {
System.out.println("HaierTelevision is playing!");
}
}
public class HaierAirConditioner implements AirConditioner{
@Override
public void changeTemperature() {
System.out.println("HaierAirConditioner is working!");
}
}
测试类TestMain
public class TestMain {
public static void main(String[] args) {
EFactory factory;
Television tv;
AirConditioner ac;
factory = new HaierFactory();
tv = factory.productTelevision();
tv.play();
factory = new SamsungFactory();
ac = factory.productAirConditioner();
ac.changeTemperature();
}
}
跑一遍!
抽象工厂模式在工厂方法模式的基础上增加了产品族的概念,我们可以通过工厂生产多种产品。但在增加或者替换产品族时,这个优点又会变成缺点。当我们增加或者替换产品族时需要修改抽象工厂和所有具体工厂类。
抽象工厂模式适用于以下环境:
- 一个系统不依赖于产品类实例如何被创建、组合和表达的细节。
- 系统中有多于一个的产品族,而每次只使用其中某一产品族。
- 属于同一产品族的产品将在一起使用,同一产品族中的产品可以是没有任何关系的对象,但是它们都具有一些共同的约束。
- 系统提供一个产品类的库,所有的产品以同样的接口出现,从而使客户端不依赖于具体实现。
参考:《设计模式》清华大学出版社