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中先启动容器的原因。

  • 相关阅读:
    ubuntu下文件安装与卸载
    webkit中的JavaScriptCore部分
    ubuntu 显示文件夹中的隐藏文件
    C语言中的fscanf函数
    test
    Use SandCastle to generate help document automatically.
    XElement Getting OuterXML and InnerXML
    XUACompatible meta 用法
    Adobe Dreamweaver CS5.5 中文版 下载 注册码
    The Difference Between jQuery’s .bind(), .live(), and .delegate()
  • 原文地址:https://www.cnblogs.com/hello-daocaoren/p/8677831.html
Copyright © 2011-2022 走看看