java设计模式之—工厂模式Spring Factory (三套方案:步步深入,模拟Spring工厂),涵盖JDOM对XML解析

一、目的:为你揭开Factory的神秘面纱,其实很简单。

本案例适用人群:

1、对面向对象有深入了解。

2、接口、抽象类有所了解。

3、对反射了解。

可以帮你:

1、爱上工厂(只用关注自己的业务,生产新类、给类创建新方法,通过配置文件xml通知Spring就可以了(读取XML文件,动态调用想要的组合))。

2、学会读取配置文件。

3 、学会解析XML文件。

注:如果你时间有限,请跳过前4个步骤,直接看五。(六是对五的延伸,提供读取xml)

二、工厂模式的前世今生

举例说明:有一个Person

1、他可以有很多交通工具(Vehicle),都有一个Run的方法:Car、Tank、Train、Plane、Broom等等。

2、他也可以吃很多食物(Food),都有一个PrintFoodName的方法:Orange、Apple、MushRoom等等。

3、他还可以有很多武器(Weapon)都有一个Shoot的方法:Gun、MagicStick、等等。

要求:

Person 在用驾驶一个Vehicle的时候、同时拿着Weapon进行射击、同时嘴里还吃着Food。

而且,还不停的交换着交通工具、武器、食物。

实现代码的时候,是不是很痛苦???

初步解决方案:

1、给所有武器,都实现Vehicle接口,统一方法run();

2、给所有食物,都统一实现Food接口,统一方法:PrintFoodName();

3、给所有武器,都统一实现Weapon接口,统一方法:shoot();

4、提供:VehicleFactory 只生产武器、FoodFactory只生产食物、WeaponFactory只生产武器。所有的武器,在对应的工厂都能被 getInstance()到。

     每发明一种新的武器、食物、交通工具,比如:游艇等,对应工厂就要添加新的生产游艇的方法。食物工厂要新增面包:对应工厂就需要,新增生产面包的方法。

弊端:

1、person 在不停的交换武器使用、不停的更换交通工具、不停的吃食物。此时的工厂类的代码,要不停的改来改去。

        //第一次(不采用工厂):     
        Moveable car=new Car1();
        car.run();
        Weapon gun=new Gun();
        gun.shot();
        Food food=new Apple();
        Apple.printFoodName();
        
        //第二次(不采用工厂):
        Moveable tank=new Tank();
        tank.run();
        Weapon stick=new MagicStick();
        stick.shot();
        Food orangle=new Orangle();
        orangle.printFoodName();

       //第三次(采用工厂,每次新增新的类,对应工厂类都要改动):
        Moveable plane=VehicleFactory.getPlaneInstance();
        plane.run();
        Weapon stick=WeaponFactory.getMagicStickInstance();
        stick.shot();
        Food bread=FoodFactory.getBreadInstance();
        bread.printFoodName();

三、工厂模式的雏形

1、通过案例一,发现需求总是变动,代码写起来不灵活。

2、我们能否创建一个工厂呢,穷举所有可能的组合方式(类似于酷狗或其他软件的一键换肤,提供多种皮肤)?至少至少有一个默认的DefaultFactory 生成固定的Vehicle、Weapon、Food。MagicFactory,统一生产魔法交通工具、食物、武器……

这样,我们只需要直接操作工厂就可以了,每切换一个工厂,工厂自动帮我们生成新的组合方式。

弊端:工厂类太多,也很繁琐。

代码如下:

//1、定义抽象类 工厂
public abstract class AbstractFactory {
	public abstract Vehicle creatVehicle();
	public abstract Weapon creatWeapon();
	public abstract Food creatFood();
}

//2、提供默认工厂
public class DefaultFactory extends AbstractFactory{

	@Override
	public Vehicle creatVehicle() {
		return new Car();
	}

	@Override
	public Weapon creatWeapon() {
		return new AK47();
	}

	@Override
	public Food creatFood() {
		return new Apple();
	}

}

//3、再提供一个魔法工厂
public class MagicFactory extends AbstractFactory {

	@Override
	public Vehicle creatVehicle() {
		return new Broom();//返回扫帚
	}

	@Override
	public Weapon creatWeapon() {
		return new MagicStick();//返回魔法棒
	}

	@Override
	public Food creatFood() {
		return new MushRoom();//返回蘑菇
	}

}

     //测试类:main方法,进行调用
        Moveable broom=MagicFactory.getBroom();
		broom.run();
		Weapon stick=MagicFactory.getMagicStick();
		mush.shot();
		Food orangle=MagicFactory.getMushroom();
		mush.printFoodName();

四、工厂模式的转机(从配置文件读取)

1、新建:springFactory.properties,每次变更组合时,只需要修改配置文件(后半部分)。不新增类(武器、交通工具、食物)的话,不需要修改java代码

内容如下:

VehicleType=com.xp.test3.springFactory.Car
FoodType=com.xp.test3.springFactory.Bread
WeaponType=com.xp.test3.springFactory.Gun

读取配置文件代码如下:

	Properties properties=new Properties();
		properties.load(new Test().getClass().getResourceAsStream("/com/xp/test3/springFactory/springFactory.properties"));//读取配置文件中的内容
		String vName=properties.getProperty("VehicleType");
		String vFood=properties.getProperty("FoodType");
		String vWeapon=properties.getProperty("WeaponType");
		Moveable vehicle=(Moveable)Class.forName(vName).newInstance();//用反射生成实体类
		Food food=(Food)Class.forName(vName).newInstance();
		Weapon weapon=(Weapon)Class.forName(vName).newInstance();
		vehicle.run();
		food.printName();
		weapon.shoot();
	}

