当前位置: 首页
AI教程
面试这样答装饰器模式,薪资至少多3000

面试这样答装饰器模式,薪资至少多3000

热心网友 时间:2026-06-29
转载

在最近一次 Code Review 中,我发现同事的代码里“继承满天飞”。为了实现一个核心类的功能扩展,他竟然创建了十几个子类。看着那庞大的类继承树,像族谱一样,手里的咖啡瞬间失去了香味——这正是过度使用继承导致的“类爆炸”问题。

面试官问“装饰器模式”,这样回答薪资多要 3000!

许多初学者乃至资深开发者,在遇到“如何在不修改原有类代码的前提下动态扩展对象功能”这类问题时,第一时间想到的解决方案通常是继承。

然而,继承是一把双刃剑。当需要将多种功能进行排列组合时,继承机制极易导致“类爆炸”。

今天,让我们抛开枯燥的理论,以一杯奶茶为例,由浅入深地彻底掌握 PHP 装饰器模式(Decorator Pattern)的原理与实战用法。

1. 痛点:当需求开始“套娃”

假设我们正在开发一套奶茶店收银系统。
最初的需求非常单纯:只出售原味奶茶,每杯定价 10 元。

你可能会定义一个 MilkTea 类,其中包含一个 cost() 方法,直接返回 10。

然而,老板的需求总是来得猝不及防:

  • 加珍珠 +2 元
  • 加椰果 +3 元
  • 加布丁 +4 元
  • 加糖?去冰?…

如果采用继承方式,你可能会写出这样的子类:

PearlMilkTea (珍珠奶茶)
CoconutMilkTea (椰果奶茶)
PuddingMilkTea (布丁奶茶)
…

此时,顾客下单:“我要一杯加珍珠、加椰果、半糖、去冰的奶茶”。

这下糟了,难道要创建一个 PearlAndCoconutAndHalfSugarAndNoIceMilkTea 类吗?
如果配料有 10 种,排列组合出来的子类数量将是天文数字——这就是典型的“类爆炸”。

2. 破局之道:像“洋葱”一样层层包裹

装饰器模式的核心思想正是“组合优于继承”。

换个思路:不必为每种搭配创建新奶茶,而是将基础奶茶作为核心,通过动态组合来扩展功能。

  • 加珍珠?只需在奶茶外层包裹一层“珍珠装饰器”。
  • 再加椰果?就在刚才的整体外面再包一层“椰果装饰器”。

这样一层层包裹,如同俄罗斯套娃或洋葱结构。每层装饰器只负责增加自己的价格,并将剩余任务委托给内层对象。

3. 实战演练:用 PHP 逐步实现装饰器模式

Talk is cheap, show me the code. 我们使用 PHP 8 的语法优雅地实现它。

第一步:定义统一接口 (Contract)

无论是基础奶茶还是添加配料的扩展版本,本质上都属于“饮品”。因此需要定义一个统一的接口来规范行为。

第二步:实现原始对象 (Concrete Component)

这是最基础的组件——一杯纯粹的原味奶茶。

class SimpleMilkTea implements Beverage
{
    public function getDescription(): string
    {
        return "原味奶茶";
    }

    public function cost(): int
    {
        return 10; // 基础价 10 元
    }
}

第三步:构建装饰器基类 (Base Decorator)

这是装饰器模式的精髓。装饰器本身也实现了 Beverage 接口,但内部持有另一个 Beverage 对象。
注意:这里使用 abstract 关键字,子类只需实现具体的加料逻辑即可。

abstract class BeverageDecorator implements Beverage
{
    // PHP 8 构造函数属性提升,直接注入被装饰的对象
    public function __construct(protected Beverage $beverage) { }

    // 默认行为:直接调用里面那层的方法
    public function getDescription(): string
    {
        return $this->beverage->getDescription();
    }

    public function cost(): int
    {
        return $this->beverage->cost();
    }
}

第四步:编写具体的装饰器 (Concrete Decorators)

现在,想加什么配料,就创建对应的装饰器类,即写即用,互不干扰。

加珍珠(Pearl):

class PearlDecorator extends BeverageDecorator
{
    public function getDescription(): string
    {
        // 先获取里面的描述,再追加自己的描述
        return $this->beverage->getDescription() . " + 珍珠";
    }

    public function cost(): int
    {
        // 核心价格 + 珍珠的价格(2元)
        return $this->beverage->cost() + 2;
    }
}

加布丁(Pudding):

class PuddingDecorator extends BeverageDecorator
{
    public function getDescription(): string
    {
        return $this->beverage->getDescription() . " + 布丁";
    }

    public function cost(): int
    {
        return $this->beverage->cost() + 4; // 布丁贵一点
    }
}

4. 效果演示:见证组合的威力

代码编写完毕,接下来看看在业务逻辑中如何灵活运用。你会发现装饰器的组合方式极其灵活。

// 1. 点一杯原味奶茶
$myDrink = new SimpleMilkTea();
echo "刚开始: " . $myDrink->getDescription() . " 价格:" . $myDrink->cost() . "";

// 2. 顾客说要加珍珠
// 把原味奶茶塞进珍珠装饰器里
$myDrink = new PearlDecorator($myDrink);

