创建型模式——工厂模式

2020-08-12  本文已影响0人  Doooook

我们在实例化对象时,一般都是通过类的构造函数来实例化的,比如:

// Car继承自Vehicle
Vehicle vehicle = new Car();

这段代码说明了Vehicle和Car两个类之间的依赖关系。这样的依赖关系使代码紧密耦合,在不更改的情况下很难扩展。举例来说,假设要用Truck替换Car,就需要修改相应的代码:

// Car继承自Vehicle
Vehicle vehicle = new Truck();

这里存在两个问题:其一,类应该保持对扩展的开放和对修改的关闭(开闭原则);其二,每个类应该只有一个发生变化的原因(单一职责原则)。每增加新的类造成主要代码修改时会打破开闭原则,而主类除了其固有功能之外还负责实例化vehicle对象,这种行为将会打破单一职责原则。
在这种情况下就需要一种更好的设计方案。我们可以增加一个新类来负责实例化vehicle对象,称之为简单工厂模式

简单工厂模式

工厂模式用于实现逻辑的封装,并通过公共的接口提供对象的实例化服务,在添加新的类时只需要做少量的修改。
SimpleFactory中包含实例化ConcreteProduct1ConcreteProduct2的代码。当客户需要对象时,调用SimpleFactorycreateProduct()方法,并提供参数指明所需对象的类型。SimpleFactory实例化相应的具体产品并返回,返回的产品对象被转换为基类类型。因此,无论是ConcreteProduct1还是ConcreteProduct2,客户能以相同的方式处理。

image.png

1. 静态工厂模式

下面我们写一个简单的工厂类用来创建Vehicle实例。我们创建一个抽象Vehicle类和继承自它的三个具体类:BikeCarTruck。工厂类(也叫静态工厂类)代码如下所示:

image.png
/**
 * @author: Jay Mitter
 * @date: 2020-08-12 21:26
 * @description: 静态工厂模式
 * 1.抽象类要被子类继承,接口要被类实现。
 * 2.接口只能做方法声明,抽象类中可以作方法声明,也可以做方法实现。
 * 3.接口里定义的变量只能是公共的静态的常量,抽象类中的变量是普通变量。
 * 4.接口是设计的结果,抽象类是重构的结果。
 * 5.抽象类和接口都是用来抽象具体对象,但是接口的抽象级别最高。
 * 6.抽象类可以有具体的方法和属性,接口只能有抽象方法和不可变常量。
 * 7.抽象类主要用来抽象类别,接口主要用来抽象功能。
 */
public class VehicleFactory {

    public enum VehicleType {
        Bike, Car, Truck;
    }

    public static AbstractVehicle create(VehicleType type) {
        if (type.equals(VehicleType.Bike)) {
            return new Bike();
        }
        if (type.equals(VehicleType.Car)) {
            return new Car();
        }
        if (type.equals(VehicleType.Truck)) {
            return new Truck();
        }
        return null;
    }

}

工厂类逻辑非常简单,只负责Vehicle类的实例化,符合单一职责原则;用户只调用Vehicle接口,这样做可以减少耦合,符合依赖倒置原则;但是当增加一个新的Vehicle类时,需要对VehicleFactory类进行修改,这样就打破了开闭原则。
我们可以改进这种简单工厂模式,使得注册的新类在使用时才被实例化,从而保证其对扩展开放,同时对修改闭合。

具体的实现方式有以下两种:

2. 使用反射机制进行类注册的简单工厂模式

/**
 * @author: Jay Mitter
 * @date: 2020-08-12 21:26
 * @description: 静态工厂模式
 * 1.抽象类要被子类继承,接口要被类实现。
 * 2.接口只能做方法声明,抽象类中可以作方法声明,也可以做方法实现。
 * 3.接口里定义的变量只能是公共的静态的常量,抽象类中的变量是普通变量。
 * 4.接口是设计的结果,抽象类是重构的结果。
 * 5.抽象类和接口都是用来抽象具体对象,但是接口的抽象级别最高。
 * 6.抽象类可以有具体的方法和属性,接口只能有抽象方法和不可变常量。
 * 7.抽象类主要用来抽象类别,接口主要用来抽象功能。
 */