注:读到这里,是不是放松些了。美滋滋,代码几乎不需要改动,每次修改properties配置文件,就可以得到自己想要的组合了。

五、工厂模式的归宿(Spring自带)

原理:你把自己的JavaBean 类,配置到Spring配置文件中。 在Java代码中,通过BeanFactory管理实例管理你的Bean。需要哪个,顺手直接取。

注:为了让你的代码能正常运行,本案例采用最精简模式。尽可能少的创建类(核心代码就一行:ClassPathXmlApplicationContext)。

1、准备工作,新建xml文件。

工程根目录,不是Src下,新建applicationContext.xml,内容如下(代码已做精简,需要其他Bean的自行添加):

<?xml version="1.0" encoding="UTF-8"?>

<beans>
	<bean id="car" class="com.xp.test3.springFactory.Car"></bean>
	<bean id="train" class="com.xp.test3.springFactory.Train"></bean>
</beans>

2、测试案例

//1、声明交通工具接口
public interface Moveable {
	void run();
}

//2、创建交通工具的实现类Car
public class Train implements Moveable {
	@Override
	public void run() {
		System.out.println(" wu  wu  wu  火车来了");
	}
}
//3、测试:
package com.xp.test3.springFactory;

import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test3 {
	
	public static void main(String[] args) throws Exception, InstantiationException, IllegalAccessException, ClassNotFoundException {
		
		BeanFactory factory=new ClassPathXmlApplicationContext("applicationContext.xml");//使用Spring自带的Bean工厂,读取工程跟目录XML
		//Moveable moveable=(Moveable)factory.getBean("car");;
		Moveable moveable=(Moveable)factory.getBean("train");//所有的交通工具,都实现了moveable接口
		moveable.run();
	}

}

运行结果:
 wu  wu  wu  火车来了

六、延伸:模拟Spring工厂模式

小节:以上方法,之所以如此短小精悍,是spring帮我们自动管理了bean,并通过ClassPathXmlApplicationContext类,帮我们生成新的bean。

原理:ClassPathXmlApplicationContext里有个map容器,在加载这个类的时候,Spring会把对应xml中的bean都从内存中load到这个map中,你就可以随用随取了。

具体实现如下:

//1、创建BeanFactory(模拟Spring的BeanFactory)
public interface BeanFactory {
	Object getBean(String key);
}


//2、创建(模拟Spring的),需要自己导入读取XML的Jdom的jar包。
package com.xp.test3.springFactory;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.input.SAXBuilder;

public class ClassPathXmlApplicationContext implements BeanFactory {
	
	Map<String,Object> container=new HashMap<String,Object>();//存放读取Xml文件中对象的容器
	
	@Override
	public Object getBean(String key) {
		return container.get(key);
	}
	
	//默认构造方法
	public ClassPathXmlApplicationContext(String xmlFileName) throws Exception{
		SAXBuilder sb = new SAXBuilder();//获取文档解析器
		Document doc = sb.build(xmlFileName);//写法1:默认是从项目根目录找(不是项目下的src,是项目根目录)
		//Document doc = sb.build(this.getClass().getClassLoader().getResourceAsStream(xmlFileName));//写法2:读取某个java类同级目录下的文件
		Element root = doc.getRootElement();
		List<Element> list = root.getChildren("bean");//写法1:jdom自带的(建议)
		//List<Element> list =  (List<Element>) XPath.selectNodes(root, "/beans/bean");//写法2:xpath自带的(不建议,老版本过时了)
		for (Element element : list) {
			String id = element.getAttributeValue("id");//获取XML中单个bean节点的ID
			String className = element.getAttributeValue("class");//获取XML中单个bean节点的Class字符串
			container.put(id, Class.forName(className).newInstance());//通过反射转换为对象,存到BeanMap中
		}
	}
}

//3、用自己模拟类进行测试。
package com.xp.test3.springFactory;

import java.util.Properties;

import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.xp.test2.Food;
import com.xp.test2.Test;
import com.xp.test2.Weapon;

public class Test3 {
	
	//纯手工,用自己写的ClassPathXmlApplicationContext进行测试
	public static void myselfSpring()throws Exception {
        //这里两个类都要引用自己创建的,因为和Spring的类同名,不要引用错了。
		com.xp.test3.springFactory.BeanFactory factory=new com.xp.test3.springFactory.ClassPathXmlApplicationContext("applicationContext.xml");
		//Moveable moveable=(Moveable)factory.getBean("car");
		Moveable moveable=(Moveable)factory.getBean("train");
		moveable.run();
	}
	
	public static void main(String[] args) throws Exception, InstantiationException, IllegalAccessException, ClassNotFoundException {
		myselfSpring();
	}

}

运行结果:

 wu  wu  wu  火车来了

结语:

1、至此,你会发现,Spring的工厂模式,如此简单。配置,调用就完了。

2、XML解析需要导入Jar包,找不到Jdom的jar包,或者需要进一步了解的,请参考:XML解析,简单易学

3、爱学习的你,我们一起强大,欢迎吐槽,交流。

  • 5
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值