1.7 抽象工厂模式

在制作三明治时,面包只是首个最基本的原料,显然我们还需要一些馅料。用编程语言来讲,这意味着只需简单地构建另一个像Bread的接口,可以将其称为Filling,并为其提供关联的工厂。同样,我们也可以创建一个名为Ingredient的全局接口,并将Bread和Filling都作为其样本。无论使用哪种方式,我们都必须重写许多代码。

设计模式范例提供了抽象工厂模式,它可能是最适合解决这一难题的方案。简单来说,抽象工厂就是创建其他工厂的工厂。额外添加的抽象层可以减少对主活动中上层控制代码的更改。能够修改底层结构而不影响前面的结构,这是应用设计模式的主要原因之一。当应用于复杂的体系结构时,这种灵活性可以节省多个星期的开发时间,并且相比其他方法有更多的实验空间。

使用多个工厂

下一个项目和前一个项目惊人地相似,也理应如此。使用模式最大的优点之一就是可以复用结构。你可以继续编辑上一个示例,也可以重新创建一个。这里,我们将重新开始一个新项目,希望这样可以让模式的讲解更清晰。

抽象工厂的工作方式与前面的示例稍有不同。这里,活动使用工厂生成器,而工厂生成器又使用抽象工厂类来决定实际调用的工厂任务和创建的具体类。

和之前一样,我们不关注输入和输出的实际机制,而是专注于模式的结构。在继续之前,启动一个新的Android Studio项目。无论你如何命名,请将最低API级别设置为你想要的最低版本,并选择使用Blank Activity(空白活动)模板。

(1)和之前一样,开始创建接口,只是这次我们需要两个接口:一个用于面包,另一个用于馅料。两个接口的代码应该如下所示:

    public interface Bread {
        String name();
        String calories();
    }
    public interface Filling {
        String name();
        String calories();
    }

(2)和之前一样,创建这些接口的实体类。为了节省空间,每种接口将只创建两个实体类。因为它们的代码几乎完全相同,所以这里只给出一个示例:

    public class Baguette implements Bread {
        @Override
        public String name() {
            return "Baguette";
        }
        @Override
        public String calories() {
            return " : 65 kcal";
        }
    }

(3)创建另一个叫Brioche的Bread,以及叫Cheese和Tomato的两种馅料。

(4)接下来,创建一个类,它可以调用所有类型的工厂类:

    public abstract class AbstractFactory {
        abstract Bread getBread(String bread);
        abstract Filling getFilling(String filling);
    }

(5)下面创建工厂。首先是BreadFactory:

    public class BreadFactory extends AbstractFactory {
        @Override
        Bread getBread(String bread) {
            if (bread == null) {
                return null;
            }
            if (bread == "BAG") {
                return new Baguette();
            } else if (bread == "BRI") {
                return new Brioche();
            }
            return null;
        }
        @Override
        Filling getFilling(String filling) {
            return null;
        }
    }

(6)然后是FillingFactory:

    public class FillingFactory extends AbstractFactory {
        @Override
        Filling getFilling(String filling) {
            if (filling == null) {
                return null;
            }
            if (filling == "CHE") {
                return new Cheese();
            } else if (filling == "TOM") {
                return new Tomato();
            }
            return null;
        }
        @Override
        Bread getBread(String bread) {
            return null;
        }
    }

(7)最后,添加工厂生成器类:

    public class FactoryGenerator {
        public static AbstractFactory getFactory(String factory) {
            if (factory == null) {
                return null;
            }
            if (factory == "BRE") {
                return new BreadFactory();
            } else if (factory == "FIL") {
                return new FillingFactory();
            }
            return null;
        }
    }

(8)我们可以像之前一样,使用调试标记测试代码:

    AbstractFactory fillingFactory = FactoryGenerator.getFactory("FIL");
    Filling filling = fillingFactory.getFilling("CHE");
    Log.d(DEBUG_TAG, filling.name()+" : "+filling.calories());
    AbstractFactory breadFactory = FactoryGenerator.getFactory("BRE");
    Bread bread = breadFactory.getBread("BRI");
    Log.d(DEBUG_TAG, bread.name()+" : "+bread.calories());

测试的时候,Android监视器中将产生如下输出:

    com.example.kyle.abstractfactory D/tag: Cheese : : 155 kcal
    com.example.kyle.abstractfactory D/tag: Brioche : : 85 kcal

至本书末尾时,每种原料都将是一个复杂的对象,具有相关的图像、描述文字、价格、热值,等等。这时,我们就会看到坚持使用模式的回报。此处这样一个简单的示例,只是用来演示创建型模式(例如抽象工厂)如何在不影响客户端代码或部署的情况下对产品进行更改。

和前面一样,可以通过视觉表示来增强我们对模式的理解,见图1-9。

图1-9

想象一下,我们想在菜单中加入软饮料。它们既不是面包也不是馅料,因此我们需要引入一种全新的对象。增加软饮料所需的模式已经讲过了。我们需要一个与其他接口相同的新接口,称为Drink,它将使用相同的name()和calories()方法。具体类(例如IcedTea)可以按照与上面完全相同的方式实现,例如:

    public class IcedTeaimplements Drink {
        @Override
        public String name() {
            return "Iced tea";
        }
        @Override
        public String calories() {
               return " : 110 kcal";
           }
         }

我们需要如下代码来扩展抽象工厂。

    abstract Drink getDrink(String drinkType);

当然,我们还需要实现一个DrinkFactory类,它和其他工厂有相同的结构。

换言之,我们可以添加、删除、更改以及徘徊在项目的细节之中,而无须担心软件上层逻辑如何感知这些变化。

工厂模式是最常使用的模式之一,它可以且应该在很多情景下使用。但是和所有模式一样,如果不仔细思考,它可能被过度使用或未充分使用。当考虑项目的总体架构时,如我们所见,还有许多其他模式可供使用。