工廠模式-理解Spring的Bean工廠
接面向對象里面 “老張開車去東北”的場景。鏈接名稱
封裝“老張開車去東北”里面的交通工具,封裝交通工具Car
只給司機一輛車(單例、多例)
順帶講解單例
要求只能有一輛車,別人不能new Car,只有Car自己能控制newCar的邏輯。私有化構造方法,別人就不能new了。
/**
* 交通工具Car
*
*/
public class Car {
//private static Car car = new Car();
private Car(){}
public static Car getInstance(){
return new Car();
}
public void run(){
System.out.println("冒着煙奔跑中...");
}
}
工廠就是自主生產自己的產品,不再依賴於new。比如你想new我家的一個抽屜,你想拿錢就拿錢, 肯定不行。
但是我要給你提供一個方法:getChouTi(); 我就能在get方法里面做各種各樣的限制了。
比如返回Car的getInstance方法,可以做邏輯判斷。
/**
* 交通工具Car
*/
public class Car {
private Car(){}
public static Car getInstance(){
if(有駕照){
return new Car();
}
return null;
}
public void run(){
System.out.println("冒着煙奔跑中...");
}
}
再回到上面的要求,只有一輛車,這么做:自己new一個Car,調用getInstance時候,返回這個Car 。
public class Car {
private static Car car = new Car();
private Car(){}
public static Car getInstance(){
return car;
}
public void run(){
System.out.println("冒着煙奔跑中...");
}
}
測試
getInstance兩次看是不是一輛車:
public static void main(String[] args) {
Car car1 = Car.getInstance();
Car car2 = Car.getInstance();
System.err.println(car1 == car2);
}
打印:true
打印true,說明是一輛車
這個模式叫 單例,又有人叫這個getInstance方法叫靜態工廠方法。
任何方法,里面控制了產生對象的邏輯,都可以叫工廠方法。
多例
如果Car類里面返回的不是一個Car,里面有一個List裝了一堆的Car,getInstance的時候隨機返回一個,這個又有人起了個名字 叫---多例
public class Car {
//private static Car car = new Car();
private static List<Car> cars = new ArrayList<>();
static{
//靜態初始化cars
cars.add(new Car());
cars.add(new Car());
}
private Car(){}
public static Car getInstance(){
//return car;
//隨機返回一個Car,這里就不隨機了
return cars.get(1);
}
public void run(){
System.out.println("冒着煙奔跑中...");
}
}
JDBC連接池,里面裝的Connection,就是多例。
任意定制交通工具的類型和生產過程
自然就想起了多態,抽取一個接口:Moveable,然后讓Car實現Moveable接口:
public interface Moveable {
void run();
}
Car的實現:
public class Car implements Moveable{
@Override
public void run() {
System.out.println("冒着煙奔跑中...");
}
}
飛機的實現:
public class Plane implements Moveable{
@Override
public void run() {
System.out.println("扇着翅膀飛呀飛...");
}
}
測試類:
調用的時候,父類引用指向子類對象,多態,我new誰,就調用的是誰,很隨意就換了交通工具:
Moveable m = new Car();
m.run();
m = new Plane();
m.run();
打印結果:
冒着煙奔跑中...
扇着翅膀飛呀飛...
還有其他任何交通工具 比如交通工具是哈利波特的掃帚,就直接實現Moveable接口,你就可以直接new Broom(); 了 。
現在存在的問題就是,可以任意的new 交通工具,構造方法是公開的。現在想對任意交通工具的生產過程也能夠定制的話。有了上面單例的思路,現在第一個想到的還是,在交通工具類里面寫一個靜態的方法控制new 的過程。這里比如飛機,把產生飛機的過程單獨一個類拿出來,比如叫飛機工廠PlaneFactory:
//飛機工廠類
public class PlaneFactory {
public Plane createPlane(){
//單例、多例、條件檢查自己控制
return new Plane();
}
}
測試 :
//飛機工廠
PlaneFactory factory = new PlaneFactory();
Moveable m = factory.createPlane();
m.run();
打印結果:
扇着翅膀飛呀飛...
如果現在想有一個Car工廠,那么很簡單,就是這樣:
//Car工廠類
public class CarFactory {
public Car createPlane(){
//單例、多例、條件檢查自己控制
return new Car();
}
}
測試代碼:
以前是開着飛機,現在想換成開Car,需要把飛機工廠換成了Car工廠,調用他的createCar方法。這樣太別扭了。
//飛機工廠
//PlaneFactory factory = new PlaneFactory();
//換成Car工廠,整個工廠方法都得換
CarFactory factory = new CarFactory();
Moveable m = factory.createCar();
m.run();
有沒有什么辦法,從飛機換成Car的時候,只換工廠的實現就行呢?自然就想起了多態,有多態就得有父類、子類。所以工廠類需要有一個父類。Factory本來是產生交通工具的,抽象出一個產生交通工具的工廠:
//交通工具工廠
public abstract class VehicleFactory {
//具體生成什么交通工具由子類決定,這里是抽象的。
public abstract Moveable create();
}
這時候讓CarFactory和PlaneFactory去繼承VehicleFactory :
//Car工廠類
public class CarFactory extends VehicleFactory{
@Override
public Moveable create() {
//單例、多例、條件檢查自己控制
return new Car();
}
}
//飛機工廠類
public class PlaneFactory extends VehicleFactory {
@Override
public Moveable create() {
//單例、多例、條件檢查自己控制
return new Plane();
}
}
換了工廠的實現,就可以換交通工具了,比如你加了一個哈利波特的魔法掃帚,需要加一個Broom類實現Moveable接口,和一個BroomFactory工廠類繼承VehicleFactory就可以了。
//掃帚
public class Broom implements Moveable{
@Override
public void run() {
System.out.println("掃帚搖着尾巴呼呼呼...");
}
}
//掃帚工廠類
public class BroomFactory extends VehicleFactory {
@Override
public Moveable create() {
//單例、多例、條件檢查自己控制
return new Broom();
}
}
此時測試代碼就成了這樣子:
//機車工廠,new飛機工廠實例
VehicleFactory factory = new PlaneFactory();
Moveable m = factory.create();
m.run();
//換成Car工廠
factory = new CarFactory();
m = factory.create();
m.run();
//換成掃帚工廠
factory = new BroomFactory();
m = factory.create();
m.run();
打印結果;
扇着翅膀飛呀飛...
冒着煙奔跑中...
掃帚搖着尾巴呼呼呼...
在某一個維度上有了可擴展了,不僅可以控制產生交通工具的類型,還可以控制產生交通工具的生產過程。需要改的只有一個地方:要是需要換交通工具,站在客戶角度,需要改的只有交通工具的工廠,其他地方都不用動。如果用了配置文件的話,代碼都不用該,只改配置文件即可。后面再說。
關於抽象類和接口的選擇:
比如上面的交通工具工廠,是一個抽象類,也可以設計成接口,么問題。
假如這個概念在我們腦子是確確實實存在的,就用抽象類,
假如這個概念只是某些方面的特性:比如會飛的,會跑的,就用接口
假如兩個概念模糊的時候,不知道選擇哪個的時候,就用接口,原因是,從實現了這個接口后,還能從其它的抽象類繼承,更靈活。
抽象工廠
看一下JDK里面,先看getInstance方法,有一大堆,不同類里面有同樣的方法getInstance。
這些大多數都是靜態的工廠方法,是不是單例不一定,得看具體的實現。
各種各樣的Factory也很多。像下面的加密的key,就不適合new,用一個工廠去實現它,產生的時候可以實現各種各種算法,檢測各種資質,new的話構造方法只能是寫死的,用工廠的話,實現一個子類的時候還可以控制生產過程,更靈活。
總而言之,getInstance和Factory,JDK里面很常用。下面開始說抽象工廠。
回到最原始的狀態,我們有一輛車Car。
控制一系列的產品(車、武器、食品補給)
現在讓這個人,開着車,拿着AK47,吃着蘋果。 意思就是,這是一些列的產品,要控制這一些列的產品的生產。
比如你要裝修,海爾整體廚房,有微波爐,油煙機,洗衣機,電磁爐。一系列的產品。
public class Car{
public void run() {
System.out.println("冒着煙奔跑中...");
}
}
public class AK47 {
public void shoot(){
System.out.print("噠噠噠....");
}
}
public class Apple {
public void getName(){
System.out.println("Apple...");
}
}
測試類:
Car car = new Car();
car.run();
AK47 ak = new AK47();
ak.shoot();
Apple apple = new Apple();
apple.getName();
打印:
冒着煙奔跑中...
噠噠噠....
Apple...
產生這一系列的產品,需要有一個默認的工廠:
//默認的工廠
public class DefaultFactory {
public Car createCar(){
return new Car();
}
public AK47 createAK47(){
return new AK47();
}
public Apple createApple(){
return new Apple();
}
}
此時的測試程序,只要new出來一個默認工廠,就可以生產這一系列的產品了:
DefaultFactory factory = new DefaultFactory();
Car car = factory.createCar();
car.run();
AK47 ak = factory.createAK47();
ak.shoot();
Apple apple = factory.createApple();
apple.getName();
打印:
冒着煙奔跑中...
噠噠噠....
Apple...
當需要吧這一系列產品全換掉的話,把這個工廠換掉就可以了:
這里新建一個工廠,魔法工廠:
//哈利波特的魔法工廠
public class MagicFactory {
//交通工具:掃把
public Broom createBroom(){
return new Broom();
}
//武器:魔法棒
public MagicStick createMagicStick(){
return new MagicStick();
}
//食物:毒蘑菇
public MushRoom createMushRoom(){
return new MushRoom();
}
}
//掃帚
public class Broom{
public void run() {
System.out.println("掃帚搖着尾巴呼呼呼...");
}
}
//武器:魔法棒
public class MagicStick {
}
//食物:毒蘑菇
public class MushRoom {
}
但是此時,站在客戶的角度,想把工廠從DefaultFactory換到MagicFactory:
有了之前的經驗,這里自然就聯想到,工廠創建的不能是具體的類,要是一個接口/或者是一個抽象類,比如你的Car,工廠創建的不能是Car,要是Car的父類,這樣在換工廠的時候,下面的代碼才可以不用改動。所以,我們要建一個抽象工廠,然后讓DefaultFactory、MagicFactory都去繼承/實現這個工廠。而且工廠的返回值,都是抽象類或者接口。
//抽象工廠
public abstract class AbstractFactory {
//生產 交通工具
public abstract Vehicle createVehicle();
//生產 武器
public abstract Weapon createWeapon();
//生產食物
public abstract Food createFood();
}
//交通工具
public abstract class Vehicle {
//實現由子類決定
public abstract void run();
}
//食物
public abstract class Food {
public abstract void printName();
}
//武器
public abstract class Weapon {
//
public abstract void shoot();
}
產品類:都繼承產品的抽象類
public class Car extends Vehicle{
@Override
public void run() {
System.out.println("冒着煙奔跑中...");
}
}
//掃帚
public class Broom extends Vehicle{
@Override
public void run() {
System.out.println("掃帚搖着尾巴呼呼呼...");
}
}
//食物:毒蘑菇
public class MushRoom extends Food {
@Override
public void printName() {
System.out.println("mushroom");
}
}
public class Apple extends Food {
@Override
public void printName() {
System.out.println("apple");
}
}
public class AK47 extends Weapon{
public void shoot(){
System.out.println("噠噠噠....");
}
}
//武器:魔法棒
public class MagicStick extends Weapon {
@Override
public void shoot() {
System.out.println("fire hu hu hu ...");
}
}
魔法工廠繼承抽象工廠:
//哈利波特的魔法工廠
public class MagicFactory extends AbstractFactory {
//交通工具:掃把
public Vehicle createVehicle(){
return new Broom();
}
//武器:魔法棒
public Weapon createWeapon(){
return new MagicStick();
}
//食物:毒蘑菇
public Food createFood(){
return new MushRoom();
}
}
DefaultFactory繼承抽象工廠:
//默認的工廠
public class DefaultFactory extends AbstractFactory{
@Override
public Food createFood() {
return new Apple();
}
@Override
public Vehicle createVehicle() {
return new Car();
}
@Override
public Weapon createWeapon() {
return new AK47();
}
}
最終形成的類結構是這樣的:
測試程序:
//換一個工廠,只需要改動這一處,就可以了,換一個工廠,就把生產的系列產品都換了
AbstractFactory factory = new MagicFactory(); //new DefaultFactory();
//換一個工廠
Vehicle vehicle = factory.createVehicle();
vehicle.run();
Weapon weapon = factory.createWeapon();
weapon.shoot();
Food food = factory.createFood();
food.printName();
DefaultFactory打印:
冒着煙奔跑中...
噠噠噠....
apple
**把工廠換為****MagicFactory
AbstractFactory factory = new DefaultFactory();
打印:
掃帚搖着尾巴呼呼呼...
fire hu hu hu ...
mushroom
抽象工廠:生產一系列的產品,如果你想換掉一系列的產品,或者你想在這一系列的產品上進行擴展,以及想對這一些列產品的生成過程進行控制,用抽象工廠。
抽象工廠和普通工廠的優缺點:
普通工廠:
可以在產品的維度上進行擴展,可以產生新的產品,可以產生新的產品的工廠。也就是說可以在產品的維度進行擴展。
在普通工廠想要產生產品系列,就會特別麻煩。產生一個產品,就會出現一個產品的Factory。就會出現”工廠泛濫”。
抽象工廠:
能換工廠,生產新的產品系列,但是不能產生新的產品品種,新添加一個產品,就要在抽象工廠里面加 createXXX(); 方法,所有子類都要實現這個方法。要改動的地方太多。
有沒有一種工廠,結合普通工廠和抽象工廠的有點呢?
既可以隨意添加產品品種,又很方便的添加產品系列? 沒有。
Spring提供了一種方案,Spring的Bean工廠。
Spring說,你就不要這么Moveable m = new Car(); 就行new Car了,你給配置到配置文件里。
測試java讀取properties配置文件反射生成對象
spring.properties:
VehicleType=com.lhy.springfactory.Car
測試代碼:
public static void main(String[] args) throws Exception{
Properties props = new Properties();
props.load(Test.class.getClassLoader().getResourceAsStream("com/lhy/springfactory/spring.properties"));
String vehicleTypeName = props.getProperty("VehicleType");
System.out.println(vehicleTypeName);
//反射生成對象
Object o = Class.forName(vehicleTypeName).newInstance();
Moveable m = (Moveable)o;
m.run();
}
打印結果:
com.lhy.springfactory.Car
冒着煙奔跑中...
把配置文件換成或者Trian:
VehicleType=com.lhy.springfactory.Train
執行測試代碼,打印:
com.lhy.springfactory.Train
小火車嗚嗚嗚...
可以看到,只是改了配置文件,就可以動態控制生成的類了。代碼都不用動。Spring就是這樣的思路。
最簡單的使用Spring:
引入必須的jar包,
Spring要求的配置文件:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<bean id="v" class="com.bjsxt.spring.factory.Train">
</bean>
<!-- //v=com.bjsxt.spring.factory.Car -->
</beans>
測試程序:
package com.bjsxt.spring.factory;
import java.io.IOException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test {
/**
* @param args
* @throws IOException
*/
public static void main(String[] args) throws Exception {
BeanFactory f = new ClassPathXmlApplicationContext("applicationContext.xml");
Object o = f.getBean("v");
Moveable m = (Moveable)o;
m.run();
}
}
配置文件配置的火車類,打印:
小火車嗚嗚嗚...
換成Car,打印:
冒着煙奔跑中...
下面來模擬Spring的Bean工廠
Spring的BeanFactory ,就是一個容器,是用一個map實現的,就是從配置文件讀取 <bean id="v" class="com.bjsxt.spring.factory.Train"/> 這樣的配置,遍歷解析這樣的xml配置,以id為key,class后的類全限定名用反射生成的對象為value,放到這個map中去。當用的時候,直接map.get(id); 獲取這個Bean對象。
ClassPathXmlApplicationContext是BeanFactory的一種實現。這里模擬這種實現。
這里模擬Spring的Bean工廠:
public interface BeanFactory {
Object getBean(String id);
}
ClassPathXmlApplicationContext:
public class ClassPathXmlApplicationContext implements BeanFactory{
//存放一個個Bean對象的容器,
private Map<String, Object> container = new HashMap<String, Object>();
// 構造方法找到配置文件,讀取xml配置文件
public ClassPathXmlApplicationContext(String fileName) throws Exception{
SAXBuilder sb = new SAXBuilder();
Document doc = sb.build(this.getClass().getClassLoader()
.getResourceAsStream(fileName));
Element root = doc.getRootElement();
List list = XPath.selectNodes(root, "/beans/bean");
System.out.println(list.size());
for (int i = 0; i < list.size(); i++) {
Element bean = (Element) list.get(i);
String id = bean.getAttributeValue("id");
String clazz = bean.getAttributeValue("class");
Object o = Class.forName(clazz).newInstance();
container.put(id, o);
System.out.println(id + " " + clazz);
}
}
//讀取配置文件,讀取id為傳進來的id的Bean,實例化
@Override
public Object getBean(String id) {
return container.get(id);
}
}
測試程序:
public static void main(String[] args) throws Exception{
BeanFactory f = new ClassPathXmlApplicationContext("com/lhy/springfactory/applicationContext.xml");
Object o = f.getBean("v");
Moveable m = (Moveable)o;
m.run();
Train trian = (Train)f.getBean("trian");
trian.run();
}
applicationContext.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans>
<bean id="v" class="com.lhy.springfactory.Car"/>
<bean id="trian" class="com.lhy.springfactory.Train"/>
<!-- //v=com.bjsxt.spring.factory.Car -->
</beans>
打印:
2
v com.lhy.springfactory.Car
trian com.lhy.springfactory.Train
冒着煙奔跑中...
小火車嗚嗚嗚...
這樣,就把類配置在了配置文件里,更靈活了!
歡迎關注個人公眾號交流學習: