zoukankan      html  css  js  c++  java
  • 工厂模式

    简介

    工厂模式(Factory Pattern)是 Java 中最常用的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。工厂模式主要是解决创建对象的问题,典型的应用就是在spring中的IOC,反转控制,反转控制就是把创建对象的权限交给框架,所以spring就是一个生产对象的工厂。

    思路

    工厂模式的思路就是设计一个产生对象的机制,让生产对象的过程交给第三方,在工厂模式中,不会对客户端暴露创建逻辑,并且使用通用接口接收新创建的对象。

    实现过程

    • 新建抽象的接口
    • 新建具体的实体类,实现抽象的接口
    • 创建实例化对象的工厂
    • 在客户端中通过工厂创建具体的实体对象,对象可以用抽象接口接收。

    这种方式是最简单的实现方式:

    // 创建接口
    public interface Shape {
        void draw();
    }
    
    // 创建实体类Circle
    public class Circle implements Shape {
        @Override
        public void draw() {
            System.out.println("drawing a circle");
        }
    }
    
    // 创建实体类Rectangle
    public class Rectangle implements Shape {
        @Override
        public void draw() {
            System.out.println("drawing a Rectangle");
        }
    }
    
    // 创建实体类Square 
    public class Square implements Shape {
        @Override
        public void draw() {
            System.out.println("drawing a square");
        }
    }
    

    然后创建工厂类,生成对应的实体类

    public class ShapeFactory {
        public static Shape getShapes(String shapeType) {
            if (shapeType == null) {
                System.out.println("shapeType is null");
                throw new RuntimeException();
            } else if (shapeType.equalsIgnoreCase("Rectangle")) {
                return new Rectangle();
            } else if (shapeType.equalsIgnoreCase("Square")) {
                return new Square();
            }else if(shapeType.equalsIgnoreCase("Circle")){
                return new Circle();
            }else {
                System.out.println("nothing to do");
                return null;
            }
        }
    
        // 测试简单工厂模式
        @Test
        public void testSimpleFactoryPattern(){
            Shape circle = ShapeFactory.getShapes("circle");
            circle.draw();
            Shape rectangle = ShapeFactory.getShapes("Rectangle");
            rectangle.draw();
            Shape square = ShapeFactory.getShapes("Square");
            square.draw();
        }
    }
    

    这种方式实现工厂模式很简单,但是缺点也很明显,比如,在增加一个实现Shape接口的实体类,又需要去修改ShapeFactory中的代码,这样其实不符合设计模式的原则,对扩展开放,对修改关闭。

    工厂模式的改进

    分析一下,之所以每新增一个类都需要去修改工厂的代码,是因为在工厂中,生成类的代码太具体了,要想改变这种情况,就需要把这个工厂生成类实例的过程变得抽象化,在Java中,生成对象的方法不止一种,还可以利用反射机制,工厂接收的是和类相关的参数,可以把这个参数换成需要生成实例的类,这样工厂中生成类的代码就很抽象了。具体代码如下:

    public class ShapeFactory {
        public static Shape getClass(Class<? extends Shape> clazz) {
            Shape shape = null;
            try {
                // 通过反射生成一个类的实例
                shape = (Shape) Class.forName(clazz.getName()).newInstance();
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
            return shape;
        }
    
        // 测试反射改进后的工厂
        @Test
        public void testReflectShapeFac(){
            Shape rectangle = ShapeFactory.getClass(Rectangle.class);
            rectangle.draw();
            Shape square = ShapeFactory.getClass(Square.class);
            square.draw();
            Shape circle = ShapeFactory.getClass(Circle.class);
            circle.draw();
        }
    }
    

    这样工厂的生产过程就很抽象了,但是还有一个问题,这个工厂只能生成实现Shape接口的类实例,如果出现了另外一种接口,就有需要新增一个工厂,这样也未尝不可,因为只是扩展而已,但是又出现了一个新的问题,这些工厂中的生产对象的代码都差不多,只是强转的接口不同,代码还是有优化的空间的。工厂可以进一步抽象:

    // 改进后的工厂方法
     public static <T> T getClass(Class<? extends T> clazz){
            T obj = null;
            try {
                obj= (T) Class.forName(clazz.getName()).newInstance();
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
            return obj;
        }
    

    借助配置文件的工厂模式

    到这里,可以去思考一下在spring中,是怎么利用工厂模式的,首先你需要去xml中配置你需要实例化的类,然后读取这个配置文件,通过反射生成这个类的实例返回。其实这里也可以简单模仿一下:

    // 解析Properties配置文件
    public class PropertiesUtil {
    
        private static Properties properties = new Properties();
        
        public static String getPackageByName(String name) {
            return properties.getProperty(name);
        }
        
        // 解析配置文件
        private static Map<String, String> parseProperties() {
            Map<String, String> map = new HashMap<>();
            InputStream inputStream = PropertiesUtil.class.getClassLoader().getResourceAsStream("application.properties");
            try {
                properties.load(inputStream);
            } catch (IOException e) {
                System.err.println("file is not exists");
                e.printStackTrace();
            }
            return map;
        }
    }
    

    配置文件中的内容如下:

    # 类名和包名的映射
    circle=com.factory.pattern.simple.Circle
    rectangle=com.factory.pattern.simple.Rectangle
    square=com.factory.pattern.simple.Square
    

    然后根据提供的配置文件的报名,通过反射实例化对应的类:

    public class ConfigFactory {
         // 通过配置文件中的包名生成实例
        public static <T> T getNewInstance(String className) {
            String packageName = PropertiesUtil.getPackageByName(className);
            try {
                return (T) Class.forName(packageName).newInstance();
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
            return null;
        }
    }
    

    这种方式需要需要注意的是,加载配置文件一定是在调用工厂的前面,因为需要读取报名,把对应的数据读到内存中,这也就是spring中先启动容器的原因。

  • 相关阅读:
    使用ShareSDK完成第三方(QQ、微信、微博)登录和分享
    Https适配
    AFNetworking实现程序重新启动时的断点续传
    开源Word读写组件DocX 的深入研究和问题总结
    ITTC数据挖掘平台介绍(四) 框架改进和新功能
    聪明的投资者读书笔记1
    word中公式居中编号在最右端
    tensorflow中使用Batch Normalization
    财务自由之路读书笔记二
    mybatis与mysql中的Date和String之间转换
  • 原文地址:https://www.cnblogs.com/hello-daocaoren/p/8677831.html
Copyright © 2011-2022 走看看