public class VehicleFactory {

    public enum VehicleType {
        Bike, Car, Truck;
    }

    /**
     * 使用map对象来保存产品ID极其对应的类
     */
    private Map<String, Class> registeredProducts = new HashMap<>();

    /**
     * 注册新Vehicle类的方法
     * @param vehicleId id
     * @param vehicleClass 类全类路径
     */
    public void registerVehicle(String vehicleId, Class vehicleClass) {
        registeredProducts.put(vehicleId, vehicleClass);
    }

    /**
     * 构造方法,根据注册的对象获取对应的类
     * @param type 对应vehicleId
     * @return
     * @throws IllegalAccessException
     * @throws InstantiationException
     */
    public AbstractVehicle createVehicle(String type) throws IllegalAccessException, InstantiationException {
        Class productClass = registeredProducts.get(type);
        return (AbstractVehicle) productClass.newInstance();
    }

    public static AbstractVehicle create(VehicleType type) {
        if (type.equals(VehicleType.Bike)) {
            return new Bike();
        }
        if (type.equals(VehicleType.Car)) {
            return new Car();
        }
        if (type.equals(VehicleType.Truck)) {
            return new Truck();
        }
        return null;
    }

}

测试:

    /**
     * 简单工厂模式测试
     */
    @Test
    public void testFactoryPattern() {
        // 静态方法创建对象,根据已经写死的VehicleType创建对象
        AbstractVehicle abstractVehicle = VehicleFactory.create(VehicleFactory.VehicleType.Car);
        assert abstractVehicle != null;
        abstractVehicle.doSomething();
        // 注册的新类在使用时才被实例化
        VehicleFactory vehicleFactory = new VehicleFactory();
        try {
            // 先注册
            vehicleFactory.registerVehicle("car", com.pengjs.book.review.designpattern.factory.Car.class);
            // 再创建对象实例
            AbstractVehicle car = vehicleFactory.createVehicle("car");
            car.doSomething();
        } catch (IllegalAccessException | InstantiationException e) {
            e.printStackTrace();
        }
    }

但在某些情况下,反射机制并不适用。比如,反射机制需要运行时权限,这在某些特定环境中是无法实现的。反射机制也会降低程序的运行效率,在对性能要求很高的场景下应该避免使用这种机制。

3. 使用newInstance方法进行类注册的简单工厂模式

image.png
/**
 * @author: Jay Mitter
 * @date: 2020-08-12 21:26
 * @description: 静态工厂模式
 * 1.抽象类要被子类继承,接口要被类实现。
 * 2.接口只能做方法声明,抽象类中可以作方法声明,也可以做方法实现。
 * 3.接口里定义的变量只能是公共的静态的常量,抽象类中的变量是普通变量。
 * 4.接口是设计的结果,抽象类是重构的结果。
 * 5.抽象类和接口都是用来抽象具体对象,但是接口的抽象级别最高。
 * 6.抽象类可以有具体的方法和属性,接口只能有抽象方法和不可变常量。
 * 7.抽象类主要用来抽象类别,接口主要用来抽象功能。
 */
public class VehicleFactory {

    public enum VehicleType {
        Bike, Car, Truck;
    }

    /**
     * 使用map对象来保存产品ID及其对应的类
     */
    private Map<String, Class> registeredProducts = new HashMap<>();

    /**
     * 使用map对象来保存产品ID极其对应的抽象类
     */
    private Map<String, AbstractVehicle> registeredVehicles = new HashMap<>();

    /**
     * 注册新Vehicle类的方法
     * @param vehicleId id
     * @param vehicleClass 类全类路径
     */
    public void registerVehicle(String vehicleId, Class vehicleClass) {
        registeredProducts.put(vehicleId, vehicleClass);
    }

    /**
     * 注册子类,使用具体的实现类vehicle,避免了使用反射
     * @param vehicleId id
     * @param vehicle 具体的子类:Car Bike Truck等
     */
    public void registerVehicleBySub(String vehicleId, AbstractVehicle vehicle) {
        registeredVehicles.put(vehicleId, vehicle);
    }

