探究Java 设计模式之工厂模式

版权声明:作者:星云 交流即分享,分享才能进步! https://blog.csdn.net/hadues/article/details/83897660

Java作为一种面向对象的高级语言,程序开发中涉及到很多设计模式。
这篇博文与大家一起探讨下工厂模式。

1. 为什么要用工厂模式?

“Talk is cheap,show me the code”.

想要找到这个问题的答案,我们先来看看下面这个项目。
在这里插入图片描述
项目很简单,一个实体类,一个接口,两个接口实现类。
实体类 User.java

public class User {
	 public String username;
	 public String password;
}

用户接口类 IUser.java

import com.xingyun.model.User;
public interface IUser {
	public void insert(User user);
}

用户接口MySQL实现类IUserMySQLImpl.java

import com.xingyun.interfaces.IUser;
import com.xingyun.model.User;

public class IUserMySQLImpl implements IUser{

	public void insert(User user) {
		// TODO Auto-generated method stub
		System.out.println("insert to MySQL success");
	}
}

Oracle 用户接口实现类IUserOracleImpl.java

import com.xingyun.interfaces.IUser;
import com.xingyun.model.User;

public class IUserOracleImpl implements IUser{

	public void insert(User user) {
		// TODO Auto-generated method stub
		System.out.println("insert to Oracle success");
	}
}

假设当前有调用类Test1.java 想将用户保存到MySQL里,那么一般会这么写

Test1.java

import com.xingyun.interfaces.IUser;
import com.xingyun.interfaces.impl.IUserMySQLImpl;
import com.xingyun.model.User;

public class Test1 {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		IUser iUser=new IUserMySQLImpl();
		
		iUser.insert(new User());
	}
}

输出结果:
在这里插入图片描述
也许你会好奇,为什么不是

 IUserMySQLImpl  iUser=new IUserMySQLImpl();
iUser.insert(new User());

而是使用下面这样呢?

IUser iUser=new IUserMySQLImpl();
iUser.insert(new User());

这正是面向接口编程的好处,这里不做详述,请自行查阅资料了解。

Test2.java 和Test1.java 一样也使用的MySQL的实现类

import com.xingyun.interfaces.IUser;
import com.xingyun.interfaces.impl.IUserMySQLImpl;
import com.xingyun.model.User;

public class Test2 {
	public static void main(String[] args) {
		IUser iUser = new IUserMySQLImpl();
		iUser.insert(new User());
	}
}

执行结果如下:
在这里插入图片描述
有一天突然通知调用者 Test1 和Test2 都要换成Oracle 的实现类,那么我们将不得不修改Test1.java 和Test2.java 中的代码。比如这样:

import com.xingyun.interfaces.IUser;
import com.xingyun.interfaces.impl.IUserMySQLImpl;
import com.xingyun.interfaces.impl.IUserOracleImpl;
import com.xingyun.model.User;

public class Test1 {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		//IUser iUser = new IUserMySQLImpl();
		IUser iUser = new IUserOracleImpl();
		iUser.insert(new User());
	}
}

如果Test1.java Test2.java 比较少还好办,如果有Test3.java,Test4.java 甚至Test100.java ,这么多类都改的话就会非常非常累。
那么有没有什么好的办法呢?
答案是肯定的,它就是我们今天的主角工厂模式。

2 工厂模式实现

2.1 直接编码实现工厂模式

在这里插入图片描述
其他类都不改动,只添加一个新的工厂类,然后改变之前的调用方式。
添加一个新类工厂类UserFactory.java

import com.xingyun.interfaces.IUser;
import com.xingyun.interfaces.impl.IUserMySQLImpl;
public class UserFactory {
	public static IUser getIUserImpl() {
		return new IUserMySQLImpl();
	}
}

然后在Test1.java 和Test2.java 中调用的时候就可以这么写

import com.xingyun.factory.UserFactory;
import com.xingyun.interfaces.IUser;
import com.xingyun.interfaces.impl.IUserMySQLImpl;
import com.xingyun.interfaces.impl.IUserOracleImpl;
import com.xingyun.model.User;
public class Test1 {
				public static void main(String[] args) {
					// TODO Auto-generated method stub
					IUser iUser = UserFactory.getIUserImpl();
					iUser.insert(new User());
				}
}

在这里插入图片描述
Test2 也这么写

import com.xingyun.factory.UserFactory;
import com.xingyun.interfaces.IUser;
import com.xingyun.interfaces.impl.IUserOracleImpl;
import com.xingyun.model.User;

public class Test2 {
	public static void main(String[] args) {
		IUser iUser = UserFactory.getIUserImpl();
		iUser.insert(new User());
	}
}

