序言
简单工厂模式虽然简单,但是存在一个问题。 因为简单工厂模式是调用工厂类中的工厂方法,然后传入不同的参数来创建不同的产品,当引入一个新产品时,就需要修改工厂类,不符合“开闭原则”。 工厂模式不再提供一个工厂类来创建所有的产品对象,而是根据不同的产品来提供不同的工厂类。
工厂模式
定义 :定义一个用于创建对象的接口,让子类决定哪一个类实例化,创建具体的产品对象。工厂方法模式让一个类的实例化延迟到其子类。工厂方法模式又简称为工厂模式,又可称作虚拟构造器模式或多态工厂模式。工厂模式是一种类创建型模式
在工厂模式结构图有如下几个角色:
- Product(抽象产品) : 定义产品的接口,是工厂方法模式所创建对象的超类型,也就是产品对象的公共父类。
- ConcreteProduct(具体产品) : 它实现了抽象产品接口,某种类型的产品由专门的具体工厂创建,具体工厂和具体产品有一一对应关系。
- Factory(抽象工厂): 在抽象工厂类中,声明了工厂方法用于返回一个产品,抽象工厂是工厂模式的核心,所有对象的工厂类都必须实现该接口。
- ConcreteFactory(具体工厂): 它是抽象工厂的子类,实现了抽象工厂中的方法,并由客户端调用返回一个具体的产品。
与简单工厂模式相比,我们可以看到工厂模式多了一个抽象工厂角色。 抽象工厂可以是接口,也可以是抽象类或是类。
代码实现:
Product (抽象产品)
/**
* @program
* @Desc 抽象产品
* @Author 游戏人日常
* @CreateTime 2019/07/14--13:38
*/
public interface Product {
}
ConcreteProduct(具体产品)
/**
* @program
* @Desc 具体产品A
* @Author 游戏人日常
* @CreateTime 2019/07/14--13:39
*/
public class ConcreteProductA implements Product{
}
/**
* @program
* @Desc 具体产品B
* @Author 游戏人日常
* @CreateTime 2019/07/14--13:40
*/
public class ConcreteProductB implements Product {
}
Factory(抽象工厂)
/**
* @program
* @Desc 抽象工厂
* @Author 游戏人日常
* @CreateTime 2019/07/14--13:42
*/
public interface Factory {
public Product factoryMrthod();
}
ConcreteFactory(具体工厂)
/**
* @program
* @Desc 具体工厂 A
* @Author 游戏人日常
* @CreateTime 2019/07/14--13:46
*/
public class ConcreteFactoryA implements Factory {
@Override
public Product factoryMrthod() {
return new ConcreteProductA();
}
}
/**
* @program
* @Desc 具体工厂 B
* @Author 游戏人日常
* @CreateTime 2019/07/14--13:48
*/
public class ConcreteFactoryB implements Factory {
@Override
public Product factoryMrthod() {
return new ConcreteProductB();
}
}
客户端测试
/**
* @program
* @Desc 客户端测试
* @Author 游戏人日常
* @CreateTime 2019/07/14--13:51
*/
public class Test {
public static void main(String [] args){
//获取产品 A 只需获取 工厂 A
Factory concreteFactoryA=new ConcreteFactoryA();
Product productA= concreteFactoryA.factoryMrthod();
//获取产品 B 只需获取 工厂 B
Factory concreteFactoryB=new ConcreteFactoryB();
Product productB= concreteFactoryB.factoryMrthod();
}
}
我们可以看到客户端想要获取哪个具体厂品时,只需要跟据不同工厂来获取。 不像简单工厂模式中的一样,通过参数不同来获取。
重载的工厂方法
在UML类图可以看到,在工厂类中重载了工厂方法, 以不同的方法来创建。
代码实现:
Factory(抽象工厂)
/**
* @program
* @Desc 抽象工厂
* @Author 游戏人日常
* @CreateTime 2019/07/14--13:42
*/
public interface Factory {
public Product factoryMrthod();
public Product factoryMrthod(String args);
public Product factoryMrthod(int args);
}
ConcreteFactory (具体的工厂)
/**
* @program
* @Desc 具体工厂 A
* @Author 游戏人日常
* @CreateTime 2019/07/14--13:46
*/
public class ConcreteFactoryA implements Factory {
@Override
public Product factoryMrthod() {
Product productA = new ConcreteProductA();
//一般为默认方式创建产品
return productA;
}
@Override
public Product factoryMrthod(String args) {
Product productA = new ConcreteProductA();
// 有时创建产品时 产品对象需要String 型参数
return productA;
}
@Override
public Product factoryMrthod(int args) {
Product productA = new ConcreteProductA();
// 有时创建产品时 产品对象需要int 型参数
return productA;
}
}
//具体工厂 B ,也是如此 代码就不写了。
客户端测试:
/**
* @program
* @Desc 客户端测试
* @Author 游戏人日常
* @CreateTime 2019/07/14--13:51
*/
public class Test {
public static void main(String [] args){
//获取产品 A 只需获取 工厂 A
Factory concreteFactoryA=new ConcreteFactoryA();
Product productA1= concreteFactoryA.factoryMrthod("A");
Product productA2= concreteFactoryA.factoryMrthod(0);
Product productA3= concreteFactoryA.factoryMrthod();
}
}
在抽象工厂方法中定义了几个重载的方法,在具体的工厂类中实现了这些工厂方法,这些方法可以包含不同的业务来满足不同产品对象的需求。
工厂方法的隐藏
有时候为了简化客户端的使用,我们可以隐藏工厂方法,直接在工厂类中调用产品类中的业务方法。客户端无需调用工厂方法来创建产品,然后调用产品业务方法。
代码如下:
Product(抽象产品)
/**
* @program
* @Desc 抽象产品
* @Author 游戏人日常
* @CreateTime 2019/07/14--13:38
*/
public interface Product {
public void productMethod();
}
ConcreteProduct (具体产品)
/**
* @program
* @Desc
* @Author 游戏人日常
* @CreateTime 2019/07/14--13:39
*/
public class ConcreteProductA implements Product{
@Override
public void productMethod() {
System.out.println("A 产品业务方法!!!");
}
}
Factory(抽象工厂)
/**
* @program
* @Desc 抽象工厂 改为抽象类
* @Author 游戏人日常
* @CreateTime 2019/07/14--13:42
*/
public abstract class Factory {
public abstract Product factoryMrthod();
//产品业务方法
public void productMethod(){
Product product=this.factoryMrthod();
product.productMethod();
}
}
ConcreteFactory (具体工厂)
/**
* @program
* @Desc 具体工厂 A
* @Author 游戏人日常
* @CreateTime 2019/07/14--13:46
*/
public class ConcreteFactoryA extends Factory {
Product productA;
@Override
public Product factoryMrthod() {
return new ConcreteProductA();;
}
}
客户端测试:
**
* @program
* @Desc 客户端测试
* @Author 游戏人日常
* @CreateTime 2019/07/14--13:51
*/
public class Test {
public static void main(String [] args){
ConcreteFactoryA factoryA= new ConcreteFactoryA();
factoryA.productMethod(); //输出结果 : A 产品业务方法!!!
}
}
把产品的业务方法移到工厂类中,可以直接使用工厂对象来调用产品的业务方法。无需使用工厂方法来获取产品,然后调用业务方法。
总结
工厂模式是简单工厂模式的延伸,它继承了简单工厂模式的优点,同时还解决了简单工厂模式中的一些不足。
上面出现两种工厂模式的变形: 1 : 重载工厂方法 2: 隐藏工厂方法
- 重载工厂方法: 指的是重载工厂类中的工厂方法, 满足产品的业务需求。
- 隐藏工厂方法 : 指得是把产品的业务方法移动到工厂类中, 只是想使用产品中的业务方法, 客户端不需创建产品对象。使用工厂对象来调用产品业务方法。
工厂模式优缺点:
优点
- 客户端只需要关心所需产品的对应工厂,无需关心产品创建的细节。
- 加入新的产品时,无需修改抽象产品和抽象工厂,也不需要修改具体的产品和具体工厂,只需要添加一个具体工厂和具体产品就行了,这样就符合”开闭原则“。
缺点
- 添加新产品时,需要加入新的具体产品类和相对应的具体工厂类,导致系统中类的数量增加,在一定程度上增加了系统的复杂性。
- 由于考虑系统的可扩展性,需引入抽象层,在客户端代码中均使用抽象层进行定义,增加了系统的抽象性和理解难度。
适用场景
- 客户端不知道它所需要的对象的类,在工厂模式中,只需要知道所对应的工厂即可。
- 抽象工厂类(Factory)通过其子类来指定创建哪个对象,在工厂模式中,抽象工厂类只需提供一个创建产品的接口,由它子类来确定具体所需的对象,利用面向对象的多态性和里氏代换原则,在程序运行时,子类对象会覆盖父类的对象,从而使得系统容易扩展。