CodeGym /Java 博客 /随机的 /设计模式:工厂方法
John Squirrels
第 41 级
San Francisco

设计模式:工厂方法

已在 随机的 群组中发布
你好!今天我们继续学习设计模式,我们将讨论工厂方法模式。 设计模式:工厂方法 - 1 您将了解它是什么以及该模式适用于哪些任务。我们将在实践中考虑这种设计模式并研究其结构。为确保一切都清楚,您需要了解以下主题:
  1. Java中的继承。
  2. Java 中的抽象方法和类

工厂方法解决什么问题?

所有的工厂设计模式都有两类参与者:创建者(工厂本身)和产品(工厂创建的对象)。想象一下以下情况:我们有一家生产 CodeGym 品牌汽车的工厂。它知道如何创建具有各种车身类型的汽车模型:
  • 轿车
  • 旅行车
  • 双门轿车
我们的业务蒸蒸日上,以至于有一天我们收购了另一家汽车制造商——OneAuto。作为明智的企业主,我们不想失去任何 OneAuto 客户,因此我们面临重组生产的任务,以便我们能够生产:
  • CodeGym 轿车
  • CodeGym 旅行车
  • CodeGym 跑车
  • OneAuto轿车
  • OneAuto旅行车
  • OneAuto轿跑车
如您所见,我们现在有两组产品,而不是一组产品,它们在某些细节上有所不同。工厂方法设计模式适用于当我们需要创建不同的产品组时,每个产品组都有一些特定的特征。我们将在实践中考虑此模式的指导原则,使用我们在之前课程之一中创建的咖啡店示例,逐渐从简单过渡到复杂。

关于工厂模式的一点

让我提醒你,我们之前建立了一个小型虚拟咖啡店。在一个简单工厂的帮助下,我们学会了如何制作不同类型的咖啡。今天我们将重做这个例子。让我们回想一下我们的咖啡店的样子,里面有一个简单的工厂。我们有咖啡课:

public class Coffee {
    public void grindCoffee(){
        // Grind the coffee
    }
    public void makeCoffee(){
        // Brew the coffee
    }
    public void pourIntoCup(){
        // Pour into a cup
    }
}
以及我们工厂可以生产的特定类型咖啡对应的几个子类:

public class Americano extends Coffee {}
public class Cappuccino extends Coffee {}
public class CaffeLatte extends Coffee {}
public class Espresso extends Coffee {}
我们创建了一个枚举,以便于下订单:

public enum CoffeeType {
    ESPRESSO,
    AMERICANO,
    CAFFE_LATTE,
    CAPPUCCINO
}
咖啡工厂本身是这样的:

public class SimpleCoffeeFactory {
    public Coffee createCoffee(CoffeeType type) {
        Coffee coffee = null;

        switch (type) {
            case AMERICANO:
                coffee = new Americano();
                break;
            case ESPRESSO:
                coffee = new Espresso();
                break;
            case CAPPUCCINO:
                coffee = new Cappuccino();
                break;
            case CAFFE_LATTE:
                coffee = new CaffeLatte();
                break;
        }
        
        return coffee;
    }
}
最后,咖啡店本身看起来像这样:

public class CoffeeShop {

    private final SimpleCoffeeFactory coffeeFactory;

    public CoffeeShop(SimpleCoffeeFactory coffeeFactory) {
        this.coffeeFactory = coffeeFactory;
    }

    public Coffee orderCoffee(CoffeeType type) {
        Coffee coffee = coffeeFactory.createCoffee(type);
        coffee.grindCoffee();
        coffee.makeCoffee();
        coffee.pourIntoCup();

        System.out.println("Here's your coffee! Thanks! Come again!");
        return coffee;
    }
}

使一个简单的工厂现代化

我们的咖啡店经营得很好。如此之多,以至于我们正在考虑扩大规模。我们想开一些新的地点。我们大胆进取,所以我们不会搞无聊的咖啡店。我们希望每家商店都有一个特别的转折点。因此,首先,我们将开设两个地点:一个意大利和一个美国。这些变化不仅会影响室内设计,还会影响提供的饮品:
  • 在意式咖啡店,我们会选用意大利独家咖啡品牌,经过特殊的研磨和烘焙。
  • 美国分店的份量更大,我们将在每份订单中提供棉花糖。
唯一不变的是我们的商业模式,它已经证明自己是优秀的。就代码而言,这就是发生的事情。我们有 4 个类对应于我们的产品:

public class Americano extends Coffee {}
public class Cappuccino extends Coffee {}
public class CaffeLatte extends Coffee {}
public class Espresso extends Coffee {}
但现在我们将有 8 个:

public class ItalianStyleAmericano extends Coffee {}
public class ItalianStyleCappucino extends Coffee {}
public class ItalianStyleCaffeLatte extends Coffee {}
public class ItalianStyleEspresso extends Coffee {}

