一、背景

本篇内容是 Java 设计模式创建型模式的第二篇。上一篇主题为 《Java 设计模式之单例模式(一)》

二、简单介绍

在介绍工厂模式之前,我们需要了解设计模式中应该遵循的原则。

五大原则:

1) 单一职责原则(SRP):就一个类而言,应该仅有一个引起它变化的原因。即一个类中应该只有一类逻辑。

2) 开放-封闭原则(OCP):软件实体(类、模块、函数等等)应该可以扩展,但是不可修改。即开放扩展,封闭修改。

3) 依赖倒转原则(DIP):高层模块不应该依赖底层模块。两个都应该依赖抽象;抽象不应该依赖细节。细节应该依赖抽象。即面向接口编程,而不是面向实现编程。

4) 里氏代换原则(LSP):子类型必须能够替换掉他们的父类型。

5) 合成/聚合复用原则(CARP):尽量使用合成/聚合,尽量不要使用类继承。

迪米特法则:如果两个类不必彼此直接通信,那么这两个类就不应当发生直接的相互作用。如果其中一个类需要调用另一个类的某一方法的话,可以通过第三者转发这个调用

三、实现方式

工厂模式有 3 种:简单工厂模式、工厂方法模式和抽象工厂模式。

本篇文章只讲解简单工厂模式、工厂方法模式。因为简单工厂模式和工厂方法模式是针对单独一类产品的创建方式,而抽象工厂模式是针对产品族考虑。

我们以创建轿车为例,未使用工厂模式:

public abstract class Car {

    public abstract void run();
}

public class Benz extends Car {

    @Override
    public void run() {
        System.out.println("开奔驰");
    }

}

public class Bmw extends Car{

    @Override
    public void run() {
        System.out.println("开宝马");
    }

}

public class Client {

    public static void main(String[] args) {
        Car car1 = new Benz();
        car1.run();
        
        Car car2 = new Bmw();
        car2.run();
    }
}
public abstract class Car {

    public abstract void run();
}

public class Benz extends Car {

    @Override
    public void run() {
        System.out.println("开奔驰");
    }

}

public class Bmw extends Car{

    @Override
    public void run() {
        System.out.println("开宝马");
    }

}

public class Client {

    public static void main(String[] args) {
        Car car1 = new Benz();
        car1.run();
        
        Car car2 = new Bmw();
        car2.run();
    }
}

在 Client 类中既有创建对象逻辑又有调用方法逻辑,且与多个类发生耦合。违背了单一职责原则和迪米特法则。

UML 类图表示如下:

3.1 简单工厂模式

简单工厂模式属于类的创建型模式,又叫做静态工厂方法模式。通过专门定义一个类来负责创建其他类的实例,被创建的实例通常都具有共同的父类。

工厂类:

public class CarFactory {

    public static Car createCar(String type) {
        Car car = null;
        switch (type) {
            case "benz":
                car = new Benz();
                break;
            case "bmw":
                car = new Bmw();
                break;
        }

        return car;
    }
}
public class CarFactory {

    public static Car createCar(String type) {
        Car car = null;
        switch (type) {
            case "benz":
                car = new Benz();
                break;
            case "bmw":
                car = new Bmw();
                break;
        }

        return car;
    }
}

客户端调用:

public class Client {

    public static void main(String[] args) {
        
        Car car1 = CarFactory.createCar("benz");
        car1.run();
        
        Car car2 = CarFactory.createCar("bmw");
        car2.run();
        
    }
}
public class Client {

    public static void main(String[] args) {
        
        Car car1 = CarFactory.createCar("benz");
        car1.run();
        
        Car car2 = CarFactory.createCar("bmw");
        car2.run();
        
    }
}

总结:Client 类与 Car 的子类解耦,它不需要知道 Car 实例具体创建细节,只需要通知工厂类需要怎样的实例。这样,减少了与外部类(Car 的子类)的通信,遵循了迪米特法则。

UML 类图表示如下:

3.2 工厂方法模式

简单工厂模式设计和使用非常简单。当我们需要新品牌的车子(如:奥迪)时,只需要在 CarFactory 的静态方法中添加一个判断即可。但是,这种写法违背了开放封闭原则,我们在编码是尽量不要修改业务代码。

为了解决这一问题,我们可以使用工厂方法模式。

工厂方法模式同样属于类的创建型模式又被称为多态工厂模式。工厂方法模式的意义是定义一个创建产品对象的工厂接口,将实际创建工作推迟到子类当中。

创建父类工厂:

public interface CarFactory {

    Car createCar();
}
public interface CarFactory {

    Car createCar();
}

工厂实现类:

public class BenzFactory implements CarFactory {

    @Override
    public Car createCar() {
        return new Benz();
    }

}

public class BmwFactory implements CarFactory {

    @Override
    public Car createCar() {
        return new Bmw();
    }

}
public class BenzFactory implements CarFactory {

    @Override
    public Car createCar() {
        return new Benz();
    }

}

public class BmwFactory implements CarFactory {

    @Override
    public Car createCar() {
        return new Bmw();
    }

}

客户端调用:

public class Client {

    public static void main(String[] args) {
    
        CarFactory cf1 = new BenzFactory();
        Car car1 = cf1.createCar();
        car1.run();
        
        CarFactory cf2 = new BmwFactory();
        Car car2 = cf2.createCar();
        car2.run();
    }
}
public class Client {

    public static void main(String[] args) {
    
        CarFactory cf1 = new BenzFactory();
        Car car1 = cf1.createCar();
        car1.run();
        
        CarFactory cf2 = new BmwFactory();
        Car car2 = cf2.createCar();
        car2.run();
    }
}

当我们需要添加奥迪车时,创建 Audi 实体类继承 Car 和 AudiFactory 工厂类去实现 CarFactory 接口方法即可。

总结:不用修改业务代码,只需创建新的类即可实现功能扩展,遵循了开放封闭原则。但是,Client 类中出现创建对象逻辑,违背责任单一原则。每新增一类产品就需要创建 N 个类文件,增加项目结构的复杂度。

UML 类图表示如下: