Welcome to the Food Factory!

这是一篇介绍 工厂模式 的短文。

原始机器

假如你是一位机械工程师,你开发了一款机器,它可以自动使用原材料生产食物。

fn original_machine(input: Ingredients) -> Dish {
    let water = boil(100);
    let dish = make_dish_with_water(input, water);
    dish
}

你觉得这样很不错,因为你可以使用这一台机器构造流水线,完成每天的工厂生产任务。

let mut products = vec![];
for i in 0..1000 {
    let input = get_ingredients();
    let new_product = original_machine(input);
    products.push(new_product);
}

订单需求改变

你突然接到了一个新订单,它不再需要用水煮的食物,而是用火烤的。你的第一反应是改进原本的机器。

fn original_machine(input: Ingredients) -> Dish {
    let dish = roast(input);
    dish
}

let mut products = vec![];
for i in 0..1000 {
    let input = get_ingredients();
    let new_product = original_machine(input);
    products.push(new_product);
}

实际上,这是一个很不明智的做法:如果你还需要生产原始的订单,你还需要把机器再改回去。所以,一个更好的方法是,在保留原有的机器基础上,重新设计一台新机器。

fn original_machine(input: Ingredients) -> Dish {
    let water = boil(100);
    let dish = make_dish_with_water(input, water);
    dish
}

fn roast_machine(input: Ingredients) -> Dish {
    let dish = roast(input);
    dish
}

这样的话,你就能根据订单类型来决定生产哪种食品了。

enum Order{
    Original,
    Roast,
}

fn assembly_line(order: Order, amount: usize) -> Vec<Dish> {
    let mut products = vec![];
    for i in 0..amount {
        use Order::*;
        let input = get_ingredients();

        let new_product = match order {
            Original => original_machine(input),
            Roast => roast_machine(input),
        };
        products.push(new_product);
    }

    products
}

然而,随着你业务的增长,你的流水线里很可能有非常多的机器种类。

fn assembly_line(order: Order, amount: usize) -> Vec<Dish> {
    // ...

    let new_product = match order {
        Original => // ...
        Roast => // ...
        // Oh my god. Too many machines!
        Fry => // ...
    }

    ///...
}

这个时候,你的流水线构造(或者说你的代码)将会变得十分冗余,每次添加新的订单种类,总是需要设计新的机器。有没有办法作出改变呢?

做什么,你来决定

在之前的设计中,流水线只能生产已经设计好的产品(已经设计了机器的商品)。而实际上,客户总是能提出新的订单需求,作为工程师,每次设计一个新的机器是十分费神的。为了节约精力,我们需要让客户自己决定生产的流程。

trait Order {
    fn produce(input: Ingredients) -> Dish;
}

客户需要通过写明制作流程,告诉我们应该如何制作。我们只需要制造新的满足流程的机器就好了。

struct OriginalOrder;
impl Order for OriginalOrder {
    fn produce(input: Ingredients) -> Dish {
        original_machine(input) // 客户说,使用本来的设计就行了。
    }
}

struct RoastOrder;
impl Order for RoastOrder {
    fn produce(input: Ingredients) -> Dish {
        roast_machine(input) // 这个订单需要火烤。
    }
}

struct FryOrder;
impl Order for FryOrder {
    fn produce(input: Ingredients) -> Dish {
        fry_order(input) // 油炸也是可以完成的。
    }
}

当需要生产时,客户把订单交给我们,我们按订单上写明的流程生产就行了。

fn assembly_line(order: impl Order, amount: usize) -> Vec<Dish> {
    let mut products = vec![];
    for i in 0..amount {
        let input = get_ingredients();
        let new_product = order.produce(input);

        products.push(new_product);
    }

    products
}

现在你拥有了工厂模式

如果我们把这层食品工厂的皮扒掉,工厂其实是一个这样的东西。

Factory :: Input -> Output

它获取一个输入,返回一个输出。这里的输出既可以是某一个具体的类型,也可以是满足一个特征的类型集合,我们根据输入的不同产生不同的输出。

工厂提供了创建一个值的流程的抽象,将创建流程的控制反转给了输入。

Produce :: input -> Factory -> output
Produce input factory = factory input -- 根据不同的工厂决定如何输出