Spring01:工厂模式

1. EJB存在的问题

在这里插入图片描述

2. 什么是Spring

Spring是一个轻量级的JavaEE解决方案,整合众多优秀的设计模式
  • 轻量级
 1. 对于运行环境是没有额外要求的
    开源 tomcat resion jetty 
    收费 weblogic  websphere 
 2. 代码移植性高
    不需要实现额外接口
  • JavaEE的解决方案

在这里插入图片描述

  • 整合设计模式
1. 工厂
2. 代理
3. 模板
4. 策略

3. 设计模式

1. 广义概念
面向对象设计中,解决特定问题的经典代码
2. 狭义概念
GOF4人帮定义的23种设计模式:工厂、适配器、装饰器、门面、代理、模板...

4. 工厂设计模式

4.1 一个简单工厂模式实现

  1. 概念:通过工厂类,创建对象
    User user = new User();
    UserDAO userDAO = new UserDAOImpl();

  2. 工厂模式创建对象的好处:解耦合

    耦合:指定是代码间的强关联关系,一方的改变会影响到另一方

    问题:不利于代码维护,改一处代理会影响到许多地方。

    简单:把接口的实现类,硬编码在程序中:UserService userService = new UserServiceImpl();

  3. 通过工厂类能解除这种耦合。

  4. 代码案例:

public class TestSpring {
    
    
    @Test
    public void test1(){
    
    

        //UserService userService = new UserServiceImpl();

        /**
         *  1.运用工厂模式中的工厂方法来创建对象,实现解耦。
         *  2.此时在这段代码中,就没有了耦合:UserService接口的实现类,在这段代码中是没有任何体现的。
         *  3.但是在工厂中不还是引入了具体的实现类吗?耦合不就转移到工厂中了吗?
         *      所以要对工厂进行后续的设计。
         *  4.但至少现在,从应用层面上,站在调用者的角度这部分的耦合是解决了。
         */
        UserService userService = BeanFactory.getUserService();
        User user = new User("ttt", "123456");

        userService.login(user.getName(), user.getPassword());
        userService.register(user);

    }

}
  • 4.1 耦合体现在这行代码:UserService userService = new UserServiceImpl();

    1.如果我后续提供了一个新的实现类UserServiceImpl2,而且我想要替换掉旧的实现类UserServiceImpl。那么我就要重新改这行代码,改成:UserService userService = new UserServiceImpl2();

    2.如果我们在开发中真的进行这样的修改的话,它是会经过一个编译部署的过程,经常这样是有很大额外开销的。而且这也不符合面向对象的“开闭原则”:对扩展开放,对修改关闭。

    3.所以耦合处位于:UserService userService = new UserServiceImpl();解决这个耦合:运用工厂模式,通过工厂类来创建对象,代替new对象。

  • 4.2 一个最简单的工厂

public class BeanFactory {
    
    

    /**
     *  1.此时在工厂中引入了具体的实现类吗?耦合不就转移到工厂中了吗?所以要对工厂进行后续的设计。
     *  2.但至少现在,从应用层面上,站在调用者的角度这部分的耦合是解决了。
     * @return
     */
    public static UserService getUserService() {
    
    
        return new UserServiceImpl();
    }
}
    • 1.此时在工厂中引入了具体的实现类吗?耦合不就转移到工厂中了吗?所以要对工厂进行后续的设计。
    • 2.但至少现在,从应用层面上,站在调用者的角度这部分的耦合是解决了。

4.2.进一步设计:反射工厂

1.先回顾一下对象的创建方式

  • 1.直接调用构造方法创建对象:UserService userService = new UserServiceImpl();。但是存在耦合。
  • 2.通过反射的形式创建对象,可以实现解耦合的目的。
    2.1.先拿到Class类对象:Class clazz = Class.forName(“全限定类名”);
    2.2.创建对象实例:UserService userService = (UserService )clazz.newInstance();

2.反射工厂代码实现:

