设计模式—工厂模式
工厂就是用来创建产品的,而从产品和产品族可以分为工厂方法和抽象工厂两种模式, 该模式用于封装和管理对象的创建,是一种创建型模式 。
一、简单工厂模式(Simple Factory)
简单工厂定义:定义了一个创建对象的类,由这个类来封装实例化对象行为(代码)
简单工厂模式是属于创建型模式,是工厂模式的一种,简单工厂模式是由一个工厂对象决定创建哪一种产品类的实例,简单工厂模式是工厂模式家族中最简单实用的模式
使用汽车生产来举例
Car接口:定义汽车标准(Abstract Product)
public interface Car {
void move();
}
QQCar类:制作QQ汽车(Product1)
public class QQCar implements Car {
public QQCar(){
System.out.println("制造QQ汽车");
}
@Override
public void move() {
System.out.println("我是QQ汽车,我可以上路了...");
}
}
AudiCar类:制作奥迪汽车(Product2)
public class AudiCar implements Car {
public AudiCar(){
System.out.println("制作奥迪汽车");
}
@Override
public void move() {
System.out.println("我是奥迪汽车,我可以上路了....");
}
}
CarFactory类:汽车生产代工厂
public class CarFactory {
public Car getCar(String carType){
if("QQ".equals(carType)){
return new QQCar();
}else if ("Audi".equals(carType)){
return new AudiCar();
}
return null;
}
}
User类:表示用户需要买车(Client)
public class User {
public static void main(String[] args) {
CarFactory factory = new CarFactory();
Car qq = factory.getCar("QQ");
qq.move();
Car audi = factory.getCar("Audi");
audi.move();
}
}
到这里,一个简单工厂模式的案例完成了。
1.1 静态工厂模式
CarFactory类
public class CarFactory {
public static Car getCar(String carType){ //就是在这里加上static,通过类名直接调用
if("QQ".equals(carType)){
return new QQCar();
}else if ("Audi".equals(carType)){
return new AudiCar();
}
return null;
}
}
User类
public class User {
public static void main(String[] args) {
CarFactory
Car qq = CarFactory.getCar("QQ"); //通过类名直接获取实例对象
qq.move();
Car audi = CarFactory.getCar("Audi"); //通过类名直接获取实例对象
audi.move();
}
}
通过上面的代码,让我们对简单工厂模式有了一个初步的认识,此时会不会有一个疑问:如果想要生产更多品牌的汽车,代码该怎么修改呢?
修改思路
- 首先创建想要的汽车类(比如宝马,奔驰,兰博基尼等等)
- 修改工厂类,增加对应获取某一个品牌汽车的代码
public class CarFactory {
public static Car getCar(String carType){
if("QQ".equals(carType)){
return new QQCar();
}else if ("Audi".equals(carType)){
return new AudiCar();
}else if("Benz".equals(carType)){ //需要在这里新增判断条件
return new Benz();
}
return null;
}
}
很容易就可以看出,这样修改代码的问题
- 由于工厂类集中了所有产品创建逻辑,一旦不能正常工作,整个系统都要受到影响。
- **系统扩展困难,一旦添加新产品就不得不修改工厂逻辑,在产品类型较多时,有可能造成工厂逻辑过于复杂,不利于系统的扩展和维护。**所以工厂方法出现了。
二、工厂方法(Factory Method)
简单工厂模式中工厂负责生产所有产品,而工厂方法模式则是每一个产品对应一个工厂
大体实现思路如下
- 定义一个抽象工厂,定义一个产品生产接口,但是不负责具体生产
- 针对不同产品定义不同的产品工厂,每个产品工厂都继承自抽象工厂,并且具体实现产品生产方法
下面还是以汽车生产为例
其中,关于具体每个汽车的定义不变,即产品内容不变
IFactory接口:定义生产每个品牌汽车的工厂的规范(Abstract Factory)
public interface AbstractFactory {
Car getCar();
}
QQFactory类:生产QQ汽车的工厂(Concrete Factory1)
public class QQFactory implements AbstractFactory{
@Override
public Car getCar() {
return new QQCar();
}
}
AudiFactory类:生产奥迪汽车的工厂(Concrete Factory2)
public class AudiFactory implements AbstractFactory {
@Override
public Car getCar() {
return new AudiCar();
}
}
User类:用户购买汽车(Client)
public class User {
public static void main(String[] args) {
//通过QQ代工厂生产QQ汽车
AbstractFactory qqFactory = new QQFactory();
Car qq = qqFactory.getCar();
qq.move();
//通过奥迪代工厂生产奥迪汽车
AbstractFactory audiFactory = new AudiFactory();
Car audi = audiFactory.getCar();
audi.move();
}
}
完成了工厂方法的代码实现后,我们回头再看一下在简单工厂模式中存在的问题:代工厂中新增或减少汽车品牌,会不会对已有的代码造成影响。
假如现在工厂规模扩展,需要增加奔驰汽车的代工,代码如下
//只需要新建一个奔驰代工厂就可以,不会对之前的代工厂造成影响
public class BenzFactory implements AbstractFactory {
@Override
public Car getCar() {
return new Benz();
}
}
而User类,只需要新增获取奔驰车的代码即可,也不会对之前的代码造成影响
public class User {
public static void main(String[] args) {
//通过QQ代工厂生产QQ汽车
AbstractFactory qqFactory = new QQFactory();
Car qq = qqFactory.getCar();
qq.move();
//通过奥迪代工厂生产奥迪汽车
AbstractFactory audiFactory = new AudiFactory();
Car audi = audiFactory.getCar();
audi.move();
//新增奔驰汽车,不用修改之前的代码
AbstractFactory benzFactory = new BenzFactory();
Car benz = benzFactory.getCar();
benz.move();
}
}
到这里,是不是对工厂模式有了更进一步的认识,但是有没有发现,上面两种模式不管工厂怎么拆分抽象,都只是针对一类产品Car(AbstractProduct),如果想要生产另一种产品,应该怎么表示呢?
可能最容易想到的办法是:把工厂方法模式完全复制一份,但是这次生产的是Truck。所以也就意味着我们要完全复制和修改Car生产管理的所有代码,显然这是一个笨办法,并不利于扩展和维护。 所以抽象工厂该出现了。
三、抽象工厂(Abstract Factory)
抽象工厂模式通过在AbstarctFactory中增加创建产品的接口,并在具体子工厂中实现新加产品的创建,当然前提是子工厂支持生产该产品。否则继承的这个接口可以什么也不干。
在说抽象工厂之前,需要说一个概念:产品和产品族
3.1 产品和产品族
产品等级结构:是一个上下之间的继承关系,比如一个抽象类或者接口Car,它有多个子类,如奔驰汽车、宝马汽车、大众汽车等等。所以抽象类或者接口与具体实现的子类之间构成了一个产品等级结构。
产品族:抽象工厂模式中,产品族是指由同一个工厂生产的,位于不同产品等级结构中的一组产品,比如大众产品工厂生产的VWCar、VWTruck、VWBicycle,它们三个位于不同的产品等级结构中,VWCar位于Car等级结构中、VWTruck位于Truck等级结构中、VWBicycle位于Bicycler等级结构中,所以VWCar、VWTruck、VWBicycle构成了一个产品族。
不同颜色的长方形、椭圆形和菱形分别构成了一个产品族,而相同颜色的多个长方形、椭圆形和菱形构成了一个产品等级机构,每一个形状对象都位于某个产品族,并属于某个产品等级结构。图中一共有五个产品族,分属于三个不同的产品等级结构。我们只要指明一个产品所处的产品族以及它所属的等级结构,就可以唯一确定这个产品。
当系统所提供的工厂生产的具体产品并不是一个简单的对象,而是多个位于不同产品等级结构、属于不同类型的具体产品时就可以使用抽象工厂模式。
抽象工厂模式与工厂方法模式最大的区别在于,工厂方法模式针对的是一个产品等级结构(同一种交通工具),而抽象工厂模式需要面对多个产品等级结构,一个工厂等级结构可以负责多个不同产品等级结构中的产品对象的创建。当一个工厂等级结构可以创建出分属于不同产品等级结构的一个产品族中的所有对象时(Car、Truck、Bicycle),抽象工厂模式比工厂方法模式更为简单、更有效率。
在了解完产品与产品族之间的关系后,下面来看一组代码案例。
3.2 代码案例
Car接口:定义汽车标准(Product1)
public interface Car {
void move();
}
Truck接口:定义卡车标准(Product2)
public interface Truck {
void move();
}
QQCar和QQTruck则为一个产品族
QQCar类:制作QQ汽车(Product1—QQ)
public class QQCar implements Car {
public QQCar(){
System.out.println("制造QQ汽车");
}
@Override
public void move() {
System.out.println("我是QQ汽车,我可以上路了...");
}
}
QQTruck类:制作QQ卡车(Product2—QQ)
public class QQTruck implements Truck {
public QQTruck(){
System.out.println("制造QQ卡车");
}
@Override
public void move() {
System.out.println("我是QQ卡车,现在可以上路了....");
}
}
AudiCar和AudiTruck则为一个产品族
AudiCar类:制作QQ汽车(Product1—Audi)
public class AudiCar implements Car {
public AudiCar(){
System.out.println("制造Audi汽车");
}
@Override
public void move() {
System.out.println("我是Audi汽车,我可以上路了...");
}
}
AudiTruck类:制作Audi汽车(Product2—Audi)
public class AudiTruck implements Truck {
public AudiTruck(){
System.out.println("制造Audi卡车");
}
@Override
public void move() {
System.out.println("我是Audi卡车,现在可以上路了....");
}
}
AbstractFactory接口:定义工厂的标准(Abstract Factory)
public interface AbstractFactory {
//生产car
Car getCar();
//生产truck
Truck getTruck();
}
QQFactory类:定义生产QQ产品的工厂( ConcreteFactory 1)
public class QQFactory implements AbstractFactory{
@Override
public Car getCar() {
return new QQCar();
}
@Override
public Truck getTruck() {
return new QQTruck();
}
}
AudiFactory类:定义生产QQ产品的工厂( ConcreteFactory 2)
public class AudiFactory implements AbstractFactory {
@Override
public Car getCar() {
return new AudiCar();
}
@Override
public Truck getTruck() {
return new AudiTruck();
}
}
User类:模拟客户端(Client)
public class User {
public static void main(String[] args) {
//创建QQ品牌的car和truck
AbstractFactory qqFactory = new QQFactory();
Car qqCar = qqFactory.getCar();
Truck qqTruck = qqFactory.getTruck();
qqCar.move();
qqTruck.move();
//创建Audi品牌的car和truck
AbstractFactory audiFactory = new AudiFactory();
Car audiCar = audiFactory.getCar();
Truck audiTruck = audiFactory.getTruck();
audiCar.move();
audiTruck.move();
}
}
如果此时需求变更:增加一个Benz的产品族,则需要新增以下代码
- 新增BenzCar类,实现Car接口
public class BenzCar implements Car {
public BenzCar(){
System.out.println("制造Benz汽车");
}
@Override
public void move() {
System.out.println("我是Benz汽车,现在可以上路了....");
}
}
- 新增BenzTruck类,实现Truck接口
public class BenzTruck implements Truck {
public BenzTruck(){
System.out.println("制造Benz卡车");
}
@Override
public void move() {
System.out.println("我是Benz卡车,现在可以上路了....");
}
}
- 新增一个BenzFactory类,实现AbstractFactory接口
public class BenzFactory implements AbstractFactory {
@Override
public Car getCar() {
return new BenzCar();
}
@Override
public Truck getTruck() {
return new BenzTruck();
}
}
- 新增客户端测试代码
AbstractFactory benzFactory = new BenzFactory();
Car benzCar = benzFactory.getCar();
Truck benzTruck = benzFactory.getTruck();
benzCar.move();
benzTruck.move();
通过以上代码可以看出,对于新增一个产品族来说,只需要新增对应的代码即可,不会影响到之前的代码
但是,如果新增一个产品会怎样呢?假如新增一个bicycle(自行车)
- 会导致AbstractFactory接口发生变化
public interface AbstractFactory {
//生产car
Car getCar();
//生产truck
Truck getTruck();
//新增产品——bicycle
Bicycle getBicycle();
}
因为QQFactory、AudiFactory以及BenzFactory都实现了AbstractFactory接口,进而会导致这三个工厂子类发生变化,所以这就违反了OCP原则。
四、总结
上面介绍的三种工厂模式有各自的应用场景,实际应用时能解决问题满足需求即可,可灵活变通,无所谓高级与低级。
工厂模式的意义 将实例化对象的代码提取出来,放到一个类中统一管理和维护,达到和主项目的依赖关系的解耦。从而提高项目的扩展和维护性。
- 简单工厂模式适用于结构比较简单的关系
- 工厂方法适用于产品变更较为频繁的场景
- 抽象工厂适用于产品在最初设计好之后,几乎不会发生变更的场景中。