工厂模式有 3 种不同的实现方式,分别是简单工厂模式、工厂方法模式和抽象工厂模式,所以这里将分多篇文章来介绍工厂模式。
工厂模式的定义:
定义一个创建产品对象的工厂接口,将产品对象的实际创建工作推迟到具体子工厂类当中。这满足创建型模式中所要求的“创建与使用相分离”的特点。
现实生活中,原始社会自给自足(没有工厂),农耕社会小作坊(简单工厂,民间酒坊),工业革命流水线(工厂方法,自产自销),现代产业链代工厂(抽象工厂,富士康。我们可以看到工厂的变化趋势,但是对于我们来说,看到是越简单越好,也就是工厂更智能化。
其实凡是需要生成复杂对象的地方,都可以尝试考虑使用工厂模式来代替。
我们把被创建的对象称为“产品”,把创建产品的对象称为“工厂”。如果要创建的产品不多,只要一个工厂类就可以完成,这种模式叫“简单工厂模式”。
简单的说:
简单工厂模式有一个具体的工厂类,可以生成多个不同的产品,属于创建型设计模式
注意:
但是简单工厂模式每增加一个产品就要增加一个具体产品类和一个对应的具体工厂类,这增加了系统的复杂度,违背了开闭原则。
优点:
缺点:
可乐工厂:
现在我们有一个可乐工厂,可以生产可乐:可口可乐、百事可乐、无糖可乐(目前暂定三种)
先定义一个可乐接口:
/**
* 我们有一个可乐工厂
*/
public interface Cola {
//生产可乐的方法
public void cola();
}
定义可口可乐的方法,实现可乐接口,重写方法:
/**
* 可口可乐
*/
public class CocaCola implements Cola{
@Override
public void cola() {
System.out.println("开始生产可口可乐");
}
}
定义百事可乐的方法,实现可乐接口,重写方法:
/**
*
* 百事可乐
*/
public class PepsiCola implements Cola{
@Override
public void cola() {
System.out.println("开始生产百事可乐");
}
}
定义无糖可乐的方法,实现可乐接口,重写方法:
/**
* 五无糖可乐
*/
public class SugarFreeCola implements Cola{
@Override
public void cola() {
System.out.println("开始生产无糖可乐");
}
}
OK,接着我们先按照之前普通的方法实现功能,也就是在模拟客户端(测试类)中建立对象,使用方法的方式来实现:
/**
*普通实现方法
*/
public class Test {
public static void main(String[] args) {
Cola c1 = new CocaCola();
Cola c2 = new PepsiCola();
Cola c3 = new SugarFreeCola();
c1.cola();
c2.cola();
c3.cola();
}
}
输出:
开始生产可口可乐
开始生产百事可乐
开始生产无糖可乐
这样是没有问题的。但是这个方法相当于在测试类中实现了对象并使用了方法,虽然很好的完成了任务,但是,我们的三个实现类和和接口紧密的绑定到了一起,这意味着我们的代码耦合出现严重问题,不利于以后的维护。
方案:
我们可以在客户端和具体实现方法之间添加一个简单工厂:
/**
*可乐工厂
* 在这里可以生产无糖可乐、百事可乐、可口可乐
*/
public class ColaFactory {
//给每个产品定一个编号
public static final int PepsiCola = 1; //百事
public static final int CocaCola = 2; //可口
public static final int SugarFreeCola = 3; //无糖
//通过编号来确定产品对象
public static Cola getCola(int num){
switch (num){
case 1:
return new PepsiCola();
case 2:
return new CocaCola();
case 3:
return new SugarFreeCola();
default:
return null;
}
}
}
测试类:
public class Test2 {
public static void main(String[] args) {
//只要在这个方法捏传入需要对象的参数,即可得到
Cola c = ColaFactory.getCola(3); //参数按需传入
c.cola();
}
}
当传入3时:
开始生产无糖可乐
如果在后期需要添加产品,直接在工厂内添加选择即可,通过方法和参数就可以获取到
其他写法:
我们可以不用预先设置产品的编号,也可以直接输入产品名称:
/**
* 另一种工厂写法
*/
public class ColaFactoryTest {
public static Cola getCola(String name){
if("无糖可乐".equals(name)){
return new SugarFreeCola();
}else if ("百事可乐".equals(name)){
return new PepsiCola();
}else if("可口可乐".equals(name)){
return new CocaCola();
}else {
return null;
}
}
}
测试类:
public class Test3 {
public static void main(String[] args) {
Cola c = ColaFactoryTest.getCola("百事可乐");
c.cola();
}
}
输出:
开始生产百事可乐
这样也是没问题的,大家可以喜欢用自己喜欢的方式即可。
简单的工厂模式就是这样,符合创建与使用相分离的特点,但是缺点也显而易见,就是每次需要增加新产品的时候负责所有产品的创建,职责过重,一旦异常,整个系统将受影响。且工厂类代码会非常臃肿,违背高聚合原则,以及简单工厂模式使用了 static 工厂方法,造成工厂角色无法形成基于继承的等级结构等等。