    /**
     * 构造方法,根据注册的对象获取对应的类
     * @param type 对应vehicleId
     * @return
     * @throws IllegalAccessException
     * @throws InstantiationException
     */
    public AbstractVehicle createVehicle(String type) throws IllegalAccessException, InstantiationException {
        Class productClass = registeredProducts.get(type);
        return (AbstractVehicle) productClass.newInstance();
    }

    /**
     * 使用每个子类的newInstance创建对象(实例),避免了使用反射的性能消耗
     * @param vehicleId
     * @return
     * @throws IllegalAccessException
     * @throws InstantiationException
     */
    public AbstractVehicle createVehicleByIdSub(String vehicleId) throws IllegalAccessException, InstantiationException {
        // 每个子类都实现了抽象类的newInstance方法
        return (AbstractVehicle) registeredProducts.get(vehicleId).newInstance();
    }

    public static AbstractVehicle create(VehicleType type) {
        if (type.equals(VehicleType.Bike)) {
            return new Bike();
        }
        if (type.equals(VehicleType.Car)) {
            return new Car();
        }
        if (type.equals(VehicleType.Truck)) {
            return new Truck();
        }
        return null;
    }

}

测试:

/**
     * 简单工厂模式测试
     */
    @Test
    public void testFactoryPattern() {
        // 静态方法创建对象,根据已经写死的VehicleType创建对象
        AbstractVehicle abstractVehicle = VehicleFactory.create(VehicleFactory.VehicleType.Car);
        assert abstractVehicle != null;
        abstractVehicle.doSomething();

        // 注册的新类在使用时才被实例化
        VehicleFactory vehicleFactory = new VehicleFactory();
        try {
            // 先注册
            vehicleFactory.registerVehicle("car", com.pengjs.book.review.designpattern.factory.Car.class);
            // 再创建对象实例
            AbstractVehicle car = vehicleFactory.createVehicle("car");
            car.doSomething();
        } catch (IllegalAccessException | InstantiationException e) {
            e.printStackTrace();
        }

        // 注册
        vehicleFactory.registerVehicleBySub("car", new com.pengjs.book.review.designpattern.factory.Car());
        try {
            AbstractVehicle car = vehicleFactory.createVehicleByIdSub("car");
            car.doSomething();
        } catch (IllegalAccessException | InstantiationException e) {
            e.printStackTrace();
        }
    }

工厂方法模式

工厂方法模式是在静态工厂模式上的改进。工厂类被抽象化,用于实例化特定产品类的代码被转移到实现抽象方法的子类中。这样不需要修改就可以扩展工厂类。

image.png
假设有一个汽车工厂,目前只生产两种车型,小型跑车和大型家用车。在软件中,顾客可以自由决定买小型车或大型车。首先,我们需要创建一个Vehicle类和两个子类,子类分别为SportCarSedanCar
工厂类中并不包含任何创建新实例的代码,各个子类工厂实现该方法创建子类实例:
/**
 * @author: Jay Mitter
 * @date: 2020-08-15 08:56
 * @description:
 */
public abstract class AbstractVehicleFactory {

    /**
     * 抽象方法创建Vehicle实例,各个子类实现该方法创建子类实例
     * @param size
     * @return
     */
    protected abstract Vehicle createVehicle(String size);

    public Vehicle orderVehicle(String size, String name) {
        Vehicle vehicle = createVehicle(size);
        vehicle.setName(name);
        return vehicle;
    }

}

Car类工厂,创建SportCarSedanCar

/**
 * @author: Jay Mitter
 * @date: 2020-08-15 09:03
 * @description: Car类工厂,创建SportCar和SedanCar
 */
public class CarFactory extends AbstractVehicleFactory {
    @Override
    protected Vehicle createVehicle(String size) {
        if ("small".equals(size)) {
            return new SportCar("sport");
        } else if ("large".equals(size)) {
            return new SedanCar("sedan");
        }
        return null;
    }
}