public class BeanFactory {
    
    
    public static UserService getUserService() {
    
    
        //return new UserServiceImpl();
        UserService userService = null;
        try {
    
    
            Class<?> clazz = Class.forName("com.baizhiedu.basic.UserServiceImpl");
            userService = (UserService)clazz.newInstance();
        } catch (ClassNotFoundException e) {
    
    
            e.printStackTrace();
        } catch (InstantiationException e) {
    
    
            e.printStackTrace();
        } catch (IllegalAccessException e) {
    
    
            e.printStackTrace();
        }
        return userService;
    }
}

3.还存在耦合:

  • 如果想要创建一个其他对象,那么Class.forName("com.baizhiedu.basic.UserServiceImpl");中的全限定类名就需要改变,这又会涉及到耦合。
  • 字符串的耦合处理起来较为简单,我们可以用一个外部的properties配置文件来处理,将全限定类名写在配置文件中。那么之后我们只需要根据情况修改配置文件,再加载配置文件即可。
  • 这样就把耦合抽取到代码之外了,实现了解耦。

4.properties配置文件实现解耦:继续改进

  • 创建一个配置文件

在这里插入图片描述

  • 将类名信息写到properties文件中
userService = com.baizhiedu.basic.UserServiceImpl
  • 在将配置文件中的信息读取出来:
    1.Properties集合对象来存储Properties文件的内容
    2.Properties集合是一种特殊的Map,并且要求key=String,Value=String类型。
  • 注意:I/O
    1.I/O在整个Java操作过程中是一种系统级资源,一般要尽量避免重复性的打开I/O。而且最好是在程序启动的时候,一次性读取完我们想要的内容。
    2.所以再读取配置文件的时候,我们用静态代码块来实现。
    private static Properties env = new Properties();
    static{
    
    
        //第一步:获取IO输入流
        InputStream inputStream = BeanFactory.class.getResourceAsStream("/applicationContext.properties");
        try {
    
    
            //第二步:文件内容封装到Properties集合中,key=userService value=com.baizhiedu.basic.UserServiceImpl
            env.load(inputStream);
            //第三步:关闭流
            inputStream.close();
        } catch (IOException e) {
    
    
            e.printStackTrace();
        }
    }

-5.反射工厂的代码:从Properties对象中拿到要创建的类名

public class BeanFactory {
    
    
    private static Properties env = new Properties();
    static{
    
    
        //第一步:获取IO输入流
        InputStream inputStream = BeanFactory.class.getResourceAsStream("/applicationContext.properties");
        try {
    
    
            //第二步:文件内容封装到Properties集合中,key=userService value=com.baizhiedu.basic.UserServiceImpl
            env.load(inputStream);
            //第三步:关闭流
            inputStream.close();
        } catch (IOException e) {
    
    
            e.printStackTrace();
        }
    }

    public static UserService getUserService() {
    
    
        UserService userService = null;
        try {
    
    
        	//从Properties对象中拿到要创建的类名
            Class<?> clazz = Class.forName(env.getProperty("userService"));
            userService = (UserService)clazz.newInstance();
        } catch (ClassNotFoundException e) {
    
    
            e.printStackTrace();
        } catch (InstantiationException e) {
    
    
            e.printStackTrace();
        } catch (IllegalAccessException e) {
    
    
            e.printStackTrace();
        }
        return userService;
    }
}

6.如果后面想要别的实现类,那么在properties中再配置就好了,这样就解决了实例化UserService实现类的耦合

userService = com.baizhiedu.basic.UserServiceImpl
userServiceNew = com.baizhiedu.basic.UserServiceImplNew
  • 比如想要解决:private UserDAO userDao = new UserDAOImpl();UserServiceImpl类中的这句代码的耦合。
  • 配置文件
