作者简介:☕️大家好,我是Aomsir,一个爱折腾的开发者!
个人主页:Aomsir_Spring5应用专栏,Netty应用专栏,RPC应用专栏-CSDN博客
当前专栏:Spring5应用专栏_Aomsir的博客-CSDN博客
参考文献
引言
Spring Framework
巧妙地融合了诸多卓越的设计模式,如工厂模式、代理模式、模板模式和策略模式等,使得其不仅轻量级,更被誉为全方位的企业级解决方案。
什么是设计模式?
在广义上,设计模式指的是在面向对象设计中为解决特定问题而形成的经典代码结构。 而在狭义上,设计模式特指GOF四人帮定义的23种经典模式,如工厂、适配器、装饰器、门面、代理、模板等。实际上,这些模式在一个项目中并不总是都被用到,因为某些模式更适合桌面应用场景。 而在Spring Framework中,工厂设计模式是尤为核心和关键的一个模式。
工厂设计模式
- 工厂设计模式是指通过工厂类去创建对象,而不是直接通过new对象的方式硬编码创建
- 工厂模式的好处:为代码提供优雅的解耦合方式
- 耦合是指代码间的强关联关系,当其中一个部分发生变动,可能会直接影响到其他部分
- 问题:代码的高度耦合降低了其维护性。替换或调整关联部分时,不得不修改Java源码,然后经过重新编译、打包和部署的流程,而编译过程可能会消耗相当的时间
问题代码
我构建了一个登录注册程序的示例。在这个示例中,DAO层并未真正连接到数据库,只是模拟地打印出数据;而Controller层则通过Junit进行模拟和测试。从代码结构来看,UserService与UserDAOImpl之间存在明显的紧耦合,同样地,单元测试与UserServiceImpl也存在这种关联。
假如我们打算用UserServiceImplNew来替换现有的UserServiceImpl,就必须调整单元测试的代码,接着再进行编译、打包和部署,这无疑增加了系统的复杂性和维护难度。这是一个典型的高耦合示例。
实体类
@Data
public class User {
private String user;
private String password;
}
DAO层
public interface UserDAO {
void save(User user);
void queryUserByNameAndPassword(String name, String password);
}
public class UserDAOImpl implements UserDAO {
@Override
public void save(User user) {
System.out.println("insert into user = " + user);
}
@Override
public void queryUserByNameAndPassword(String name, String password) {
System.out.println("query User name = " + name + " password = " + password);
}
}
Service层
public interface UserService {
public void register(User user);
public void login(String name, String password);
}
public class UserServiceImpl implements UserService {
private UserDAO userDAO = new UserDAOImpl();
@Override
public void register(User user) {
this.userDAO.save(user);
}
@Override
public void login(String name, String password) {
this.userDAO.queryUserByNameAndPassword(name, password);
}
}
单元测试
public class TestSpring {
@Test
public void test1() {
UserService userService = new UserServiceImpl();
// 测试login方法
userService.login("Aomsir", "123456");
// 测试register方法
User user = new User("Aomsir1", "1234567");
userService.register(user);
}
}
简单工厂
以之前的示例为基础,我引入了一个简单的工厂类——BeanFactory。通过工厂方法,这个类负责创建UserService的实现类对象。如此一来,在单元测试中不再直接依赖特定的实现,从而消除了测试和UserServiceImpl之间的紧耦合。然而,值得注意的是,这样的设计将耦合关系转移至了工厂类中
// 工厂类
public class BeanFactory {
public static UserService getUserService() {
return new UserServiceImpl();
}
}
// 单元测试
public class TestSpring {
@Test
public void test1() {
// UserService userService = new UserServiceImpl();
// 使用工厂创建UserService对象
UserService userService = BeanFactory.getUserService();
// 测试login方法
userService.login("Aomsir", "123456");
// 测试register方法
User user = new User("Aomsir1", "1234567");
userService.register(user);
}
}
反射工厂
在前述的BeanFactory设计中,对象的创建是通过构造方法进行硬编码的,这依然导致了一定程度的耦合
例如,在BeanFactory#getUserService()方法中,我直接将实现类的全限定名硬编码在代码里。若需替换实现,我们仍然必须更改Java代码,继而执行重新编译、打包和部署的流程。为了避免这样的问题,我们引入了反射技术结合properties配置文件,以进一步降低工厂类的耦合度。我们将接口及其相关实现类的信息存储在properties文件中,然后让工厂类读取该配置文件。这种方式下,替换实现仅需修改配置文件并重新打包、部署,无需再次编译代码
# Properties配置文件 applicationContext.properties
userService = com.aomsir.basic.service.impl.UserServiceImpl
// 改良后的工厂类
public class BeanFactory {
// 读取资源目录下的文件,将文件内容封装到Properties对象env中
private static Properties env = new Properties();
static {
try {
InputStream inputStream = BeanFactory.class.getClassLoader().getResourceAsStream("applicationContext.properties");
env.load(inputStream);
inputStream.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public static UserService getUserService() {
UserService userService = null;
Class clazz = null;
try {
// clazz = Class.forName("com.aomsir.basic.service.impl.UserServiceImpl");
clazz = Class.forName(env.getProperty("userService"));
userService = (UserService) clazz.newInstance();
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
} catch (InstantiationException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
return userService;
}
}
通用工厂设计
上述描述的是简单工厂设计模式。虽然通过工厂类的方法可以实现某种程度的解耦,但这样的设计在面对大规模需求时显得不够完善。假设有100个对象需要解耦,这就意味着需要创建100个工厂方法。而这100个方法很可能存在大量的重复和冗余内容。结构上也可能出现重复。那么,能否设计出一个更为普适和通用的工厂呢?通过对这些方法中的共同内容进行抽取和整合,我们就可以打造一个这样的通用工厂。以下是具体实现
public class BeanFactory {
// // 读取资源目录下的文件,将文件内容封装到Properties对象env中
private static Properties env = new Properties();
static {
try {
InputStream inputStream = BeanFactory.class.getClassLoader().getResourceAsStream("applicationContext.properties");
env.load(inputStream);
inputStream.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public static Object getBean(String key) {
Object ret = null;
Class clzzz = null;
try {
clzzz = Class.forName(env.getProperty(key));
ret = clzzz.newInstance();
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
} catch (InstantiationException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
return ret;
}
}
结语
上面从最高耦合的代码开始,一步步将项目中代码的耦合度降到最低,从简单工厂到通用工厂,这也是Spring IoC的雏形