
00:00:00
外卖卷:非常划算 扫码领劵 省点小钱钱
文章发布较早,内容可能过时,阅读注意甄别。
想象一下,你家里只有一间茅厕(厕所),每次只能一个人进去用。当有人想用的时候,就得去排队,或者看看里面有没有人。用完了,门开着,下一个人才能进去。
单例模式就是这个意思:保证一个类在整个程序运行过程中,只有一个实例(对象)存在,而且提供一个公共的、方便的方法让大家都能访问到这个唯一的实例。
为啥要这样?比如,你家就一个管家,所有需要他做的事情都找他。或者说,程序里有一个配置中心,所有地方都得从这同一个地方读取配置。
有哪几种实现?
饿汉式(Eager Initialization):
比喻:就像你还没饿,但妈已经把饭做好了放在桌上。等你一说“饿了”,饭马上就能吃。
怎么实现:在类加载的时候,就直接把唯一的实例创建好。
代码大概长这样:
public class Singleton {
private static Singleton instance = new Singleton(); // 类加载时就创建了
private Singleton() {} // 构造器私有,外面不能随便 new
public static Singleton getInstance() { // 提供一个公共方法获取实例
return instance;
}
}优点:简单粗暴,线程安全(因为类加载时就创建了,后面不会再有并发问题)。
缺点:可能会提前创建实例,如果这个实例很占资源,但又一直没用到,就有点浪费。
懒汉式(Lazy Initialization):
比喻:你说“饿了”,妈才开始给你做饭。如果一直没说饿,饭就一直不烧。
怎么实现:第一次需要用到这个实例的时候,才去创建它。
代码大概长这样(初始版本,有线程安全问题):
public class Singleton {
private static Singleton instance; // 默认是 null
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) { // 第一次用才创建
instance = new Singleton();
}
return instance;
}
}优点:真正需要的时候才创建,节省资源。
缺点:有线程安全问题!(看下面怎么解决)
双重检查锁定(Double-Checked Locking - DCL):
比喻:你说“饿了”,妈先看冰箱有没有菜。没菜才去买菜做饭,而且为了避免你和弟弟同时说饿都跑去买菜,她会锁上门(同步),一个人去买。买完菜回来,再看看冰箱里是不是已经有菜了(双重检查),确认没有重复买。
怎么实现:在懒汉式的基础上,加锁来保证线程安全,并且进行两次 null 检查,提高性能。
代码大概长这样:
public class Singleton {
private volatile static Singleton instance; // 注意这里的 volatile 关键字
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) { // 第一次检查,如果已经创建了,就直接返回,避免进入同步块
synchronized (Singleton.class) { // 进入同步块,保证同一时间只有一个线程执行
if (instance == null) { // 第二次检查,防止多个线程同时通过第一次检查,导致重复创建
instance = new Singleton();
}
}
}
return instance;
}
}优点:既实现了懒加载,又保证了线程安全,而且性能较好(大部分时间不会进入同步块)。
缺点:实现相对复杂一点点,volatile 关键字很重要,不加可能会有问题(指令重排)。
静态内部类(Static Inner Class):
比喻:妈把饭店的菜单放在一个小抽屉里,只有你真正想点菜的时候,才去打开抽屉,拿出菜单。这个小抽屉(静态内部类)只有在第一次被打开(使用)时,里面的菜(单例实例)才会被“准备好”。
怎么实现:利用了类加载机制。静态内部类只有在被首次使用时才会被加载,它的静态成员(单例实例)也会随之被初始化。
代码大概长这样:
public class Singleton {
private Singleton() {}
// 静态内部类,只有在第一次调用 getInstance() 时才会被加载
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}优点:完美结合了懒加载和线程安全,代码也比较简洁,是推荐的实现方式之一。
枚举(Enum):
比喻:枚举天生就是独一无二的,就像你家的门牌号,只有一个,而且是系统自动保证的。
怎么实现:直接定义一个枚举类型,里面就一个枚举值。
代码大概长这样:
public enum Singleton {
INSTANCE; // 唯一的实例
public void doSomething() {
// 做一些事情
}
}
// 使用:Singleton.INSTANCE.doSomething();优点:最简洁、最推荐的方式!不仅线程安全,还能有效防止反射攻击和序列化问题,简直是“万能解药”。
如何保证线程安全?
instance == null 为真,导致创建多个实例。getInstance() 方法上加 synchronized 关键字,但性能较差,因为每次调用都会加锁。synchronized 块只在第一次创建实例时才有效,大部分时间不加锁,配合 volatile 关键字防止指令重排,兼顾性能和安全。想象一下,你要去上班,可以坐地铁、骑共享单车、打的,或者走路。每种方式都代表一种“策略”,它们都能把你送到公司,但具体怎么实现(坐地铁怎么买票,骑车怎么扫码)是不同的。你可以根据当天的天气、时间、心情来选择用哪种方式。
策略模式就是这个意思:定义一系列的算法(策略),将每一个算法封装起来,并使它们可以相互替换。客户端(你)可以根据需要选择不同的策略,而不需要改变使用策略的代码。
它解决的问题是:当一个操作有多种实现方式,并且这些方式在运行时需要灵活切换时。
一般用在哪些场景?
支付方式选择:
不同排序算法:
促销活动计算:
文件导出/导入格式:
不同税率计算:
核心思想:把“变”的部分(具体的算法实现)封装起来,把“不变”的部分(调用算法的接口)固定下来。这样,当你需要增加新的算法时,只需要增加一个新的策略类,而不需要修改原有的代码。这符合“开闭原则”(对扩展开放,对修改关闭)。
示例代码:
我们以“计算器操作”为例,实现加法、减法、乘法等不同的计算策略。
策略接口(Strategy Interface)
定义一个公共的接口,所有的具体策略都必须实现这个接口。
// Step 1: 定义策略接口
public interface OperationStrategy {
int doOperation(int num1, int num2);
}具体策略类(Concrete Strategy Classes)
实现策略接口,提供具体的算法或行为。
// Step 2: 实现具体的策略类 - 加法
public class AddOperation implements OperationStrategy {
@Override
public int doOperation(int num1, int num2) {
return num1 + num2;
}
}
// Step 2: 实现具体的策略类 - 减法
public class SubtractOperation implements OperationStrategy {
@Override
public int doOperation(int num1, int num2) {
return num1 - num2;
}
}
// Step 2: 实现具体的策略类 - 乘法
public class MultiplyOperation implements OperationStrategy {
@Override
public int doOperation(int num1, int num2) {
return num1 * num2;
}
}上下文类(Context Class)
持有策略接口的引用,客户端通过上下文类来使用具体的策略。
// Step 3: 创建上下文类
public class CalculatorContext {
private OperationStrategy strategy;
public CalculatorContext(OperationStrategy strategy) {
this.strategy = strategy;
}
public int executeOperation(int num1, int num2) {
return strategy.doOperation(num1, num2);
}
// 也可以提供一个方法来动态改变策略
public void setStrategy(OperationStrategy strategy) {
this.strategy = strategy;
}
}客户端(Client)
使用上下文类和具体的策略来执行操作。
// Step 4: 客户端代码
public class StrategyPatternDemo {
public static void main(String[] args) {
// 使用加法策略
CalculatorContext context = new CalculatorContext(new AddOperation());
System.out.println("10 + 5 = " + context.executeOperation(10, 5)); // 输出:10 + 5 = 15
// 切换到减法策略
context.setStrategy(new SubtractOperation()); // 或者直接 new CalculatorContext(new SubtractOperation())
System.out.println("10 - 5 = " + context.executeOperation(10, 5)); // 输出:10 - 5 = 5
// 切换到乘法策略
context.setStrategy(new MultiplyOperation());
System.out.println("10 * 5 = " + context.executeOperation(10, 5)); // 输出:10 * 5 = 50
// 也可以直接创建新的上下文
CalculatorContext multiplyContext = new CalculatorContext(new MultiplyOperation());
System.out.println("20 * 3 = " + multiplyContext.executeOperation(20, 3)); // 输出:20 * 3 = 60
}
}代码解释:
OperationStrategy 是策略接口,定义了所有计算操作的共同行为。AddOperation、SubtractOperation、MultiplyOperation 是具体策略,分别实现了加、减、乘的具体算法。CalculatorContext 是上下文类,它持有一个 OperationStrategy 对象的引用。客户端通过 CalculatorContext 来执行操作,而不需要知道具体的策略类是哪个,实现了策略与客户端的解耦。CalculatorContext,从而在运行时改变其行为。想象一下,你有一张做菜的通用食谱。比如“做一道家常菜”:
这个流程是固定的,但具体到“准备什么食材”、“怎么切”、“炒什么菜”、“放什么调料”,每个人家可以有不同的做法。比如,做番茄炒蛋,准备番茄鸡蛋;做青椒肉丝,准备青椒肉。
模板方法模式就是这个意思:在一个抽象类中定义一个操作中的算法骨架(模板),而将一些步骤延迟到子类中去实现。这使得子类可以在不改变算法结构的情况下,重新定义该算法的某些特定步骤。
它解决的问题是:当多个类有共同的算法骨架,但其中某些步骤的实现方式不同时。
一般用在哪些场景?
通用算法框架:
读取数据 -> 预处理 -> 核心业务逻辑 -> 保存结果。报表生成:
连接数据库 -> 查询数据 -> 格式化数据 -> 输出报表。算法骨架,具体实现交给子类:
开始游戏 -> 进行中 -> 结束游戏。Web 开发中的请求处理:
接收请求 -> 参数绑定 -> 业务逻辑处理 -> 视图渲染。构建工具的编译/打包流程:
validate -> compile -> test -> package -> install -> deploy)。核心思想:封装“不变”的部分(算法骨架),把“可变”的部分(特定步骤的实现)留给子类。这样,当你需要改变某个步骤的实现时,只需要修改或创建新的子类,而不需要动到算法的整体结构。这也符合“开闭原则”。
示例代码:
我们以“构建房子”为例,房子建造有固定的步骤,但不同类型的房子(木屋、砖房)在某些步骤的实现上有所不同。
抽象模板类(Abstract Template Class)
定义算法的骨架(模板方法),并声明一些抽象方法,供子类实现。也可以包含一些具体方法和钩子方法。
// Step 1: 定义抽象模板类
public abstract class HouseBuilder {
// 模板方法:定义了建造房子的通用算法骨架
public final void buildHouse() { // 使用 final 关键字防止子类修改模板方法的执行顺序
layFoundation(); // 步骤 1: 打地基 (具体方法)
buildWalls(); // 步骤 2: 建造墙壁 (抽象方法,子类实现)
buildRoof(); // 步骤 3: 建造屋顶 (抽象方法,子类实现)
decorateHouse(); // 步骤 4: 装修房子 (钩子方法,子类可选实现)
System.out.println("房子建造完成!");
System.out.println("--------------------");
}
// 具体方法:所有房子都一样,无需子类修改
private void layFoundation() {
System.out.println("打地基:挖掘地基,浇筑混凝土。");
}
// 抽象方法:留给子类实现,不同类型的房子墙壁建造方式不同
protected abstract void buildWalls();
// 抽象方法:留给子类实现,不同类型的房子屋顶建造方式不同
protected abstract void buildRoof();
// 钩子方法:子类可以选择是否实现(重写),提供默认的空实现或通用实现
// 比如:不是所有房子都需要装修,或者默认是简单装修
protected void decorateHouse() {
System.out.println("装修房子:进行基础装修。");
}
}具体模板实现类(Concrete Template Classes)
实现抽象类中定义的抽象方法,完成算法的具体步骤。
// Step 2: 实现具体的模板子类 - 木屋
public class WoodenHouseBuilder extends HouseBuilder {
@Override
protected void buildWalls() {
System.out.println("建造墙壁:使用木头和木板搭建墙壁。");
}
@Override
protected void buildRoof() {
System.out.println("建造屋顶:铺设木质瓦片屋顶。");
}
// 可以选择性地重写钩子方法,进行特定的装修
@Override
protected void decorateHouse() {
System.out.println("装修房子:进行木质风格的内外装修。");
}
}
// Step 2: 实现具体的模板子类 - 砖房
public class BrickHouseBuilder extends HouseBuilder {
@Override
protected void buildWalls() {
System.out.println("建造墙壁:使用砖块和水泥砌墙。");
}
@Override
protected void buildRoof() {
System.out.println("建造屋顶:铺设陶瓷瓦片屋顶。");
}
// 这个砖房就不重写 decorateHouse,使用父类的默认装修方法
}客户端(Client)
使用具体模板实现类来执行模板方法。
// Step 3: 客户端代码
public class TemplateMethodPatternDemo {
public static void main(String[] args) {
System.out.println("--- 建造木屋 ---");
HouseBuilder woodenHouse = new WoodenHouseBuilder();
woodenHouse.buildHouse(); // 调用模板方法
System.out.println("\n--- 建造砖房 ---");
HouseBuilder brickHouse = new BrickHouseBuilder();
brickHouse.buildHouse(); // 调用模板方法
}
}代码解释:
HouseBuilder 是抽象模板类,它定义了 buildHouse() 这个模板方法。这个方法包含了建造房子的固定步骤顺序。layFoundation() 是一个具体方法,所有房子打地基的方式都一样,所以在父类中直接实现。buildWalls() 和 buildRoof() 是抽象方法,它们的具体实现留给子类 WoodenHouseBuilder 和 BrickHouseBuilder 去完成。decorateHouse() 是一个钩子方法。它在父类中有一个默认的(可能是空的或通用的)实现,子类可以选择性地重写它来添加自己的特定行为。buildHouse() 方法被声明为 final,这是为了防止子类修改算法的骨架,保证了算法的稳定性和一致性。buildHouse() 来启动整个建造流程。设计模式就像是前辈们在编程路上遇到一些常见问题时总结出来的“解决方案模板”,不是死板的代码,而是一种解决问题的思路。最常见的几种:
单例模式 (Singleton Pattern)
工厂模式 (Factory Pattern)
if/else 来判断创建哪个,而是通过一个工厂方法来创建,它会根据你的选择返回对应的图形对象。观察者模式 (Observer Pattern)
策略模式 (Strategy Pattern)
好的代码就像一本好书,它有以下几个特点:
易读性 (Readable):
可维护性 (Maintainable):
可扩展性 (Extensible):
健壮性 (Robust):
高内聚低耦合 (High Cohesion, Low Coupling):
符合设计原则:比如遵循单一职责原则(一个类只做一件事)、里氏替换原则(子类可以替换父类且不破坏程序功能)等,这些都是通往好代码的“秘籍”。
总的来说,好的代码是让人舒服的代码,无论是写的人、读的人还是未来维护的人,都能感觉到它的清晰、高效和可靠。
这就像“生产线”的两种不同管理方式:
工厂模式 (Factory Pattern) - 简单工厂或工厂方法
示例:我们来创建一个披萨工厂,它可以根据用户的需求生产不同类型的披萨。
首先,定义披萨的抽象类或接口:
// 披萨的抽象基类
abstract class Pizza {
protected String name;
public abstract void prepare();
public abstract void bake();
public abstract void cut();
public abstract void box();
public String getName() {
return name;
}
}
// 具体披萨类型 1:芝士披萨
class CheesePizza extends Pizza {
public CheesePizza() {
name = "芝士披萨";
}
@Override
public void prepare() {
System.out.println("准备芝士披萨的原料:面团、番茄酱、大量芝士");
}
@Override
public void bake() {
System.out.println("烘烤芝士披萨,200 度 15 分钟");
}
@Override
public void cut() {
System.out.println("将芝士披萨切成 8 块");
}
@Override
public void box() {
System.out.println("将芝士披萨装入披萨盒");
}
}
// 具体披萨类型 2:培根披萨
class BaconPizza extends Pizza {
public BaconPizza() {
name = "培根披萨";
}
@Override
public void prepare() {
System.out.println("准备培根披萨的原料:面团、番茄酱、培根、洋葱");
}
@Override
public void bake() {
System.out.println("烘烤培根披萨,200 度 18 分钟");
}
@Override
public void cut() {
System.out.println("将培根披萨切成 6 块");
}
@Override
public void box() {
System.out.println("将培根披萨装入披萨盒");
}
}然后,定义披萨工厂的抽象接口和具体工厂类:
// 披萨工厂的抽象接口
// 每个具体工厂负责生产一种类型的披萨
interface PizzaFactory {
Pizza createPizza();
}
// 芝士披萨工厂
class CheesePizzaFactory implements PizzaFactory {
@Override
public Pizza createPizza() {
System.out.println("----通过芝士披萨工厂生产芝士披萨----");
return new CheesePizza();
}
}
// 培根披萨工厂
class BaconPizzaFactory implements PizzaFactory {
@Override
public Pizza createPizza() {
System.out.println("----通过培根披萨工厂生产培根披萨----");
return new BaconPizza();
}
}最后,客户端如何使用:
public class FactoryMethodDemo {
public static void main(String[] args) {
System.out.println("欢迎来到披萨店!");
// 客户想要一个芝士披萨
PizzaFactory cheeseFactory = new CheesePizzaFactory();
Pizza cheesePizza = cheeseFactory.createPizza();
System.out.println("你点的是:" + cheesePizza.getName());
cheesePizza.prepare();
cheesePizza.bake();
cheesePizza.cut();
cheesePizza.box();
System.out.println();
// 客户想要一个培根披萨
PizzaFactory baconFactory = new BaconPizzaFactory();
Pizza baconPizza = baconFactory.createPizza();
System.out.println("你点的是:" + baconPizza.getName());
baconPizza.prepare();
baconPizza.bake();
baconPizza.cut();
baconPizza.box();
}
}运行结果:
欢迎来到披萨店!
----通过芝士披萨工厂生产芝士披萨----
你点的是:芝士披萨
准备芝士披萨的原料:面团、番茄酱、大量芝士
烘烤芝士披萨,200 度 15 分钟
将芝士披萨切成 8 块
将芝士披萨装入披萨盒
----通过培根披萨工厂生产培根披萨----
你点的是:培根披萨
准备培根披萨的原料:面团、番茄酱、培根、洋葱
烘烤培根披萨,200 度 18 分钟
将培根披萨切成 6 块
将培根披萨装入披萨盒解释:
在这个例子中,PizzaFactory 是抽象工厂,定义了创建披萨的方法。CheesePizzaFactory 和 BaconPizzaFactory 是具体工厂,每个工厂只负责创建一种具体类型的披萨(芝士披萨或培根披萨)。当需要增加一种新的披萨类型(比如“蔬菜披萨”)时,我们只需要创建一个新的 VegetablePizza 类和一个新的 VegetablePizzaFactory 类,而不需要修改已有的工厂或披萨类。这符合“开闭原则”。
抽象工厂模式 (Abstract Factory Pattern)
示例:披萨店升级了,不只卖披萨,还卖饮料和甜点,而且披萨店有“纽约风味”和“芝加哥风味”两种风格。每种风格的披萨、饮料、甜点都是一套。
首先,定义产品族(披萨、饮料、甜点)的抽象类或接口:
// --- 产品族:披萨 ---
abstract class AbsPizza {
protected String style;
public abstract void prepare();
public abstract void bake();
public String getStyle() { return style; }
}
class NYStyleCheesePizza extends AbsPizza {
public NYStyleCheesePizza() { style = "纽约风味芝士披萨"; }
@Override public void prepare() { System.out.println("准备纽约风味芝士披萨:薄底面团、马苏里拉芝士..."); }
@Override public void bake() { System.out.println("烘烤纽约风味芝士披萨..."); }
}
class ChicagoStyleCheesePizza extends AbsPizza {
public ChicagoStyleCheesePizza() { style = "芝加哥风味芝士披萨"; }
@Override public void prepare() { System.out.println("准备芝加哥风味芝士披萨:厚底面团、深盘、大量芝士..."); }
@Override public void bake() { System.out.println("烘烤芝加哥风味芝士披萨..."); }
}
// --- 产品族:饮料 ---
abstract class AbsDrink {
protected String style;
public abstract void serve();
public String getStyle() { return style; }
}
class NYStyleSoda extends AbsDrink {
public NYStyleSoda() { style = "纽约风味苏打水"; }
@Override public void serve() { System.out.println("提供纽约风味苏打水:加冰、柠檬片..."); }
}
class ChicagoStyleCola extends AbsDrink {
public ChicagoStyleCola() { style = "芝加哥风味可乐"; }
@Override public void serve() { System.out.println("提供芝加哥风味可乐:大杯装、少冰..."); }
}
// --- 产品族:甜点 ---
abstract class AbsDessert {
protected String style;
public abstract void serve();
public String getStyle() { return style; }
}
class NYStyleCheesecake extends AbsDessert {
public NYStyleCheesecake() { style = "纽约风味芝士蛋糕"; }
@Override public void serve() { System.out.println("提供纽约风味芝士蛋糕:小巧精致..."); }
}
class ChicagoStyleBrownie extends AbsDessert {
public ChicagoStyleBrownie() { style = "芝加哥风味布朗尼"; }
@Override public void serve() { System.out.println("提供芝加哥风味布朗尼:浓郁厚实..."); }
}然后,定义抽象工厂和具体工厂:
// 抽象工厂接口:定义创建一组产品的方法
interface RestaurantFactory {
AbsPizza createPizza();
AbsDrink createDrink();
AbsDessert createDessert();
}
// 纽约风味餐厅的具体工厂
class NYStyleRestaurantFactory implements RestaurantFactory {
@Override
public AbsPizza createPizza() {
return new NYStyleCheesePizza();
}
@Override
public AbsDrink createDrink() {
return new NYStyleSoda();
}
@Override
public AbsDessert createDessert() {
return new NYStyleCheesecake();
}
}
// 芝加哥风味餐厅的具体工厂
class ChicagoStyleRestaurantFactory implements RestaurantFactory {
@Override
public AbsPizza createPizza() {
return new ChicagoStyleCheesePizza();
}
@Override
public AbsDrink createDrink() {
return new ChicagoStyleCola();
}
@Override
public AbsDessert createDessert() {
return new ChicagoStyleBrownie();
}
}最后,客户端如何使用:
public class AbstractFactoryDemo {
public static void main(String[] args) {
System.out.println("欢迎来到全球披萨店连锁!");
// 客户想点一套纽约风味的餐品
System.out.println("\n--- 顾客点了一套纽约风味餐品 ---");
RestaurantFactory nyFactory = new NYStyleRestaurantFactory();
AbsPizza nyPizza = nyFactory.createPizza();
AbsDrink nyDrink = nyFactory.createDrink();
AbsDessert nyDessert = nyFactory.createDessert();
System.out.println("你的纽约风味套餐包含:");
System.out.println("- " + nyPizza.getStyle());
nyPizza.prepare();
nyPizza.bake();
System.out.println("- " + nyDrink.getStyle());
nyDrink.serve();
System.out.println("- " + nyDessert.getStyle());
nyDessert.serve();
// 客户想点一套芝加哥风味的餐品
System.out.println("\n--- 顾客点了一套芝加哥风味餐品 ---");
RestaurantFactory chicagoFactory = new ChicagoStyleRestaurantFactory();
AbsPizza chicagoPizza = chicagoFactory.createPizza();
AbsDrink chicagoDrink = chicagoFactory.createDrink();
AbsDessert chicagoDessert = chicagoFactory.createDessert();
System.out.println("你的芝加哥风味套餐包含:");
System.out.println("- " + chicagoPizza.getStyle());
chicagoPizza.prepare();
chicagoPizza.bake();
System.out.println("- " + chicagoDrink.getStyle());
chicagoDrink.serve();
System.out.println("- " + chicagoDessert.getStyle());
chicagoDessert.serve();
}
}运行结果:
欢迎来到全球披萨店连锁!
--- 顾客点了一套纽约风味餐品 ---
你的纽约风味套餐包含:
- 纽约风味芝士披萨
准备纽约风味芝士披萨:薄底面团、马苏里拉芝士...
烘烤纽约风味芝士披萨...
- 纽约风味苏打水
提供纽约风味苏打水:加冰、柠檬片...
- 纽约风味芝士蛋糕
提供纽约风味芝士蛋糕:小巧精致...
--- 顾客点了一套芝加哥风味餐品 ---
你的芝加哥风味套餐包含:
- 芝加哥风味芝士披萨
准备芝加哥风味芝士披萨:厚底面团、深盘、大量芝士...
烘烤芝加哥风味芝士披萨...
- 芝加哥风味可乐
提供芝加哥风味可乐:大杯装、少冰...
- 芝加哥风味布朗尼
提供芝加哥风味布朗尼:浓郁厚实...抽象工厂模式解释:
在这个例子中,RestaurantFactory 是抽象工厂,定义了创建披萨、饮料和甜点的方法。NYStyleRestaurantFactory 和 ChicagoStyleRestaurantFactory 是具体工厂,每个工厂负责创建一整套特定风格的产品(纽约风味的披萨、饮料、甜点;或芝加哥风味的披萨、饮料、甜点)。
如果我们要新增一种“意大利风味”的餐厅,我们只需要创建一个新的 ItalianStyleRestaurantFactory 类,实现 RestaurantFactory 接口,并让它创建对应的意大利风味披萨、饮料和甜点即可。客户端代码只需要切换具体的工厂实例,就能获得不同风格的整套产品,而无需关心内部创建的细节。
核心区别总结:
简单来说,工厂模式关注的是“造一个什么东西”,而抽象工厂模式关注的是“造一套什么风格的东西”。
评论