userDAO =com.baizhiedu.basic.UserDAOImpl
  • 工厂类中再写一个方法
    public static UserDAO getUserDao() {
    
    
        UserDAO userDAO = null;
        try {
    
    
            Class<?> clazz = Class.forName(env.getProperty("userDAO"));
            userDAO = (UserDAO)clazz.newInstance();
        } catch (ClassNotFoundException e) {
    
    
            e.printStackTrace();
        } catch (InstantiationException e) {
    
    
            e.printStackTrace();
        } catch (IllegalAccessException e) {
    
    
            e.printStackTrace();
        }
        return userDAO;
    }
  • UserServiceImpl代码修改
public class UserServiceImpl implements UserService {
    
    

    //private UserDAO userDao = new UserDAOImpl();
    private UserDAO userDao = BeanFactory.getUserDao();

    @Override
    public void register(User user) {
    
    
        userDao.register(user);
    }

    @Override
    public void login(String name, String password) {
    
    
        userDao.login(name, password);
    }
}

4.3.通用工厂设计

1.回顾一下上面的反射工厂,我们通过工厂类中的工厂方法起到了解耦的效果。我们创建了getUserService方法,解决了UserService的耦合;创建了getUserDao方法,解决了UserDao的耦合。

2.但是在设计层面上这里是有些问题的:

  1. 上面的反射工厂中分别为UserServiceImpl和UserDaoImpl这两个类创建了工厂方法,那么就会出现一个问题。如果我想要为某个类解耦合,那么就要再创建一个该类的工厂方法吗?这样岂不是要创建成千上万个工厂方法。
  2. 观察发现上面两个工厂方法存在大量的重复冗余代码。
    在这里插入图片描述

所以为每个类都提供一个独立的工厂方法是没有价值和意义的。同时也会有大量的冗余代码。

4.那么能不能设计一个通用的工厂方法来解决这个问题,在通用的工厂里面,只提供一个通用的工厂方法,这个方法可以帮我们生产一切我们想要的对象。

  1. 设计思想:这个工厂方法不固定生产哪个对象,或者说可以生产所有工厂对象。那么到底生产哪个对象呢?由调用者来决定。
  2. 所以生产什么对象,体现在参数中,有调用者来决定。

5.具体代码:通用工厂

public class BeanFactory {
    
    
    private static Properties env = new Properties();
    static{
    
    
        InputStream inputStream = BeanFactory.class.getResourceAsStream("/applicationContext.properties");
        try {
    
    
            env.load(inputStream);
            inputStream.close();
        } catch (IOException e) {
    
    
            e.printStackTrace();
        }
    }
    
    /**
     * 通用工厂方法
     * @param key
     * @return
     */
    public static Object getBean(String key){
    
    
        Object instance = null;
        try {
    
    
            Class<?> clazz = Class.forName(env.getProperty(key));
            instance = clazz.newInstance();
        } catch (Exception e) {
    
    
            e.printStackTrace();
        }
        return instance;
    }
}

4.4.通用工厂使用

1.创建工厂还挺复杂的:首先需要反射创建对象,其次涉及大量的I/O操作读取配置文件内容,这个的处理过程相对来讲还挺麻烦的。所以日常开发中,一般不需要我们自己创建工厂。一般都会准备好工厂和配置文件,只要我们会使用它即可。那么我么那该怎么使用这个通用的工厂呢?怎么用这个通用的工厂来帮我们创建对象,最终帮我们完成解耦合的操作呢?

2.那么怎么使用通用工厂?

  1. 工厂的核心目的:创建对象。既然是创建对象,那么我们首先要定义类型。
  2. 怎么让工厂类,认识到我们刚才定义的类型(我们想要创建的对象)。通过配置文件的配置告诉工厂。key = value
  3. 工厂创建好这些对象后,我们就要获得这些对象:BeanFactory.getBean(“key”);

5.引言总结

Spring的引言部分就讲完了,主要是学习了工厂设计模式。因为Spring最核心的就是工厂,Spring的本质就是一个工厂。

Spring的工厂叫做ApplicationContext,配置文件是applicationContext.xml。

猜你喜欢

转载自blog.csdn.net/tttxxl/article/details/114699929