// 3. 顾客又说要加布丁
// 把刚才加了珍珠的奶茶,再塞进布丁装饰器里
$myDrink = new PuddingDecorator($myDrink);

echo "最终成品: " . $myDrink->getDescription() . "";
echo "最终价格: " . $myDrink->cost() . " 元";

运行结果如下:

刚开始: 原味奶茶 价格:10
最终成品: 原味奶茶 + 珍珠 + 布丁
最终价格: 16 元

看到了吗?你可以无限嵌套 new Decorator(new Decorator(...)),完全不需要修改 SimpleMilkTea 的代码,也无需构建复杂的继承层级。

5. 何时应该使用装饰器模式?

切勿“手里拿着锤子,看什么都是钉子”。以下场景强烈推荐使用装饰器模式:

  • 动态扩展功能:需要在运行时为对象动态添加额外职责,例如给文本添加加粗效果、为 HTTP 请求注入 Token 等。
  • 规避类爆炸:当类的变体过多或功能需要多种排列组合时,装饰器可避免子类疯狂增长。
  • 践行开闭原则 (OCP):对扩展开放,对修改关闭。

在著名的 PHP 框架 Laravel 中,中间件 (Middleware) 的实现机制本质上就是装饰器模式的一种变体(洋葱模型),请求逐层穿过中间件,最后到达控制器。

恭喜你!现在你不仅掌握了装饰器模式,还理解了中间件的底层原理。

来源:https://developer.aliyun.com/article/1744120

游乐网为非赢利性网站,所展示的游戏/软件/文章内容均来自于互联网或第三方用户上传分享,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系youleyoucom@outlook.com。

同类文章
更多
Windows Docker Desktop RabbitMQ生产级部署完整指南

Windows Docker Desktop RabbitMQ生产级部署完整指南

前言 在 Windows 本地开发环境中,直接安装 RabbitMQ 确实颇为周折:需要单独配置 Erlang 运行环境、手动管理环境变量、服务启停全凭手工操作。更令人困扰的是,版本兼容冲突、端口占用、环境不一致等问题层出不穷。笔者见过不少开发者为搭建环境就得耗费整整半天时间。 相比之下,借助 Do

时间:2026-06-29 17:49
AI搜索重构制造业采购逻辑的阿里云企业级GEOCMS优化实践

AI搜索重构制造业采购逻辑的阿里云企业级GEOCMS优化实践

先分享一个切实感受。过去两年,我们与福建制造企业合作较为频繁,发现一个非常突出的现象:超过80%的企业官网,产品参数仍然存放在PDF或图片中。AI爬虫?根本无法抓取。这些企业技术实力不弱、资质证照齐全、应用案例也丰富,但在AI搜索这一全新战场上,它们几乎处于隐身状态。 一、一个正在发生的行业变化 A

时间:2026-06-29 17:48
阿里云Token Plan团队版功能价格与省钱购买指南

阿里云Token Plan团队版功能价格与省钱购买指南

阿里云百炼近期推出了名为“Token Plan 团队版”的全新服务,这一服务专为企业与开发者量身打造,定位为AI大模型订阅平台。通过引入Credits作为统一计量单位,将文本生成、图像生成等多模态AI能力纳入单一计费体系,同时无缝兼容主流AI编程工具及智能体(Agent)生态系统。其核心亮点包括:全

时间:2026-06-29 17:47
阿里云物联网.NET Core客户端位置信息上报

阿里云物联网.NET Core客户端位置信息上报

阿里云物联网平台的位置服务并非一个完全独立的功能模块。位置信息可包含二维坐标与三维坐标,而位置数据的来源本质上是借助设备属性进行上传。换言之,若要让设备上报位置,您需先将其视为一个普通属性进行处理。 1)添加二维位置数据 操作过程十分简洁。进入数据分析 → 空间数据可视化 → 二维数据,点击添加,将

时间:2026-06-29 17:47
年阿里云服务器选型配置与网站部署全攻略

年阿里云服务器选型配置与网站部署全攻略

2026年,阿里云服务器生态已高度成熟,形成了清晰的轻量应用服务器与ECS云服务器两大产品阵营。无论你是计划搭建个人博客、企业官网,还是运营电商平台、进行应用开发,基本都能找到理想的解决方案。本指南将从服务器选型、配置选择、部署流程到安全运维,系统梳理2026年最实用的操作要点,帮助你少走弯路,让网

时间:2026-06-29 17:47
热门专题
更多
刀塔传奇破解版无限钻石下载大全 刀塔传奇破解版无限钻石下载大全
洛克王国正式正版手游下载安装大全 洛克王国正式正版手游下载安装大全
思美人手游下载专区 思美人手游下载专区
好玩的阿拉德之怒游戏下载合集 好玩的阿拉德之怒游戏下载合集
不思议迷宫手游下载合集 不思议迷宫手游下载合集
百宝袋汉化组游戏最新合集 百宝袋汉化组游戏最新合集
jsk游戏合集30款游戏大全 jsk游戏合集30款游戏大全
宾果消消消原版下载大全 宾果消消消原版下载大全
  • 日榜
  • 周榜
  • 月榜