Spring IOC中使用的设计模式(二) 工厂模式
问题引入
问题:现在有一家工厂,生产电视和空调等,平常的做法是,创建一个生产电视和一个生产空调的类。
public class ProductTv {
ProductTv(){
System.out.println("生产电视");
}
}
/**
* Created on 2018/5/27
*
* @author wang.teng
*/
public class ProductRefrigerator {
ProductRefrigerator(){
System.out.println("生产冰箱");
}
}
在我们使用的时候:
/**
* Created on 2018/5/27
*
* @author wang.teng
*/
public class Test {
public static void main(String[] args) {
//生产电视
ProductTv productTv = new ProductTv();
//生产冰箱
ProductRefrigerator productRefrigerator = new ProductRefrigerator();
}
}
大家会发现这么说耦合性太高,工厂和电器紧密的耦合在一起了。那么如何分离使用者和对象的分离呢?
一、 什么是工厂模式
专门负责将大量有共同接口的类的实例化。工厂模式可以动态的决定将哪个类进行实例化。
二、为什么要使用工厂模式
让使用者和对象的创建分离。减少修改,拓展比较好。
三、工厂模式的几种形态
简单工厂模式
角色
- 工厂类角色:核心,与应用逻辑紧密相关。直接调用创建产品对象
- 抽象产品:往往是一个接口或者抽象类,
- 具体产品:所有角色的实例控制类。
举例说明
根据上面的例子进行改造,讲生产作为一个接口抽象出来一个产品
- 产品接口:
public interface Product { }
产品实现
public class Tv implements Product { public Tv(){ System.out.println("电视被制造了"); } }
public class Refrigerator implements Product { public Refrigerator(){ System.out.println("冰箱被制造了"); } }
工厂类:
- 判断传入的key
public class ProductFactory { public static Product produce(String productName) throws Exception { switch (productName) { case "tv": return new Tv(); case "refrigerator": return new Refrigerator(); default: throw new Exception("没有该产品"); } } }
配置文件模式
新增配置文件:
tv=design.pattern.Tv refrigerator=design.pattern.Refrigerator
读取配置到map中:
public class PropertyResourceReader { public static Map<String, String> map = new HashMap<>(); public Map<String, String> readPropertyFile(String fileName) { Properties pro = new Properties(); InputStream in = getClass().getResourceAsStream(fileName); try { pro.load(in); Iterator<String> iterator = pro.stringPropertyNames().iterator(); while (iterator.hasNext()) { String key = iterator.next(); String value = pro.getProperty(key); map.put(key, value); } in.close(); } catch (IOException e) { e.printStackTrace(); } return map; } }
工厂类:
public class ProductFactory { public static Product produce(String key) throws Exception { PropertyResourceReader reader = new PropertyResourceReader(); Map<String, String> map = reader.readPropertyFile("product.properties"); try { Product product = (Product) Class.forName(map.get(key)).newInstance(); return product; } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } throw new Exception("没有该产品"); } }
这个处理是不是很熟悉,没错,spring就是采用这个方式来进行实例bean的,spring中的spring-bean.xml相当与product,peoperties,spring中的XmlBeanDefinitionReader相当于此处的PropertyResourceReader,同样是将配置文件的内容读取到map中。
优点
提高代码的灵活度,将实例化对象的操作集中在工厂类中处理
缺点
每添加一个对象就需要对工厂类或者对配置文件进行修改,违背了不修改代码的原则。
工厂方法模式
定义创建对象的接口,让子类去决定实例化类,工厂方法模式是一个类实例化延迟到子类。
示例:
定义一个工厂接口:
public interface Factory { public Product produce(); }
再定义一个产品接口
public interface Product{}
再定义子类来实现产品类,有子类来具体实现:
public class Tv implements Product { public Tv() { System.out.println("电视被制造了"); } }
public class Refrigerator implements Product { public Refrigerator(){ System.out.println("冰箱被制造了"); } }
工厂类:
public class TvFactory implements Factory { @Override public Product produce() { return new Tv(); } }
public class RefrigeratorFactory implements Factory { @Override public Product produce() { return new Refrigerator(); } }
- 优点
每种产品由一种工厂来创建,一个工厂保存一个new 基本完美,完全遵循 “不改代码”的原则,拓展性比较好
- 缺点
回出现成对的产品与工厂出现,新增一个产品将会对应的多出一个工厂。
抽象工厂模式
抽象工厂模式提供一个创建一系列或者相互依赖的对象接口,而不需要指定他们具体的类。
实例
定义两个接口进行分类
public interface Refrigerator {
}
public interface Tv {
}
再定义电视和冰箱的具体产品分类:
public class Meidi implements Refrigerator {
public Meidi(){
System.out.println("美的空调生产出来了");
}
}
public class Hair implements Refrigerator {
public Hair(){
System.out.println("海尔空调生产出来了");
}
}
public class LeTv implements Tv {
public LeTv() {
System.out.println("乐视电视被生产出来了");
}
}
public class Sony implements Tv {
public Sony(){
System.out.println("索尼电视机被生产出来了");
}
}
接下来定义工厂行为接口:
public interface Factory {
public Tv produceTv();
public Car produceCar();
}
具体工厂类
public class FactoryA implements Factory {
@Override
public Tv produceTv() {
return new LeTv();
}
@Override
public Refrigerator produceRefrigerator() {
return new Hair();
}
}
public class FactoryB implements Factory {
@Override
public Tv produceTv() {
return new Sony();
}
@Override
public Refrigerator produceRefrigerator() {
return new Meidi();
}
}
测试方法:
public class Test {
public static void main(String[] args) {
FactoryA factoryA = new FactoryA();
factoryA.produceRefrigerator();
FactoryB factoryB = new FactoryB();
factoryB.produceTv();
}
}
优点:
分类比较清晰,能保证客户端始终只使用一个产品族中的对象
缺点:
添加新产品的时候,需要修改工厂行为类,拓展比较差。
四、Spring IOC中使用工厂模式示例
上面也提到了Spring IOC
在解析配置文件的时候有用到工过工厂模(解析class核心是反射),下面具体举例说明一下,在解析spring的会有一个抽象类AbstractBeanFactory
先看下部分代码:
public abstract class AbstractFactoryBean<T>
implements FactoryBean<T>, BeanClassLoaderAware, BeanFactoryAware, InitializingBean, DisposableBean {
// 定义了获取对象的前置判断工作,创建对象的工作则交给了一个抽象方法
// 这里判断了Bean是不是单例并且是否已经被加载过了(未初始化但加载过了,这个问题涉及到Spring处理循环依赖,以后会讨论到)
public final T getObject() throws Exception {
return this.isSingleton()?(this.initialized?this.singletonInstance:this.getEarlySingletonInstance()):this.createInstance();
}
// 由子类负责具体创建对象
protected abstract T createInstance() throws Exception;
}
再看下AbstractFactoryBean
类图:
Bean工厂有很多种,每个bean负责创建不同的Bean,比如可以让Bean拥有访问Spring容器的能力的工厂类
BeanFactoryAware
,创建比较负责的bean的工厂类BeanFactory
。虽然子类可以自由创建bean,但是创建bean之前和之后对bean的处理,子类不需要关心,也没权利。就是他给了子类自由创建实例,但是又严格控制了实例创建前后的业务流程。