JavaDog程序狗
发布于 2026-10-28 / 1 阅读
0
0

【设计模式】跟着狗哥复习设计模式(二):工厂方法模式

上一篇讲单例,解决的是“一个类只留一个对象”。

这一篇换个问题:项目里的对象越来越多,new 到底该写在哪儿?

先别急着记“开闭原则”这些词。咱去狗哥的饮品店站一会儿,看完再回代码里。

🍊 顾客只管点单

你去店里说:“来杯拿铁。”

正常情况下,店员不会把围裙和咖啡豆递过来,让你自己磨。顾客负责点单,后厨负责做,分工挺明白。

加瓦狗饮品店:顾客点单,后厨负责制作不同咖啡

顾客说要什么,后厨决定怎么做。先把这个感觉记住。

程序里也能这么分。

业务代码提出“我要一杯拿铁”,专门创建对象的代码负责 new Latte()。这个负责创建对象的地方,就是我们今天说的工厂。

先看直接 new 的写法

饮品店刚开张,只有美式和拿铁。点单方法可能会写成这样:

public Coffee order(String type) {
    if ("americano".equals(type)) {
        return new Americano();
    }

    if ("latte".equals(type)) {
        return new Latte();
    }

    throw new IllegalArgumentException("没有这种咖啡");
}

两种咖啡时,这段代码没啥大毛病,也很好懂。

可菜单继续加,卡布奇诺、摩卡、燕麦拿铁都来了。每加一种,点单方法就得回来添判断、添 new。

直接创建对象时,点单代码同时负责判断和创建

菜单怎么选、对象怎么建,全挤在一个方法里。

麻烦不在 new 这个单词,而在于点单代码知道的创建细节越来越多。

哪天拿铁创建前还要准备牛奶、糖浆和杯型,这个方法会越写越长。用济南话说,有点不大利索了。

把咖啡先归到一类

先写一个 Coffee 接口。现在只放一个 getName(),够例子用就行。

Coffee.java

public interface Coffee {
    String getName();
}

美式和拿铁都实现它。

Americano.java

public class Americano implements Coffee {
    @Override
    public String getName() {
        return "Americano";
    }
}

Latte.java

public class Latte implements Coffee {
    @Override
    public String getName() {
        return "Latte";
    }
}

这一步不神秘。就是让两种咖啡都有同一个类型,以后代码可以先用 Coffee 接住它们。

再给工厂定个规矩

接着写 CoffeeFactory。

CoffeeFactory.java

public interface CoffeeFactory {
    Coffee createCoffee();
}

它只定了一条规矩:咖啡工厂都得会创建咖啡。

至于创建美式还是拿铁,交给具体工厂。

AmericanoFactory.java

public class AmericanoFactory implements CoffeeFactory {
    @Override
    public Coffee createCoffee() {
        return new Americano();
    }
}

LatteFactory.java

public class LatteFactory implements CoffeeFactory {
    @Override
    public Coffee createCoffee() {
        return new Latte();
    }
}

代码里的角色看着多,其实就是两组对应关系。

工厂方法模式中的工厂与产品关系

美式工厂做美式,拿铁工厂做拿铁。两家工厂都遵守 CoffeeFactory。

Coffee 管“咖啡能做什么”,CoffeeFactory 管“咖啡怎么创建”。

书上会把它们叫产品、具体产品、工厂、具体工厂。第一遍不用背,能看懂谁创建谁就行。

饮品店场景与 Java 代码角色对照

顾客点单对应调用代码,后厨对应工厂,做好的饮品就是 Coffee 对象。

跑起来看看

最后写一个测试入口。

FactoryMethodDemo.java

public class FactoryMethodDemo {
    public static void main(String[] args) {
        makeCoffee(new AmericanoFactory());
        makeCoffee(new LatteFactory());
    }

    private static void makeCoffee(CoffeeFactory factory) {
        Coffee coffee = factory.createCoffee();
        System.out.println("Coffee ready: " + coffee.getName());
    }
}

运行后会看到:

Coffee ready: Americano
Coffee ready: Latte

重点看 makeCoffee()。

它接收的是 CoffeeFactory,没有把参数写死成 AmericanoFactory。传进来哪家工厂,它就让哪家工厂创建咖啡。

工厂方法模式一次完整调用过程

先选具体工厂,再统一调用 createCoffee(),最后拿到 Coffee。

这里的 new 没有消失,只是挪到了更合适的地方。

**业务流程不再负责具体对象的创建,工厂把这部分接了过去。**这就是工厂方法模式最值得记住的地方。

新增一种咖啡,会改哪里

假设店里增加卡布奇诺。

我们可以新增 Cappuccino,再新增一个 CappuccinoFactory。原来的 makeCoffee() 仍然接收 CoffeeFactory,不用为了新品改方法内部。

新增卡布奇诺时两种写法的改动位置对比

创建细节被分开了,原来的核心制作流程可以保持不动。

这里有个容易说过头的地方。

工厂方法不代表新增产品后,一行旧代码都不用改。菜单、配置或者选择工厂的地方,还是得知道来了新品。

它减少的是创建逻辑对原流程的影响。别把它理解成魔法,恁简单看就行。

也别见到 new 就建工厂

工厂会多出接口和类。

如果程序只有一两个简单对象,创建方式以后也不变,直接 new 更省事。为了套模式建一堆文件,反而不好找代码。

什么时候适合使用工厂方法模式

先看创建逻辑会不会变,再决定要不要加工厂。

我会在这几种情况下考虑工厂:产品种类还会增加,创建前要准备不少东西,或者业务代码不该知道具体实现。

判断标准不用背。创建对象这块开始变乱了,再把它单独收进工厂。

🐾 狗哥捋一下

这一篇的代码稍微比单例多,但主线没变。

顾客负责点单,工厂负责创建,最后交回一个 Coffee。不同工厂创建不同咖啡,调用代码只认统一的 CoffeeFactory。

自己敲一遍时,可以先只写美式。跑通以后,再加拿铁工厂。一步一步来,不容易绕木乱。

下一篇准备聊抽象工厂模式。它和今天这个名字很像,但解决的问题不一样。到时候还是从生活例子往代码里走。

#Java #设计模式 #工厂方法模式 #Java基础 #后端开发 #程序员入门


评论