从一个例子开始介绍工厂模式。
假设有一个生产Windows系统兼容机和Mac电脑的工厂,里面有一个装配员,当客户需要一台电脑时,告诉装配员是需要Windows系统电脑还是Mac系统电脑,工厂就会生产一台电脑并且交由装配员装配,然后交给客户。
首先需要一个电脑的接口:
public interface Computer {
public void selfIntroduce(); // 打印电脑信息
}
Windows电脑的实现类:
public class Windows implements Computer {
@Override
public void selfIntroduce() {
System.out.println("Windows已生产.");
}
}
Mac电脑的实现类:
public class Mac implements Computer {
@Override
public void selfIntroduce() {
System.out.println("Mac已生产.");
}
}
装配员实现类:
public class Boxer {
public Computer boxComputer(String computer) {
if(computer.equals("Mac")) {
return new Mac();
} else {
return new Windows();
}
}
}
测试类:
public class Main {
public static void main(String[] args) {
Boxer boxer = new Boxer();
Computer computer1 = boxer.boxComputer("Mac"); // 客户要求生产一台Mac系统电脑
computer1.selfIntroduce();
Computer computer2 = boxer.boxComputer("Windows"); // 客户要求生产一台Windows系统电脑
computer2.selfIntroduce();
}
}
可以看到输出:
Mac已生产.
Windows已生产.
这就是简单工厂模式,客户希望得到哪台类型的电脑,只需要告诉装配员,工厂就会生产一台对应的电脑出来,用法很简单。但是简单工厂模式不符合开闭原则(对扩展开放,对修改关闭),当用户希望得到一台linux电脑时,需要增加Linux电脑实现类:public class Linux implements Computer {
@Override
public void selfIntroduce() {
System.out.println("Linux已生产.");
}
}
再增加装配员的功能:
public class Boxer {
public Computer boxComputer(String computer) {
if(computer.equals("Mac")) {
return new Mac();
} else if(computer.equals("Linux")) {
return new Linux();
} else {
return new Windows();
}
}
}
测试:
public class Main {
public static void main(String[] args) {
Boxer boxer = new Boxer();
Computer computer1 = boxer.boxComputer("Mac");
computer1.selfIntroduce();
Computer computer2 = boxer.boxComputer("Windows");
computer2.selfIntroduce();
Computer computer3 = boxer.boxComputer("Linux"); // 客户要求再生产一台Linux系统电脑
computer3.selfIntroduce();
}
}
输出:
Mac已生产.
Windows已生产.
Linux已生产.
如果还有更多系统类型的电脑,如Ubuntu、RedHat、朝鲜的红星,如果再细一点,Windows需要生产Win98、Win7、Win10等等,这样,需要装配员实现类代码会急剧增加,严重违反开闭原则。如果继续采用上面的方式,整个需求的实现将是个噩梦。要实现用户的新需求,整个工厂方法应该如何来写呢?
这里,我们采用抽象工厂模式。
抽象工厂模式与简单工厂模式最大的区别在于,抽象工厂模式将产生产品类和角色类,同一个系列的产品将由对应的产品类和角色类组成的工厂来生产,也就是说抽象工厂模式是由许多个工厂流水线组成。假如客户需要Windows系统电脑,工厂j就会交由Windows装配员来完成并将产品交给客户,这就是抽象工厂模式。
开始完成客户的新需求:
装配员的接口:
public interface Boxer {
public Computer assembledComputer();
}
Windows电脑装配员实现类:
public class WindowsBoxer implements Boxer {
@Override
public Computer assembledComputer() {
return new Windows();
}
}
Mac电脑装配员实现类:
public class MacBoxer implements Boxer {
@Override
public Computer assembledComputer() {
return new Mac();
}
}
Linux电脑装配员实现类:
public class LinuxBoxer implements Boxer {
@Override
public Computer assembledComputer() {
return new Linux();
}
}
测试:
public class Main {
public static void main(String[] args) {
Boxer boxer1 = new WindowsBoxer();
Computer computer1 = boxer1.assembledComputer();
computer1.selfIntroduce();
Boxer boxer2 = new MacBoxer();
Computer computer2 = boxer2.assembledComputer();
computer2.selfIntroduce();
Boxer boxer3 = new LinuxBoxer();
Computer computer3 = boxer3.assembledComputer();
computer3.selfIntroduce();
}
}
输出:
Windows已生产.
Mac已生产.
Linux已生产.
可以看到,采用抽象工厂模式后,整个需求实现的代码量增大了,但是结构上更加清晰,并且符合开闭原则。假如客户又有新的需求,需要的电脑包装里面装好电源适配器,那我们可以继续用抽象工厂模式的方式来扩展。
增加电源适配器接口:
public interface Adapter {
public void assembled();
}
Windows电源适配器实现类:
public class WindowsAdapter implements Adapter {
@Override
public void assembled() {
System.out.println("Windows电源适配器已装配.");
}
}
Mac和Linux电源适配器同理,此处略。
修改Windows实现类:
public class Windows implements Computer {
private Adapter windowsAdapter;
public Windows(Adapter windowsAdapter) {
this.windowsAdapter = windowsAdapter;
}
@Override
public void selfIntroduce() {
System.out.println("Windows已生产.");
windowsAdapter.assembled();
}
}
同理修改Mac和Linux实现类。
测试:
public class Main {
public static void main(String[] args) {
Boxer boxer1 = new WindowsBoxer();
Computer computer1 = boxer1.assembledComputer();
computer1.selfIntroduce();
Boxer boxer2 = new MacBoxer();
Computer computer2 = boxer2.assembledComputer();
computer2.selfIntroduce();
Boxer boxer3 = new LinuxBoxer();
Computer computer3 = boxer3.assembledComputer();
computer3.selfIntroduce();
}
}
输出:
Windows已生产.
Windows电源适配器已装配.
Mac已生产.
Mac电源适配器已装配.
Linux已生产.
Linux电源适配器已装配.
新的需求将抽象工厂类的产品和对应角色组装在了一起,也可以理解为工厂里面包含一个小的工厂,具体的产品交由小的工厂来实现,这样在产品和角色比较多的场合是非常适合的。