此时,我们意识到汽车工厂所带来的收益,是时候进一步拓展业务了。市场调查显示卡车的需求量很大,因此我们建一个卡车工厂(TruckFactory):

/**
 * @author: Jay Mitter
 * @date: 2020-08-15 09:03
 * @description:
 */
public class TruckFactory extends AbstractVehicleFactory {
    @Override
    protected Vehicle createVehicle(String size) {
        if ("small".equals(size)) {
            return new SmallTruck("small truck");
        } else if ("large".equals(size)) {
            return new LargeTruck("large truck");
        }
        return null;
    }
}

匿名具体工厂模式

    /**
     * 简单工厂模式测试2
     */
    @Test
    public void testFactoryPattern2() {
        // Car类工厂
        AbstractVehicleFactory carFactory = new CarFactory();
        SedanCar sedanCar = (SedanCar) carFactory.orderVehicle("large", "car");
        System.out.println(JSON.toJSONString(sedanCar));

        // Truck类工厂
        AbstractVehicleFactory truckFactory = new TruckFactory();
        LargeTruck largeTruck = (LargeTruck) truckFactory.orderVehicle("large", "large truck");
        System.out.println(JSON.toJSONString(largeTruck));

        // Truck类工厂,直接创建匿名类来对AbstractVehicleFactory扩展即可
        AbstractVehicleFactory bikeFactory = new AbstractVehicleFactory() {
            @Override
            protected Vehicle createVehicle(String size) {
                if ("small".equals(size)) {
                    return new MountainBike("mountain bike");
                } else if ("large".equals(size)) {
                    return new CityBike("city bike");
                }
                return null;
            }
        };
        MountainBike mountainBike = (MountainBike) bikeFactory.orderVehicle("small", "mountain bike");
        System.out.println(JSON.toJSONString(mountainBike));
    }

实体类:

/**
 * @author: Jay Mitter
 * @date: 2020-08-15 09:05
 * @description:
 */
public class SedanCar extends Vehicle {
    public SedanCar(String name) {
        super(name);
    }
}
/**
 * @author: Jay Mitter
 * @date: 2020-08-15 09:05
 * @description:
 */
public class SportCar extends Vehicle {
    public SportCar(String name) {
        super(name);
    }
}
/**
 * @author: Jay Mitter
 * @date: 2020-08-15 09:19
 * @description:
 */
public class SmallTruck extends Vehicle {
    public SmallTruck(String name) {
        super(name);
    }
}
/**
 * @author: Jay Mitter
 * @date: 2020-08-15 09:20
 * @description:
 */
public class LargeTruck extends Vehicle {
    public LargeTruck(String name) {
        super(name);
    }
}
/**
 * @author: Jay Mitter
 * @date: 2020-08-15 09:19
 * @description:
 */
public class SmallTruck extends Vehicle {
    public SmallTruck(String name) {
        super(name);
    }
}
/**
 * @author: Jay Mitter
 * @date: 2020-08-15 09:25
 * @description:
 */
public class MountainBike extends Vehicle {
    public MountainBike(String name) {
        super(name);
    }
}
/**
 * @author: Jay Mitter
 * @date: 2020-08-15 09:26
 * @description:
 */
public class CityBike extends Vehicle {
    public CityBike(String name) {
        super(name);
    }
}

抽象工厂模式

抽象工厂模式是工厂方法模式的扩展版本。它不再是创建单一类型的对象,而是创建一系列相关联的对象。如果说工厂方法模式中只包含一个抽象产品类,那么抽象工厂模式则包含多个抽象产品类
工厂方法类中只有一个抽象方法,在不同的具体工厂类中分别实现抽象产品的实例化,而抽象工厂类中,每个抽象产品都有一个实例化方法。
如果我们采用抽象工厂模式并将它应用于包含单个对象的簇,那么就得到了工厂方法模式。工厂方法模式只是抽象工厂模式的一种特例。

image.png

抽象工厂模式由以下类组成:

上一篇下一篇

猜你喜欢

热点阅读