执行结果:
在这里插入图片描述
有一天突然通知要改用Oracle实现类,那么我们Test1.java 和Test2.java 不需要做任何更改。只需要更改我们的工厂类就可以了。

import com.xingyun.interfaces.IUser;
import com.xingyun.interfaces.impl.IUserOracleImpl;
public class UserFactory {
	public static IUser getIUserImpl() {
		//return new IUserMySQLImpl();
		return new IUserOracleImpl();
	}
}

总结:工厂模式通过类的静态方法返回实例的方式,屏蔽掉了接口具体的实现类,在工厂类中管理统一管理使用哪个实现类。调用类Test1.java Test2.java 。。。Test100.java 都不需要做任何代码改动,这就是Java传说中的工厂模式。
需要注意的是,这种方式需要满足这个需求背景:
当整个项目中要么全部使用A实现类要么使用B实现类的时候最适合用。
如果项目中有的用A实现类有的用B实现类,那么工厂模式就不适合这种场景

2.1 Spring Factory Bean

值得注意的是,如果想用Spring实现工厂模式, Spring并不会直接利用反射机制创建bean对象,而是会利用反射机制先找到Factory类,然后利用Factory再去生成bean对象。
Factory Mothod方式分两种:

  • 静态工厂方法
  • 实例工厂方法

2.1.1 Spring 之静态工厂方法

所谓静态工厂方式就是指Factory类不本身不需要实例化, 这个Factory类中提供了1个静态方法来生成bean对象。
在这里插入图片描述
正如你看到的,其他类依然不变。
这次是Maven项目,我们需要一些依赖,Spring核心Jar 包和一个日志包。
pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.xingyun</groupId>
	<artifactId>SpringStaticFactoryPatternSample</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<description>Spring 通过实例工厂来指定使用哪个接口实现类</description>

	<dependencies>
		<!-- https://mvnrepository.com/artifact/commons-logging/commons-logging -->
		<dependency>
			<groupId>commons-logging</groupId>
			<artifactId>commons-logging</artifactId>
			<version>1.2</version>
		</dependency>

		<!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context</artifactId>
			<version>5.1.2.RELEASE</version>
		</dependency>

		<!-- https://mvnrepository.com/artifact/org.springframework/spring-core -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-core</artifactId>
			<version>5.1.2.RELEASE</version>
		</dependency>

		<!-- https://mvnrepository.com/artifact/org.springframework/spring-beans -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-beans</artifactId>
			<version>5.1.2.RELEASE</version>
		</dependency>
	</dependencies>
</project>

我们需要修改下工厂类代码和添加一个xml 配置文件。
MyIUserFactory.java

import com.xingyun.interfaces.IUser;
import com.xingyun.interfaces.impl.IUserMySQLImpl;
import com.xingyun.interfaces.impl.IUserOracleImpl;

public class MyIUserFactory {

	/***构造方法注入 ******/
	private static IUser getIUserImpl(String dbType) {
		switch (dbType) {
		case "userMySQLImpl":
			return new IUserMySQLImpl();
		case "userOracleImpl":
			return new IUserOracleImpl();
		default:
			return new IUserMySQLImpl();
		}
	}
}

配置文件里面我们需要改变下:
beans.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING/DTD BEAN/EN" 
"http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<!-- 通过静态工厂方法 -->
<!-- bean id  class="工厂类"  factory-method="工厂方法" -->
<bean id="iUser" class="com.xingyun.factory.MyIUserFactory" factory-method="getIUserImpl">
   <!-- 静态构造方法中构造参数  -->
   <constructor-arg value="userMySQLImpl"></constructor-arg>
   <!-- default is userMySQLImpl,optional:userMySQLImpl,userOracleImpl -->
</bean>     
</beans>

以后需求一旦变更,Test1.java 和Test2.java 不用改变,我们只需要修改这个配置文件即可。如果做了配置会根据配置指定到底使用哪个实现类,如果不配置默认会使用MySQL实现类。
将MySQL接口实现类切换到Oracle接口实现类就这么做,

 <constructor-arg value="userMySQLImpl"></constructor-arg>

改成如下即可

 <constructor-arg value="userOracleImpl"></constructor-arg>

调用方式也需要做下改变:
Test1.java

import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource;

import com.xingyun.interfaces.IUser;
import com.xingyun.model.User;

public class Test1 {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
        Resource resource=new FileSystemResource("src/main/resources/beans.xml");
		
		BeanFactory beanFactory=new XmlBeanFactory(resource);
		
		IUser iUser=(IUser)beanFactory.getBean("iUser");
		
		iUser.insert(new User());
	}
}

执行后:
在这里插入图片描述

2.1.1 Spring 之实例工厂模式