public class AmericanStyleAmericano extends Coffee {}
public class AmericanStyleCappucino extends Coffee {}
public class AmericanStyleCaffeLatte extends Coffee {}
public class AmericanStyleEspresso extends Coffee {}
由于我们要保留当前的业务模型,因此我们希望该orderCoffee(CoffeeType type)方法进行尽可能少的更改。看看它:

public Coffee orderCoffee(CoffeeType type) {
    Coffee coffee = coffeeFactory.createCoffee(type);
    coffee.grindCoffee();
    coffee.makeCoffee();
    coffee.pourIntoCup();

    System.out.println("Here's your coffee! Thanks! Come again!");
    return coffee;
}
我们有什么选择?好吧,我们已经知道如何编写工厂了,对吧?立即想到的最简单的事情就是编写两个类似的工厂,然后将所需的实现传递给我们咖啡店的构造函数。通过这样做,咖啡店的等级不会改变。首先,我们需要新建一个工厂类,让它继承我们的简单工厂,然后重写方法createCoffee(CoffeeType type)。让我们编写用于创建意式咖啡和美式咖啡的工厂:

public class SimpleItalianCoffeeFactory extends SimpleCoffeeFactory {

    @Override
    public Coffee createCoffee(CoffeeType type) {
        Coffee coffee = null;
        switch (type) {
            case AMERICANO:
                coffee = new ItalianStyleAmericano();
                break;
            case ESPRESSO:
                coffee = new ItalianStyleEspresso();
                break;
            case CAPPUCCINO:
                coffee = new ItalianStyleCappuccino();
                break;
            case CAFFE_LATTE:
                coffee = new ItalianStyleCaffeLatte();
                break;
        }
        return coffee;
    }
}

public class SimpleAmericanCoffeeFactory extends SimpleCoffeeFactory{

    @Override
    public Coffee createCoffee (CoffeeType type) {
        Coffee coffee = null;

        switch (type) {
            case AMERICANO:
                coffee = new AmericanStyleAmericano();
                break;
            case ESPRESSO:
                coffee = new AmericanStyleEspresso();
                break;
            case CAPPUCCINO:
                coffee = new AmericanStyleCappuccino();
                break;
            case CAFFE_LATTE:
                coffee = new AmericanStyleCaffeLatte();
                break;
        }

        return coffee;
    }

}
现在我们可以将所需的工厂实现传递给 CoffeeShop。让我们看看从不同咖啡店订购咖啡的代码是什么样的。比如意式卡布奇诺和美式卡布奇诺:

public class Main {
    public static void main(String[] args) {
        /*
            Order an Italian-style cappuccino:
            1. Create a factory for making Italian coffee
            2. Create a new coffee shop, passing the Italian coffee factory to it through the constructor
            3. Order our coffee
         */
        SimpleItalianCoffeeFactory italianCoffeeFactory = new SimpleItalianCoffeeFactory();
        CoffeeShop italianCoffeeShop = new CoffeeShop(italianCoffeeFactory);
        italianCoffeeShop.orderCoffee(CoffeeType.CAPPUCCINO);
        
        
         /*
            Order an American-style cappuccino
            1. Create a factory for making American coffee
            2. Create a new coffee shop, passing the American coffee factory to it through the constructor
            3. Order our coffee
         */
        SimpleAmericanCoffeeFactory americanCoffeeFactory = new SimpleAmericanCoffeeFactory();
        CoffeeShop americanCoffeeShop = new CoffeeShop(americanCoffeeFactory);
        americanCoffeeShop.orderCoffee(CoffeeType.CAPPUCCINO);
    }
}
我们创建了两个不同的咖啡店,将所需的工厂传递给每个咖啡店。一方面,我们已经实现了我们的目标,但另一方面……不知何故,这对企业家来说并不合适……让我们找出问题所在。一是工厂多。什么?现在对于每个新位置,我们应该创建自己的工厂,除此之外,确保在创建咖啡店时将相关工厂传递给构造函数?第二,它仍然是一个简单的工厂。只是稍微现代化了。但我们来这里是为了学习一种新模式。第三,是否可以采用不同的方法?如果我们能把所有与咖啡准备有关的问题都放在CoffeeShop通过将创建咖啡和服务订单的过程联系起来进行分类,同时保持足够的灵活性来制作各种风格的咖啡。答案是肯定的,我们可以。这称为工厂方法设计模式。

从简单工厂到工厂方法

为了尽可能高效地解决任务:
  1. 我们将createCoffee(CoffeeType type)方法返回给CoffeeShop类。
  2. 我们将把这个方法抽象化。
  3. CoffeeShop本身将变得抽象。
  4. CoffeeShop班级将有子班级。
是的,朋友。意大利咖啡店无非是阶级的后代CoffeeShop,它createCoffee(CoffeeType type)按照意大利咖啡师的最佳传统实施方法。现在,一步一个脚印。步骤 1. 使Coffee类抽象。我们有两个完整的不同产品系列。不过,意大利咖啡和美国咖啡有一个共同的祖先——阶级Coffee。将其抽象化是合适的:

