Spring 与工厂模式

工厂模式优化

先简单回顾一下工厂模式,例如,针对下面 Phone 接口及其实现

1
2
3
public interface Phone {
void powerOn();
}

HWPhone 类

1
2
3
4
5
6
public class HWPhone implements Phone{
@Override
public void powerOn() {
System.out.println("华为");
}
}

现在,假设有一个 Shop 类,负责使用 phone 对象

1
2
3
4
5
6
public class Shop {
public static void main(String[] args) {
Phone phone = new HWPhone();
phone.powerOn();
}
}

现在华为销量不好,商店想要改卖小米,应该怎么办呢?

首先,需要创建一个小米手机类,并实现 Phone 接口

1
2
3
4
5
6
public class XMPhone implements Phone {
@Override
public void powerOn() {
System.out.println("小米");
}
}

然后在商店主方法中修改生产的手机类型为小米

1
2
3
4
5
6
7
public class Shop {
public static void main(String[] args) {
//Phone phone = new HWPhone();
Phone phone = new XMPhone();
phone.powerOn();
}
}

通过以上修改就实现了销售品牌从华为变更为小米,但是如果商店想改卖 Apple 怎么办呢?是不是又要重复以上动作。这样,每次修改销售品类都要频繁修改程序代码,使得程序难以维护。根据以往经验,我们可以将手机的创建过程封装到一个工厂类中,此后修改销售品类只需要更改工厂代码即可

工厂类如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class PhoneFactory {
public static Phone getPhone(String type) {
Phone phone = null;
switch (type) {
case "HW":
phone = new HWPhone();
break;
case "XM":
phone = new XMPhone();
break;
}
return phone;
}
}

修改一下商店代码,使其根据类型从工厂中获取手机:

1
2
3
4
5
6
7
public class Shop {
public static void main(String[] args) {
// 具体类型可以使用其他持久化方式进行配置(数据库、配置文件等)以减少耦合,为演示简单此处进行硬编码
Phone phone = PhoneFactory.getPhone("HW");
phone.powerOn();
}
}

经过上述修改后,后续商店只要修改手机类型即可从工厂中获取对应产品了,而不需要考虑产品是如何创造的。但是又出现了另外一个问题:如果后续新增 Oppo 品牌还是需要修改工厂类,该怎么办呢?

之所以出现上面的问题,是因为上面出现的产品都是通过 new 来创建的,这样就不可避免的出现硬编码问题。那除了使用 new 有没有其他方式呢?还真有,那就是反射

1
Phone phone = (Phone) Class.forName("me.zyp.basic.HWPhone").newInstance();

为了使用反射创建对象,我们需要知道对应类的全类名,该怎么获取呢?

很简单,将全类名使用其他持久化方式进行配置(数据库、配置文件等)即可,这样后续即使增加再多的产品,只需要维护一下其全类名即可

下面是以配置文件为例,进行改造后的工厂

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class PhoneFactory {
public static Properties env;

static {
try {
env = new Properties();
// 加载类定义
InputStream in = BeanFactory.class.getClassLoader().getResourceAsStream("application.properties");
env.load(in);
in.close();
} catch (Exception e) {
e.printStackTrace();
}
}

public static Phone getPhone(String type) throws Exception {
Phone phone = null;
try{
String className = env.getProperty(type);
phone = (Phone) Class.forName(className).newInstance();
}catch (Exception e){
e.printStackTrace();
}
return phone;
}
}

对应配置文件如下:

1
2
HW=me.zyp.basic.HWPhone
XM=me.zyp.basic.XMPhone

商店类如下:

1
2
3
4
5
6
public class Shop {
public static void main(String[] args) {
Phone phone = PhoneFactory.getPhone("HW");
phone.powerOn();
}
}

经过上面改造,产品创建已完成解耦。即使增加产品也不需要更改程序代码了

Spring 中的工厂

Spring 框架就类似于上面的手机工厂,它负责生产和管理应用程序中的各种对象(手机)。我们可以在配置文件中定义 Bean (手机品牌)的属性,例如依赖关系、作用域、生命周期等,然后 Spring 框架(手机工厂 PhoneFactory)会根据这些配置来创建和管理相应的对象