在这里插入图片描述
和刚才略有不同,工厂类做了改动,xml配置文件做了改动。其他都不用动。
pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.xingyun</groupId>
	<artifactId>SpringStaticFactoryPatternSample</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<description>Spring 静态工厂方法</description>

	<dependencies>
		<!-- https://mvnrepository.com/artifact/commons-logging/commons-logging -->
		<dependency>
			<groupId>commons-logging</groupId>
			<artifactId>commons-logging</artifactId>
			<version>1.2</version>
		</dependency>

		<!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context</artifactId>
			<version>5.1.2.RELEASE</version>
		</dependency>

		<!-- https://mvnrepository.com/artifact/org.springframework/spring-core -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-core</artifactId>
			<version>5.1.2.RELEASE</version>
		</dependency>

		<!-- https://mvnrepository.com/artifact/org.springframework/spring-beans -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-beans</artifactId>
			<version>5.1.2.RELEASE</version>
		</dependency>
	</dependencies>
</project>

MyIUserFactory.java

import com.xingyun.interfaces.IUser;
import com.xingyun.interfaces.impl.IUserMySQLImpl;
import com.xingyun.interfaces.impl.IUserOracleImpl;

public class MyIUserFactory {

	private IUser getIUserImpl(String dbType) {
		switch (dbType) {
		case "userMySQLImpl":
			return new IUserMySQLImpl();
		case "userOracleImpl":
			return new IUserOracleImpl();
		default:
			return new IUserMySQLImpl();
		}
	}
}

beans.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING/DTD BEAN/EN" 
"http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
	<bean id="iUserFactory" class="com.xingyun.factory.MyIUserFactory" >
	</bean>
	<!-- bean id class="工厂类" factory-method="工厂方法" -->
	<bean id="iUser" factory-bean="iUserFactory" factory-method="getIUserImpl">
		<constructor-arg value="userMySQLImpl" /> 
		<!-- default is userMySQLImpl,optional:userMySQLImpl,userOracleImpl -->
	</bean>
</beans>

今后一旦需求改动,只需要改配置文件,其他全部不用改了。

2.1 Spring Bean Factory

Spring 的IOC容器提供了一个最强大的Bean 工厂,因此如果针对上述需求,你需要不同调用类调用不同的实现的话,那么也可以这么做。
在这里插入图片描述
其他类都不变,只修改beans.xml 和调用方式
pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.xingyun</groupId>
	<artifactId>SpringBeanFactoryPatternSample</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<description>Spring Bean工厂 案例源码</description>

	<dependencies>
		<!-- https://mvnrepository.com/artifact/commons-logging/commons-logging -->
		<dependency>
			<groupId>commons-logging</groupId>
			<artifactId>commons-logging</artifactId>
			<version>1.2</version>
		</dependency>

		<!-- Spring 核心Jar包 -->
		<!-- https://mvnrepository.com/artifact/org.springframework/spring-core -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-core</artifactId>
			<version>5.1.2.RELEASE</version>
		</dependency>
		<!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context</artifactId>
			<version>5.1.2.RELEASE</version>
		</dependency>
		<!-- https://mvnrepository.com/artifact/org.springframework/spring-beans -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-beans</artifactId>
			<version>5.1.2.RELEASE</version>
		</dependency>
	</dependencies>
</project>

Spring3.1以后XmlBeanFactory已经废弃,即下面这段代码已经废弃
Resource resource = new ClassPathResource(“applicationContext.xml”); //装载配置文件
BeanFactory factory = new XmlBeanFactory(resource);

新的替代调用方式有两种:
第一种:当调用getBean()的时候才会配置文件中的bean才会实例化

import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionReader;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource;

import com.xingyun.interfaces.IUser;
import com.xingyun.model.User;

public class Test1 {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
        Resource resource=new FileSystemResource("src/main/resources/beans.xml");
        BeanFactory fa=new DefaultListableBeanFactory();  
		BeanDefinitionReader bdr=new XmlBeanDefinitionReader((BeanDefinitionRegistry) fa);  
		bdr.loadBeanDefinitions(resource);
		
		IUser iUser=(IUser)fa.getBean("iUserMySQLImpl");
		
		iUser.insert(new User());
		
	}
}

执行结果:
在这里插入图片描述
第二种:读取XMl配置文件的时候配置文件中所有的bean 就全部实例化

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.xingyun.interfaces.IUser;
import com.xingyun.model.User;

public class Test2 {
	public static void main(String[] args) {
		
		ApplicationContext fa=new ClassPathXmlApplicationContext("beans.xml");
			
		IUser iUser=(IUser)fa.getBean("iUserOracleImpl");
			
		iUser.insert(new User());
	}
}

执行结果:
在这里插入图片描述
本篇完~
源码下周一上传更新~

猜你喜欢

转载自blog.csdn.net/hadues/article/details/83897660