public abstract class Coffee {
    public void makeCoffee(){
        // Brew the coffee
    }
    public void pourIntoCup(){
        // Pour into a cup
    }
}
步骤 2. 使用CoffeeShop抽象createCoffee(CoffeeType type)方法 进行抽象

public abstract class CoffeeShop {

    public Coffee orderCoffee(CoffeeType type) {
        Coffee coffee = createCoffee(type);

        coffee.makeCoffee();
        coffee.pourIntoCup();

        System.out.println("Here's your coffee! Thanks! Come again!");
        return coffee;
    }

    protected abstract Coffee createCoffee(CoffeeType type);
}
第 3 步。创建一家意大利咖啡店,它是抽象咖啡店的后代。我们createCoffee(CoffeeType type)在其中实施该方法,同时考虑到意大利食谱的具体情况。

public class ItalianCoffeeShop extends CoffeeShop {

    @Override
    public Coffee createCoffee (CoffeeType type) {
        Coffee coffee = null;
        switch (type) {
            case AMERICANO:
                coffee = new ItalianStyleAmericano();
                break;
            case ESPRESSO:
                coffee = new ItalianStyleEspresso();
                break;
            case CAPPUCCINO:
                coffee = new ItalianStyleCappuccino();
                break;
            case CAFFE_LATTE:
                coffee = new ItalianStyleCaffeLatte();
                break;
        }
        return coffee;
    }
}
Step 4. 美式咖啡店同理

public class AmericanCoffeeShop extends CoffeeShop {
    @Override
    public Coffee createCoffee(CoffeeType type) {
        Coffee coffee = null;

        switch (type) {
            case AMERICANO:
                coffee = new AmericanStyleAmericano();
                break;
            case ESPRESSO:
                coffee = new AmericanStyleEspresso();
                break;
            case CAPPUCCINO:
                coffee = new AmericanStyleCappuccino();
                break;
            case CAFFE_LATTE:
                coffee = new AmericanStyleCaffeLatte();
                break;
        }

        return coffee;
    }
}
第 5 步。查看美式和意式拿铁咖啡的外观:

public class Main {
    public static void main(String[] args) {
        CoffeeShop italianCoffeeShop = new ItalianCoffeeShop();
        italianCoffeeShop.orderCoffee(CoffeeType.CAFFE_LATTE);

        CoffeeShop americanCoffeeShop = new AmericanCoffeeShop();
        americanCoffeeShop.orderCoffee(CoffeeType.CAFFE_LATTE);
    }
}
恭喜。我们刚刚以我们的咖啡店为例实现了工厂方法设计模式。

工厂方法背后的原理

现在让我们更详细地考虑我们得到了什么。下图显示了生成的类。绿色块是创作者类,蓝色块是产品类。 设计模式:工厂方法 - 2我们可以得出什么结论?
  1. 所有产品都是抽象Coffee类的实现。
  2. 所有的创建者都是抽象CoffeeShop类的实现。
  3. 我们看到两个平行的类层次结构:
    • 产品的层次结构。我们看到意大利后裔和美国后裔
    • 创作者的等级制度。我们看到意大利后裔和美国后裔
  4. CoffeeShop类没有关于Coffee将创建哪个特定产品 ( ) 的信息。
  5. CoffeeShop类将特定产品的创建委托给其后代。
  6. 该类的每个后代都根据自己的具体功能CoffeeShop实现一个工厂方法。createCoffee()换句话说,生产者类的实现根据生产者类的具体情况准备特定的产品。
您现在已准备好定义工厂方法模式。工厂方法模式定义了创建对象的接口,但允许子类选择创建对象的类。因此,工厂方法将实例的创建委托给子类。一般来说,记住定义不如理解它是如何工作的重要。

工厂方法的结构

设计模式:工厂方法 - 3上图显示了工厂方法模式的一般结构。这里还有什么重要的?
  1. Creator 类实现了除工厂方法之外的所有与产品交互的方法。
  2. 抽象factoryMethod()方法必须由该类的所有后代实现Creator
  3. 该类ConcreteCreator实现了factoryMethod()直接创建产品的方法。
  4. 这个类负责创建特定的产品。这是唯一包含有关创建这些产品的信息的类。
  5. 所有产品都必须实现一个通用接口,即它们必须是一个通用产品类的后代。这是必要的,以便使用产品的类可以作为抽象而不是特定的实现对它们进行操作。

家庭作业

今天我们做了不少功课,学习了工厂方法设计模式。是时候加强材料了!练习 1. 做开另一家咖啡店的工作。它可以是英式或西班牙式咖啡店。甚至是宇宙飞船风格。在咖啡中添加食用色素使其发光,您的咖啡将简直出类拔萃!练习 2. 在上一课中,您进行了创建虚拟寿司吧或虚拟比萨店的练习。现在你的练习是不要站着不动。今天您学习了如何使用工厂方法模式来发挥您的优势。是时候利用这些知识扩展您自己的业务了;)
评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION