美文网首页
创建型模式——工厂模式

创建型模式——工厂模式

作者: Doooook | 来源:发表于2020-08-12 22:42 被阅读0次

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

    // 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类进行修改,这样就打破了开闭原则。
    我们可以改进这种简单工厂模式,使得注册的新类在使用时才被实例化,从而保证其对扩展开放,同时对修改闭合。

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

    • 使用反射机制注册产品类对象和实例化。
    • 注册产品对象并向每个产品添加newInstance方法,该方法返回与自身类型相同的新实例。

    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

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

    • AbstractFactory(抽象工厂类):抽象类,用于声明创建不同类型产品的方法。它针对不同的抽象产品类都有对应的创建方法。
    • ConcreteFactory(具体工厂类):具体类,用于实现抽象工厂基类中声明的方法。针对每个系列的产品都有一个对应的具体工厂类。
    • AbstracProduct(抽象产品类):对象所需的基本接口或类。一簇相关的产品类由来自不同层级的相似产品类组成。ProductA1ProductB1来自第一个类簇,由ConcreteFactory1实例化。ProductA2ProductB2来自第二个类簇,由ConcreteFactory2实例化。

    相关文章

      网友评论

          本文标题:创建型模式——工厂模式

          本文链接:https://www.haomeiwen.com/subject/kteudktx.html