工厂模式算是最常见的设计模式之一了,使用工厂模式可以让客户端直接调用工厂类创建自己需要的对象而无需自己去创建(此处对象一般是某个类的实现类)。工厂模式又包括简单工厂模式,工厂模式和抽象工厂模式。
简单工厂模式
简单工厂模式是属于创建型模式,又叫做静态工厂方法(Static Factory Method)模式,但不属于23种GOF设计模式之一。简单工厂模式是由一个工厂对象决定创建出哪一种产品类的实例。简单工厂模式是工厂模式家族中最简单实用的模式,可以理解为是不同工厂模式的一个特殊实现。 --百度百科
简单点说就是简单工厂类通过客户端传入的某些参数new符合要求的对象供客户端使用,这里我们用奶茶的口味作为例子,我们在奶茶店购买奶茶只要说出想要的口味奶茶店就给我们做好了我们想要的奶茶。
类图
主要角色有:
- 抽象产品类:MilkTea类,具体产品的抽象;
- 具体产品类:PrimaryOdorMilkTea类,FruitMilkTea类和BubbleMilkTea类,在工厂中真正被创建的类;
- 工厂类:MilkTeaShop类,负责根据条件创建具体产品类。
具体实现
抽象奶茶类:
abstract class MilkTea {
protected String name;
public void drink(){
System.out.println("我喝的是"+name);
}
}
具体奶茶类:
class PrimaryOdorMilkTea extends MilkTea {
public PrimaryOdorMilkTea() {
this.name = "原味奶茶...";
}
}
class BubbleMilkTea extends MilkTea {
public BubbleMilkTea() {
this.name = "珍珠奶茶...";
}
}
class FruitMilkTea extends MilkTea {
public FruitMilkTea() {
this.name = "水果奶茶...";
}
}
奶茶店(工厂):
class MilkTeaShop {
public static MilkTea makeMilkTea(String name){
switch (name) {
case "原味奶茶":
return new PrimaryOdorMilkTea();
case "珍珠奶茶":
return new BubbleMilkTea();
case "水果奶茶":
return new FruitMilkTea();
default:
System.out.println("对不起,我们店没有这种口味的奶茶!");
//正常这里应该抛异常了不会return null
return null;
}
}
}
测试程序及输出结果:
//测试程序
public static void main(String[] args) {
MilkTea milkTeaP = MilkTeaShop.makeMilkTea("原味奶茶");
milkTeaP.drink();
MilkTea milkTeaB = MilkTeaShop.makeMilkTea("珍珠奶茶");
milkTeaB.drink();
MilkTea milkTeaF = MilkTeaShop.makeMilkTea("水果奶茶");
milkTeaF.drink();
//这里就不调用drink()方法了,会报空指针
MilkTea milkTeaN = MilkTeaShop.makeMilkTea("巧克力奶茶");
}
//输出结果
我喝的是原味奶茶...
我喝的是珍珠奶茶...
我喝的是水果奶茶...
对不起,我们店没有这种口味的奶茶!
客户端不需要自己去创建对象,也不需要处理创建对象时的逻辑,这一切交给工厂类来做。但这里的工厂类(即奶茶店)不管是新增加了一种口味的奶茶还是去掉了一种口味的奶茶还是修改了一种口味的奶茶都需要修改工厂类,很明显的违反了开闭原则,一个设计良好的设计模式起码遵守的就是开闭原则,这就引出了工厂模式。
工厂模式
定义一个创建产品对象的工厂接口,将产品对象的实际创建工作推迟到具体子工厂类当中。这满足创建型模式中所要求的“创建与使用相分离”的特点。
从定义中我们可以知道工厂模式中工厂类被抽象了出来,每种奶茶都有对应的奶茶店,这就是工厂模式与简单工厂模式的区别,解决了简单工厂模式违反开闭原则的问题。
类图
主要角色:
- 抽象产品类:MilkTea类,产品的抽象;
- 具体产品类:PrimaryOdorMilkTea类,BubbleMilkTea类和FruitMilkTea类,工厂类真正创建的类;
- 抽象工厂类:AbstractMilkTeaShop类,定义了创建产品的方法;
- 具体工厂类:PrimaryOdorMilkTeaShop类,BubbleMilkTeaShop类和FruitMilkTeaShop类,分别生成对应的产品类,达到了解耦的目的。
具体实现
工厂模式的抽象奶茶类和具体奶茶类与简单工厂中相同。
抽象奶茶店:
abstract class AbstractMilkTeaShop {
abstract MilkTea makeMilkTea();
}
具体奶茶店:
class PrimaryOdorMilkTeaShop extends AbstractMilkTeaShop{
@Override
MilkTea makeMilkTea() {
return new PrimaryOdorMilkTea();
}
}
class BubbleMilkTeaShop extends AbstractMilkTeaShop{
@Override
MilkTea makeMilkTea() {
return new BubbleMilkTea();
}
}
class FruitMilkTeaShop extends AbstractMilkTeaShop{
@Override
MilkTea makeMilkTea() {
return new FruitMilkTea();
}
}
测试程序及输出结果:
//测试程序
public static void main(String[] args) {
AbstractMilkTeaShop milkTeaShopP = new PrimaryOdorMilkTeaShop();
MilkTea milkTeaP = milkTeaShopP.makeMilkTea();
milkTeaP.drink();
AbstractMilkTeaShop milkTeaShopB = new BubbleMilkTeaShop();
MilkTea milkTeaB = milkTeaShopB.makeMilkTea();
milkTeaB.drink();
AbstractMilkTeaShop milkTeaShopF = new FruitMilkTeaShop();
MilkTea milkTeaF = milkTeaShopF.makeMilkTea();
milkTeaF.drink();
}
//输出结果
我喝的是原味奶茶...
我喝的是珍珠奶茶...
我喝的是水果奶茶...
因为要创建具体的奶茶店对象,所以看起来还比简单工厂复杂了些。但在你想新增奶茶口味的时候就会发现只需要新增一个继承了抽象奶茶店的类就可以了,无需修改原有代码。删除奶茶口味的时候也是只需要删除对应的工厂类,无需修改其他代码,即遵守了开闭原则。
抽象工厂模式
抽象工厂模式(Abstract Factory Pattern)隶属于设计模式中的创建型模式,用于产品族的构建。抽象工厂是所有形态的工厂模式中最为抽象和最具一般性的一种形态。抽象工厂是指当有多个抽象角色时使用的一种工厂模式。抽象工厂模式可以向客户端提供一个接口,使客户端在不必指定产品的具体情况下,创建多个产品族中的产品对象。
工厂模式中的每一个形态都是针对一定问题的解决方案,工厂方法针对的是多个产品系列结构;而抽象工厂模式针对的是多个产品族结构,一个产品族内有多个产品系列。 --百度百科
抽象工厂模式的重点是产品族,我看到有的文章说抽象工厂模式是对工厂模式的改进,我觉得是不合理的,这两个模式应用的场景不太一样,工厂更侧重于同一产品等级的创建,抽象工厂更侧重于产品族的创建。
现在奶茶店要卖奶茶和面包的套餐,分别有原味套餐(原味奶茶和原味面包),珍珠套餐(珍珠奶茶和珍珠面包)和水果套餐(水果奶茶和水果面包)。由工厂模式我们可以想到新增抽象面包类和具体面包类,在抽象奶茶店方法和具体奶茶店方法中添加制作面包的方法就可以实现。
类图
-
抽象产品类:MilkTea和Bread类,两种产品的抽象类;
-
具体产品类:PrimaryOdorMilkTea类,BubbleMilkTea类和FruitMilkTea类,PrimaryOdorBread类,BubbleBread类和FruitBread类;
-
抽象工厂类:AbstractMilkTeaShop类,与工厂模式不同的是有多个方法分别生成不同的属于同一产品族的产品;
-
具体工厂类:PrimaryOdorMilkTeaShop类,BubbleMilkTeaShop类和FruitMilkTeaShop类,分别生成相应的产品族。
具体实现
抽象面包类:
abstract class Bread {
protected String name;
public void eat(){
System.out.println("我吃的是"+name);
}
}
具体面包类:
class PrimaryOdorBread extends Bread {
public PrimaryOdorBread() {
this.name = "原味面包...";
}
}
class BubbleBread extends Bread {
public BubbleBread() {
this.name = "珍珠面包...";
}
}
class FruitBread extends Bread {
public FruitBread() {
this.name = "水果面包...";
}
}
抽象奶茶店类:
abstract class AbstractMilkTeaShop {
abstract MilkTea makeMilkTea();
abstract Bread makeBread();
}
具体奶茶店类:
class PrimaryOdorMilkTeaShop extends AbstractMilkTeaShop{
@Override
MilkTea makeMilkTea() {
System.out.println("原味套餐的原味奶茶制作完成...");
return new PrimaryOdorMilkTea();
}
@Override
Bread makeBread() {
System.out.println("原味套餐的原味面包制作完成...");
return new PrimaryOdorBread();
}
}
class BubbleMilkTeaShop extends AbstractMilkTeaShop{
@Override
MilkTea makeMilkTea() {
System.out.println("珍珠套餐的珍珠奶茶制作完成...");
return new BubbleMilkTea();
}
@Override
Bread makeBread() {
System.out.println("珍珠套餐的珍珠面包制作完成...");
return new BubbleBread();
}
}
class FruitMilkTeaShop extends AbstractMilkTeaShop{
@Override
MilkTea makeMilkTea() {
System.out.println("水果套餐的水果奶茶制作完成...");
return new FruitMilkTea();
}
@Override
Bread makeBread() {
System.out.println("水果套餐的水果面包制作完成...");
return new FruitBread();
}
}
测试程序及输出结果:
//测试程序
public static void main(String[] args) {
AbstractMilkTeaShop milkTeaShopP = new PrimaryOdorMilkTeaShop();
MilkTea milkTeaP = milkTeaShopP.makeMilkTea();
milkTeaP.drink();
Bread breadP = milkTeaShopP.makeBread();
breadP.eat();
AbstractMilkTeaShop milkTeaShopB = new BubbleMilkTeaShop();
MilkTea milkTeaB = milkTeaShopB.makeMilkTea();
milkTeaB.drink();
Bread breadB = milkTeaShopB.makeBread();
breadB.eat();
AbstractMilkTeaShop milkTeaShopF = new FruitMilkTeaShop();
MilkTea milkTeaF = milkTeaShopF.makeMilkTea();
milkTeaF.drink();
Bread breadF = milkTeaShopF.makeBread();
breadF.eat();
}
//输出结果
原味套餐的原味奶茶制作完成...
我喝的是原味奶茶...
原味套餐的原味面包制作完成...
我吃的是原味面包...
珍珠套餐的珍珠奶茶制作完成...
我喝的是珍珠奶茶...
珍珠套餐的珍珠面包制作完成...
我吃的是珍珠面包...
水果套餐的水果奶茶制作完成...
我喝的是水果奶茶...
水果套餐的水果面包制作完成...
我吃的是水果面包...
可以看到抽象工厂模式与工厂模式的差别就在于抽象工厂中制作的是多个产品(这些产品往往是一个产品族)。与工厂模式一样,需要增加套餐种类只需要添加相应的具体奶茶类,具体面包类和具体奶茶店类。但抽象工厂中增加或者删除或者修改某个产品时抽象工厂和具体工厂都需要修改,违反了开闭原则。