前言
本章是阅读《Java与模式》所作的笔记。
工厂模式专门将大量有共同接口的类实例化,它可以动态的来决定实现哪一个类来实例化,不必实现指导每次要实例化哪个类。它分为三种: 简单工厂、工厂方法、抽象工厂三种。我会在文章下面一一阐述。
简单工厂
定义
定义一个创建产品对象的工厂接口,将产品对象的实际创建工作推迟到具体子工厂类当中
结构与角色
简单工厂结构图
简单工厂分为三个角色分别是:工厂类角色、抽象产品角色、具体产品角色。
工厂类角色:工厂方法模式核心,由客服端直接调用创建产品对象。
抽象产品角色:拥有共同接口,由具体产品角色来实现。
具体产品角色:工厂方法模式所创建的对象都是该角色的实例。
在一些特定情况下可以将其中某些角色省略。比如系统中只有一个具体产品角色,就可以省略掉抽象产品角色,由工厂直接创建具体产品。
应用场景
产品种类相对较少,且无需关心创建对象逻辑。
代码示例
水果有多种种类,而且可食用采摘。我们就以此来编写一个简单工厂模式的一个示例。
public interface Fruits{
/**
* 种植
*/
void plant();
/**
* 生长
*/
void grow();
/**
* 收获
*/
void harvest();
}
/**
* 苹果
*/
public class Apple implements Fruits {
@Override
public void plant() {
System.out.println("种植Apple");
}
@Override
public void grow() {
System.out.println("Apple生长");
}
@Override
public void harvest() {
System.out.println("Apple收获");
}
}
/**
* 香蕉
*/
public class Banana implements Fruits {
@Override
public void plant() {
System.out.println("种植Banana");
}
@Override
public void grow() {
System.out.println("Banana生长");
}
@Override
public void harvest() {
System.out.println("Banana收获");
}
}
/**
*静态工厂方法 通过传参决定返回哪一个任务对象
*/
public static Fruit fruitGrowers(String ftuit){
switch (ftuit){
case "Apple":
return new Apple();
case "Banana":
return new Banana();
default:
return null;
}
}
简单工厂的优缺点
优点:该模式核心就是工厂类,该类决定生成哪一个产品类的实例,客服端只需要来进行调用对应产品类的方法以达到消费的目的,工厂模式通过这种做法实现了责任的分割
缺点:当产品类有复杂的多层次等级结构时,工厂类只能做到以不变应万变。若工厂类不能工作则会导致整个工厂都会受到影响。
工厂方法模式
定义
定义一个用于创建对象的接口,但是让子类来决定到底创建哪一个实例。工厂方法模式让一个类的实例化延迟到其子类
工厂方法模式优缺点
工厂方法模式使用的多态性,保持了简单工厂的优点克服了它的缺点。在工厂方法模式中,核心工厂类不在负责产品建造,将具体建造交予子类完成,不在接触需要实例化哪一个产品这种细节。
对开闭原则支持不够。
结构与角色
结构:
抽象工厂(Creator):核心、与应用程序无关,创建对象的工厂类必须实现这个接口。
具体工厂(Concrete Creator):实现抽象工厂具体类,与应用密切相关,受程序调用创建对象。
抽象产品(Product):产品所拥有的共同父类以及接口。
具体产品(Concrete Product):实现抽象产品中的接口并且被工厂创建。
代码示例
结合简单工厂模式中我们使用了水果的例子,但是负责水果的果农只有一个,需要负责所有水果。所以我们结合简单工厂中水果来结合我们工厂方法模式中来作出修改。
/**
* 水果果农抽象工厂
*/
public interface FruitGardener {
/**
* 工厂方法
*/
Fruit factory();
}
/**
* 具体工厂
* 负责苹果果农
*/
public class AppleFruitGardener implements FruitGardener{
@Override
public Fruit factory() {
return new Apple();
}
}
/**
* 负责香蕉果农
*/
public class BananaFruitGardener implements FruitGardener {
@Override
public Fruit factory() {
return new Banana();
}
}
/**
* 抽象产品
*/
public interface Fruits{
/**
* 种植
*/
void plant();
/**
* 生长
*/
void grow();
/**
* 收获
*/
void harvest();
}
/**
* 苹果
* 具体产品
*/
public class Apple implements Fruits {
@Override
public void plant() {
System.out.println("种植Apple");
}
@Override
public void grow() {
System.out.println("Apple生长");
}
@Override
public void harvest() {
System.out.println("Apple收获");
}
}
/**
* 香蕉
* 具体产品
*/
public class Banana implements Fruits {
@Override
public void plant() {
System.out.println("种植Banana");
}
@Override
public void grow() {
System.out.println("Banana生长");
}
@Override
public void harvest() {
System.out.println("Banana收获");
}
}
/**
*通过实例化某一个具体工厂对象,来获取到具体产品
*/
public static void main(String[] args) {
//获取到具体工厂来调用具体产品
FruitGardener f1 = new AppleFruitGardener();
//获取到学习任务对象
FruitGardener f2 = new FruitGardener();
}
以上就是将简单工厂的工厂类转换为接口,根据具体产品来确定自己需要返回哪些实例,然后继承抽象工厂来完成。
多态性的丧失和模式的退化
工厂方法模式实现依赖于工厂角色和产品角色的多态性,有些情况下,这个模式可以出现退化,特征就是多态性的丧失。
工厂方法创建对象
工厂方法每次并不一定返回一个新的对象,但是返回的对象一定是它自行创建的,但这不代表凡是返还一个新的对象的方法都是工厂方法。
工厂方法返还的类型
工厂方法返还的是抽象类型而不是具体类型,这样才能保证针对产品的多态。特殊情况下,工厂方法仅仅返还一个具体产品类型,这个时候工厂方法就会退化,表现产品角色多态性丧失。
工厂等级结构
工厂对象应当有一个抽象超类,也应当有数个具体工厂类作为抽象超类型的具体子类存在于结构中。如果结构中只有一个具体工厂类那么抽象工厂角色也可以省略。这时工厂方法模式也会退化造成多态性丧失,此时可以由简单工厂代替。
抽象工厂模式
定义
为访问类提供一个创建一组相关或相互依赖对象的接口,且访问类无须指定所要产品的具体类就能得到同族的不同等级的产品的模式结构。
抽象工厂模式是所有形态的工厂模式中最为抽象和最具一般性的一种形态。
抽象工厂优缺点
优点:
可以在类的内部对产品族中相关联的多等级产品共同管理,而不必专门引入多个新的类来进行管理。
当需要产品族时,抽象工厂可以保证客户端始终只使用同一个产品的产品组。
抽象工厂增强了程序的可扩展性,当增加一个新的产品族时,不需要修改原代码,满足开闭原则。
缺点:
当产品族中需要增加一个新的产品时,所有的工厂类都需要进行修改。增加了系统的抽象性和理解难度。
结构
抽象工厂涉及到的角色:
抽象工厂(AbstractFactory):核心、与应用程序无关,创建对象的工厂类必须实现这个接口。
具体工厂(Concrete Factory):实现抽象工厂具体类,与应用密切相关,受程序调用创建对象。
抽象产品(Abstract Product):产品所拥有的共同父类以及接口。
具体产品(Conctrete Product):实现抽象产品中的接口并且被工厂创建。
代码示例
我们接着上面几个模式示例,来结合当前我们抽象工厂模式修改。
/**
* 水果果农抽象工厂
*/
public interface Gardener {
/**
* 水果的工厂方法
*/
Fruit getFruit(String name);
Veggie getVeggie(String name);
}
/**
* 具体工厂
* 负责热带蔬菜水果
*/
public class TropicalGardener implements Gardener{
@Override
public Fruit getFruit(String name) {
return new TropicalFruits(name);
}
@Override
public Veggie getVeggie(String name) {
return new TropicalVeggie();
}
}
/**
* 负责温带蔬菜水果
*/
public class TemperateGardener implements Gardener {
@Override
public Fruit getFruit(String name) {
return new TemperateFruits(name);
}
@Override
public Veggie getVeggie(String name) {
return new TemperateVeggie();
}
}
/**
* 抽象产品 水果
*/
public interface Fruits{
}
//热带水果
public class TropicalFruits implements Fruits{
private String name;
public TropicalFruits(String name){
}
}
/**
* 温带水果
* 具体产品
*/
public class TemperateFruits implements Fruits {
private String name;
public TemperateFruits(String name){
}
}
/**
* 抽象蔬菜
*/
public interface Veggie{
}
public class TropicalVeggie implements Veggie {
private String name;
public TropicalVeggie(String name){
}
}
public class TemperateVeggie implements Veggie {
private String name;
public TemperateVeggie (String name){
}
}
在什么情形下应当使用抽象工厂模式
- 一个系统不应当依赖于产品类实例如何被创建、组合、表达的细节,对于所有形态的工厂模式都是重要的。
- 有多于一个的产品族,系统只消费其中某一族的产品。
- 同属于同一个产品族的产品是在一起使用的。
- 系统提供也给产品类的库,所有的产品以同样的接口出现,从而不依赖于实现。
抽象工厂模式的起源
它的最早应用是用于创建分属于不同操作系统的视窗构件。
抽象工厂模式基于开闭原则的支持
开闭原则要求我们在不修改原有代码情况下通过扩展达到增强功能的目的。对于涉及到多个产品等级结构和多个产品族的系统,增强有两个方面:
1.增加新的产品族;
在等级结构不变情况下,我们通过增加新的产品族,但是这样会衍生出多个具体产品角色。由于工厂等级结构是与产品等级结构平行,我们只需要在系统中加入新的具体工厂类就可以了,没必要修改已有的东西。
2.增加新的产品等级结构
因为结构变了,在产品等级中多出一个平行的等级,所有我们需要修改所有的工厂角色,在其中增加方法,这显然违背了开闭原则。
总结
抽象工厂以中倾斜的方式支持增加新的产品。