一、抽象工厂模式
定义: 为访问类提供一个创建一组相关或相互依赖对象的接口,且访问类无须指定所要产品的具体类就能得到同族的不同等级的产品的模式结构。
应用示例:
1.汽车抽象类&汽车引擎抽象类
public abstract class Car {
protected String brand;
public void drive(){
System.out.println("开着"+brand);
}
}
public abstract class Engine {
String engineName;
public void showEngine(){
System.out.println("使用"+engineName+"引擎");
}
}
2.几种具体品牌的车,去继承Car
public class BenzCar extends Car {
public BenzCar(){
brand="奔驰";
}
}
public class HondaCar extends Car {
public HondaCar(){
brand="本田";
}
}
3.几种具体的车辆发动机,去继承Engine类
public class V8Engine extends Engine{
public V8Engine(){
super.engineName="V8";
}
}
public class V12Engine extends Engine{
public V12Engine(){
super.engineName="V12";
}
}
4.抽象工厂接口
public interface CarFactory {
Car produceCar();
Engine getEngine();
}
5.具体的车辆生产工厂类,实现CarFactory接口
public class BenzCarFactory implements CarFactory{
@Override
public Car produceCar() {
return new BenzCar();
}
@Override
public Engine getEngine() {
return new V12Engine();
}
}
public class HondaCarFactory implements CarFactory{
@Override
public Car produceCar() {
return new HondaCar();
}
@Override
public Engine getEngine() {
return new V8Engine();
}
}
6.客户端代码根据需要选择具体的汽车工厂类来获得汽车对象和发动机对象
public class Main {
public static void main(String[] args) throws Exception {
//选择本田车生产工厂
CarFactory factory=new HondaCarFactory();
//当然也可以选择生产奔驰车
//CarFactory factory=new BenzCarFactory();
//拿到车
Car car = factory.produceCar();
//拿到发动机
Engine engine = factory.getEngine();
//展示发动机
engine.showEngine();
//开车
car.drive();
}
}
客户端代码执行结果:
使用V8引擎
开着本田
以上就是抽象工厂模式的简单应用。
二、以上示例存在什么问题?
我这里只在一处地方实例化了车辆工厂,在实际项目中可能会有几十处甚至几百处使用车辆工厂。假设使用的都是本田车工厂,那么如果说要换成奔驰车工厂就需要将修改所有客户代码中所有实例化工厂的代码,将 CarFactory factory=new HondaCarFactory();
修改为CarFactory factory=new BenzCarFactory();
。非常难受有木有~
三、保证车辆工厂单例?
一个懒汉式的单例
public class MySingleCarFactory {
private static CarFactory factory;
public CarFactory getInstance(){
if(factory==null) {
factory=new HondaCarFactory();
//如果需要将客户代码需要使用奔驰车工厂来替换本田车工厂,只需要修改此处
//factory=new BenzCarFactory();
}
return factory;
}
}
然后客户代码通过以下代码获取factory:
MySingleCarFactory msf=new MySingleCarFactory();
//获取单例的工厂
CarFactory factory = msf.getInstance();
单例模式保证了工厂的唯一性,但是这样的话客户代码就不能自己决定使用哪个factory了。
四、Java反射——从修改代码到修改配置
关于什么是Java反射,这里不多解释。读者可自行查阅。
我们可以对客户代码做以下修改:
public class Main {
public static void main(String[] args) throws Exception {
//字符串的值可以通过xml来配置
final String factoryLocation ="com.codefish.factory.HondaCarFactory";
//选择奔驰车生产工厂(通过反射创建对象)
CarFactory factory=(CarFactory) Class.forName(factoryLocation).newInstance();
//拿到车
Car car = factory.produceCar();
//拿到发动机
Engine engine = factory.getEngine();
engine.showEngine();
car.drive();
}
}
然后可以自定义XML配置文件编写规则,例如:
<!--瞎写的xml配置文件-->
<!--工厂集合-->
<factories>
<!-- 具体某个客户代码对某个具体工厂的引用-->
<factory target="com.codefish.Client1" name="com.codefish.factory.HondaCarFactory"/>
<factory target="com.codefish.Client2" name="com.codefish.factory.BenzCarFactory"/>
<factory target="com.codefish.ClientXX" name="com.codefish.factory.XXCarFactory"/>
...
</factories>
这样如果要替换一个或多个客户代码中的factory类,只要在配置文件里修改即可。不用在每个客户代码文件中修改。
而且还可以根据实际需求来配备多个版本的配置文件。