聊到设计模式,很多人的第一反应是“这老古董到底还有没有用”。尤其这两年AI辅助编程工具越来越多,简单业务场景下,让工具直接生成代码确实省事,设计模式这种需要动脑子思考的东西,好像越来越没存在感。但我最近在做多Agent系统开发时,反而发现那些被不少人嫌弃的“经典设计模式”,正以各种变形重新回到核心位置——比如热搜里提到的“主从模式”,本质上就是把子Agent当作一种特殊的Tool来调用,背后其实就是组合、委派、门面这些老模式在现代场景下的新应用。
这篇文章,我会从设计模式的本源讲起,结合真实的Java实现代码,再聊到智能体设计、多Agent协作这些新场景中模式怎么“变形”落地。不管你是准备设计模式期末考试、做设计模式大作业,还是正在研究智能体设计模式PDF资料、想搞懂Java/C++里的设计模式,这篇都值得你花十分钟静下心看完。
1. 设计模式到底是什么——先解决“为什么学它”的问题
1.1 设计模式不是代码,是“经验的抽象”
我见过太多人一上来就背模式的名字和类图,结果真到项目里,完全想不起来用,或者反过来乱用一通。说白了,设计模式不是某段具体代码,它是一类问题的“标准解法模板”。就像厨师做菜,菜谱千变万化,但“先热油、再下葱姜蒜爆香”这个思路是通用的,设计模式就是软件世界里的“烹饪套路”。
打个比方:你第一次写支付对接,可能直接在一个Service里堆if-else,今天加支付宝,明天加微信,后天加银联,每次上线都提心吊胆。等你写多了,你会发现这类“多种实现可替换”的问题,有一个固定的拆解思路——把变化的部分抽出来,定义一个统一接口,然后让不同实现去对接,调用方只认接口。这个思路被总结成文档、画成类图、起了名字,就是“策略模式”。
所以学设计模式,学的不是那张类图,而是“识别问题本质”的能力。当你看到“多种算法/行为可替换”时,能条件反射地想到策略模式;当你看到“全局只能有一个实例”时,能瞬间想起单例模式——这才是学习设计模式真正要达成的东西。
1.2 三大分类:创建型、结构型、行为型
GoF那本经典书把23种模式分成三类,这个分类本身就是一种学习地图:
- 创建型模式(Creational):解决“对象怎么创建”的问题。核心思想是“别直接用new,把创建逻辑封装起来”。典型代表:单例、工厂方法、抽象工厂、建造者、原型。
- 结构型模式(Structural):解决“类和对象怎么组合”的问题。核心思想是“通过组合/继承,把小的结构拼成大的结构”。典型代表:适配器、装饰器、代理、外观、组合、桥接。
- 行为型模式(Behavioral):解决“对象之间怎么协作”的问题。核心思想是“把对象间的通信、职责分配、算法封装起来”。典型代表:策略、观察者、模板方法、迭代器、责任链、状态。
我个人的学习建议是:不要按书的顺序从头看到尾,那会非常枯燥。按“使用频率”来学,优先学单例、工厂、策略、观察者、装饰器这五个,它们覆盖了绝大多数日常开发场景。把这三个分类理解成三个维度:创建是“出生”的问题,结构是“长相”的问题,行为是“相处”的问题,理解起来会轻松很多。
1.3 学习设计模式的最佳姿势:带着问题学,而不是带着模式学
很多初学者是反着来的——先学会了某个模式,然后满世界找地方套。比如学了装饰器模式,恨不得每个类都包一层;学了观察者模式,所有模块之间都用事件通信。这种“手里拿着锤子,看什么都像钉子”的做法,比不学模式还可怕。
正确姿势是:先遇到问题,再去找模式。具体分三步:
- 写代码时遇到“坏味道”,比如一个方法巨长、if-else堆成山、类与类之间耦合到改一处崩三处;
- 停下来问自己:这个问题的本质是什么?是“创建逻辑太复杂”还是“行为可替换”还是“对象间通信混乱”?
- 带着问题去翻模式分类,找到对应解法,理解它的思路后再去重构。
所以这篇文章后面每一章,我都会坚持“问题 → 思路 → 代码 → 适用场景”的写法,而不是直接甩一堆类图。这样你学完才能真正用得上。
2. 五个高频设计模式全解析(含Java代码示例)
2.1 单例模式(Singleton):全局唯一的“办事窗口”
问题场景:配置文件读取类、数据库连接池、线程池、日志管理器。这些对象如果每次用都new一个,会造成资源浪费,甚至数据不一致。比如读取配置文件的类,如果实例化了多个,每个实例各自缓存一份配置,其中一个实例被修改了配置,另一个还蒙在鼓里,查问题的时候能把人逼疯。
核心思路:类的构造方法私有化,外部不能直接new;类自己维护唯一实例,提供全局访问点;所有调用方拿到的都是同一个对象。
public class ConfigManager { // volatile 防止指令重排,双重检查锁定的关键 private static volatile ConfigManager instance; private Map<String, String> configMap; // 构造方法私有,外部无法 new private ConfigManager() { configMap = new HashMap<>(); // 模拟从配置文件加载 configMap.put("app.name", "MyApp"); configMap.put("app.version", "1.0.0"); } public static ConfigManager getInstance() { if (instance == null) { synchronized (ConfigManager.class) { if (instance == null) { instance = new ConfigManager(); } } } return instance; } public String getConfig(String key) { return configMap.get(key); } public void setConfig(String key, String value) { configMap.put(key, value); } }这段代码里有个经典考点“双重检查锁定”(Double-Checked Locking)。先看实例为不为空,不为空直接返回,避免每次都要抢锁;为空的线程进入同步块,里面再查一次,防止多个线程同时通过第一层检查后各自new一个实例。volatile关键字是为了防止instance = new ConfigManager()这一步发生指令重排,在极端情况下其他线程拿到一个“半初始化”的对象。
使用注意:单例模式虽然简单,但滥用会让单元测试变得很痛苦。因为单例的全局状态没法轻松mock,测试之间还会互相污染。后来流行的依赖注入(IoC)容器,本质上就是在“帮你管理单例的生命周期”,而不是让你到处写getInstance()。
2.2 工厂模式(Factory):把“new”这件事集中管理
问题场景:业务代码里到处散落着new,一旦实现类变了,所有地方都要改。比如支付场景,你对接了支付宝、微信、银联,如果每次都在业务代码里写if (type.equals("ali")) return new AliPay();,那每加一个支付方式,所有涉及支付的地方都要动一遍。
核心思路:把“创建哪个对象”的决策逻辑收敛到一个独立的工厂类/方法里。调用方只告诉工厂“我要什么类型”,工厂负责创建并返回具体对象。
public interface PaymentChannel { void pay(double amount); } public class AliPay implements PaymentChannel { @Override public void pay(double amount) { System.out.println("通过支付宝支付:" + amount + "元"); } } public class WeChatPay implements PaymentChannel { @Override public void pay(double amount) { System.out.println("通过微信支付:" + amount + "元"); } } public class PaymentFactory { public static PaymentChannel create(String channelType) { if ("ali".equalsIgnoreCase(channelType)) { return new AliPay(); } if ("wechat".equalsIgnoreCase(channelType)) { return new WeChatPay(); } throw new IllegalArgumentException("不支持的支付渠道:" + channelType); } } // 调用方: PaymentChannel channel = PaymentFactory.create("ali"); channel.pay(99.0);这一段代码写出来,很多人会质疑:这不就是把if-else从业务代码挪到工厂里了吗?好处在哪?
好处在于“变动的收敛”。新增一种支付方式时,你只需要改工厂这一个地方,调用方的代码一行都不用动。而且调用方从“依赖具体类”变成了“依赖接口”,这是依赖倒置原则的体现。更进一步的实践中,工厂方法往往会配合配置文件或者注册表,把类型和实现类的映射关系配置化,连工厂代码都不用改,这就是Spring的BeanFactory干的事情。
2.3 策略模式(Strategy):消灭“神仙打架”的if-else
问题场景:同一个行为,有多种不同的算法/规则,而且运行时要动态切换。比如电商订单的优惠计算:新用户首单减20,满300减50,VIP打8折,不同活动之间还能叠加。如果你用if-else写,每加一个活动,就要往方法里塞一个分支,方法越来越长,改一个分支还容易影响别的分支。
核心思路:定义一组算法,把它们各自封装成独立的类,并且可以互相替换。调用方持有一个策略接口引用,运行时传入具体策略。
public interface DiscountStrategy { double calculate(double originalPrice); } // 新用户立减 public class NewUserDiscount implements DiscountStrategy { private static final double REDUCTION = 20.0; @Override public double calculate(double originalPrice) { return Math.max(0, originalPrice - REDUCTION); } } // 满减 public class FullReductionDiscount implements DiscountStrategy { private final double threshold; private final double reduction; public FullReductionDiscount(double threshold, double reduction) { this.threshold = threshold; this.reduction = reduction; } @Override public double calculate(double originalPrice) { if (originalPrice >= threshold) { return originalPrice - reduction; } return originalPrice; } } // VIP折扣 public class VipDiscount implements DiscountStrategy { private final double rate; public VipDiscount(double rate) { this.rate = rate; } @Override public double calculate(double originalPrice) { return originalPrice * rate; } }在订单服务里,你想用哪个策略就传哪个:
public class OrderService { public double calculateFinalPrice(double originalPrice, DiscountStrategy strategy) { return strategy.calculate(originalPrice); } }策略模式最大的价值不是“少写几个if-else”,而是把每个算法隔离成了独立单元,互不干扰。每个策略类都可以单独测试,可以单独复用。想加一个新活动,新增一个策略类就行,完全不需要动现有代码,完美符合开闭原则。
实际心得:策略模式配合枚举用起来更顺手。把枚举作为策略的“注册表”,每个枚举项关联一个策略实现,调用时直接用枚举映射,既避免了if-else,又方便维护。
2.4 观察者模式(Observer):让“事件”驱动系统解耦
问题场景:某个对象状态变化时,需要通知一堆其他对象做出响应。典型场景:下单成功后,要发短信、发邮件、推送App通知、更新库存、记日志。如果硬编码在订单Service里,每加一个“下单后要做的事”,都要改动订单Service的代码,今天加短信,明天加积分,后天加优惠券,订单类被越改越庞大。
核心思路:定义一对多的依赖关系,一个被观察者(Subject)维护多个观察者(Observer),当被观察者状态变化时,自动通知所有观察者。
public interface OrderEventListener { void onOrderPaid(Order order); } public class SmsNotifier implements OrderEventListener { @Override public void onOrderPaid(Order order) { System.out.println("发送短信通知用户:" + order.getUserPhone()); } } public class StockUpdater implements OrderEventListener { @Override public void onOrderPaid(Order order) { System.out.println("扣减商品库存:" + order.getProductId()); } } public class OrderService { private List<OrderEventListener> listeners = new ArrayList<>(); public void registerListener(OrderEventListener listener) { listeners.add(listener); } public void unregisterListener(OrderEventListener listener) { listeners.remove(listener); } public void payOrder(Order order) { // 核心支付逻辑... System.out.println("订单支付成功:" + order.getOrderId()); // 通知所有观察者 for (OrderEventListener listener : listeners) { listener.onOrderPaid(order); } } }这样一来,下单后要发什么通知、做什么后续动作,完全是“插拔式”的。新业务方只需要实现OrderEventListener接口,然后在初始化时注册进来即可,订单核心逻辑一行都不用改。
容易踩的坑:观察者模式最大的隐患是“回调顺序”和“异常隔离”。如果某个观察者的逻辑崩溃了,会不会影响后面的观察者?所以在真实项目里,通知代码通常会加try-catch,或者使用异步事件总线,不让一个观察者的失败拖垮整个主流程。后面我会在问题排查章节详细说这个。
2.5 装饰器模式(Decorator):给对象“叠加BUFF”
问题场景:你想给一个对象增加功能,但又不想修改它原来的代码,也不想通过继承去造一堆子类。最经典的例子是Java I/O流:BufferedReader包装了FileReader,给文件读取加上了缓冲功能;LineNumberReader再包装BufferedReader,又加上了行号功能。如果每种组合都用继承,那类数量会爆炸。
核心思路:装饰器和被装饰对象实现同一个接口,装饰器内部持有一个被装饰对象,在调用被装饰对象方法的前后,添加额外的行为。
public interface DataSource { void write(String data); String read(); } public class FileDataSource implements DataSource { private String filename; public FileDataSource(String filename) { this.filename = filename; } @Override public void write(String data) { System.out.println("写入文件 " + filename + ":" + data); } @Override public String read() { return "文件内容"; } } // 加密装饰器 public class EncryptedDataSource implements DataSource { private final DataSource wrapper; public EncryptedDataSource(DataSource wrapper) { this.wrapper = wrapper; } @Override public void write(String data) { String encrypted = "encrypted(" + data + ")"; wrapper.write(encrypted); } @Override public String read() { String data = wrapper.read(); return "decrypted(" + data + ")"; } } // 压缩装饰器 public class CompressedDataSource implements DataSource { private final DataSource wrapper; public CompressedDataSource(DataSource wrapper) { this.wrapper = wrapper; } @Override public void write(String data) { wrapper.write("compressed(" + data + ")"); } @Override public String read() { return "decompressed(" + wrapper.read() + ")"; } } // 使用: DataSource source = new FileDataSource("test.txt"); // 先压缩,再加密 DataSource compressed = new CompressedDataSource(source); DataSource encrypted = new EncryptedDataSource(compressed); encrypted.write("Hello World");注意装饰器的嵌套顺序很关键,上面这段代码从外到内是:加密 → 压缩 → 文件。所以写入时,先加密处理数据,然后把加密结果交给压缩装饰器,压缩后再写入文件。读取时流程正好反过来。
和继承的对比:继承是静态的,编译期就确定了增强逻辑;装饰器是动态的,运行时可以自由组合。但装饰器有个缺点——会引入大量小类,调试时整个调用链很长,一层套一层,排查问题会比较费劲。当装饰层级超过三层,我建议考虑是否该重新设计了。
3. 设计模式在智能体开发中的新应用——老树开新花
3.1 主从模式:底层就是组合模式加委派模式
最近“主从模式”这个热词在Agent开发圈子里反复出现。简单说,就是一个主Agent(Master)负责拆解任务、调度,多个子Agent(SubAgent)各司其职,执行具体子任务。这本质上就是“组合模式”(Composite)加上“委派模式”(Delegate)在AI领域的一次华丽转身。
组合模式的核心思想是“部分-整体”的树形结构:客户端可以像处理单个对象一样处理组合对象。主Agent就是树干,子Agent就是树枝树叶。对外暴露的时候,你只需要跟主Agent对话,它内部维护着一个子Agent列表,根据任务类型动态选择让哪个子Agent干活。
再看“子Agent作为Tool调用”这个观点,很多人觉得这是新鲜概念,其实这就是“门面模式”(Facade)的AI变体。门面模式要求为子系统提供统一入口,让客户端不用关心子系统内部有多少模块。在主从Agent设计中,主Agent就是一个门面,你把子Agent包装成Tool的接口形式,调用方根本不用关心“这到底是一个模型函数还是一个拥有独立思考能力的子Agent”,反正都是输入参数、返回结果。
3.2 把SubAgent当作Tool调用——适配器模式的新战场
主流Agent框架里,Tool的抽象通常是一个函数签名:接受一个JSON字符串参数,返回一个JSON字符串结果。如果你想把一个子Agent暴露成Tool给别人调用,就需要写一个适配器(Adapter),把Agent的对话接口转换成Tool的标准接口。
这个适配器做的事情包括:
- 把Tool接收的入参转换成子Agent能理解的System Prompt或者初始消息;
- 把子Agent的多轮对话输出收敛成一个最终结果字符串;
- 处理超时、重试、异常情况;
- 必要时记录调用链,方便追踪。
class SubAgentToolAdapter: """ 把子Agent包装成标准Tool的适配器。 对上层来说,这只是一个可调用的函数; 对子Agent来说,它只是收到了一个任务请求。 """ def __init__(self, sub_agent, max_retries=3): self.sub_agent = sub_agent self.max_retries = max_retries def execute(self, params_json: str) -> str: # 转换入参为Agent可理解的格式 task_prompt = self._convert_to_prompt(params_json) for attempt in range(self.max_retries): try: # 调用子Agent,获取最终结果 result = self.sub_agent.run(task_prompt) return self._convert_to_result(result) except TimeoutError: continue return json.dumps({"status": "failed", "error": "sub_agent timeout"}) def _convert_to_prompt(self, params_json): return f"你是一个专门处理子任务的Agent,请根据以下参数完成任务:{params_json}" def _convert_to_result(self, agent_output): return json.dumps({"status": "success", "result": agent_output})这段代码我建议所有做Agent开发的都好好体会一下。适配器模式在这里干的活,和当年做Android适配、做第三方SDK接入时干的活没有任何本质区别——把不兼容的接口转换成目标系统期望的接口。模式还是那个模式,只是换了身赛博皮肤。
3.3 多Agent协作里的观察者模式与事件驱动
多Agent系统里,Agent之间不是所有时候都要同步调用。比如一个Agent在等待另一个Agent的结果时,可以先去干别的活,等结果出来了再回来处理。这种“异步协作”用到的就是观察者模式的升级版——事件总线。
每个Agent可以订阅它感兴趣的事件类型,比如“任务完成”“检索到新资料”“用户中断”。当某个Agent完成了自己的工作,它往事件总线里发布一条事件,其他订阅了该事件的Agent就会被异步唤醒。这和前面订单服务的观察者模式逻辑一模一样,只是传播介质从进程内的方法调用变成了消息队列。
我当时做多Agent编排的时候,就把消息总线设计成了组合模式:一个事件可以拆成多个子事件,也可以合并多个子事件的结果。单一事件订阅是叶子节点,组合事件订阅是树枝节点。这种设计让整个编排系统非常灵活,新加一种Agent协作方式,基本就是加几个订阅关系的事。
所以你看,设计模式的价值不在于“背下来”,而在于你能不能在遇到新问题时,认出它“旧相识”的本质。主从、Tool化、事件驱动,这些听起来高大上的Agent概念,骨子里还是老朋友。
4. 设计模式怎么选——避免从“不用模式”走到“乱用模式”
4.1 判断标准:每次纠结时问自己三个问题
我在设计模式大作业评审和技术评审中,经常看到一种情况:代码里堆了一堆模式,但没人说得清为什么用。这里分享一个我自己用了很多年的判断框架,遇到“要不要上某个模式”的纠结时,先问三个问题:
- 变化的频率有多高?如果这个维度几乎不变,比如系统里就一种支付方式,那工厂模式纯属多余,直接new就完事了。模式是为“变化”服务的,没有变化就没有模式的用武之地。
- 调用方是否需要感知具体实现?如果所有调用方都只关心接口行为,不关心具体类型,那策略模式、工厂模式就值得考虑。反之,如果调用方本来就依赖具体类,硬抽接口反而是画蛇添足。
- 对象间的协作是同步还是异步?如果A发生变化后,B必须立刻同步响应,那观察者模式要考虑好时序和异常隔离。如果允许异步,那事件总线更合适。
这三个问题问完,90%的“要不要用模式”之争都能落地。
4.2 常见误用:把简单问题复杂化
比如网上特别火的“用策略模式消灭if-else”这类文章,我持保留态度。如果你的分支只有两三个,而且一两年都不见得新增一个,那if-else完全没问题,可读性甚至更好。为了消灭if-else而搞出五六个类、一个工厂、一堆测试,这叫“过度设计”。
另一种典型误用是“观察者模式遍地开花”。我曾经维护过一个系统,团队把所有的模块间通信都改成了事件驱动,结果全局搜索某个对象在哪些地方被修改,根本搜不到,因为都通过事件广播出去了,调用链完全不可见。排查一个问题要在十几个事件的订阅关系里跳来跳去。观察者模式适合“一对多”“多对一”的解耦,但不是所有依赖都应该解耦,有些直接调用的代码,清晰程度远高于事件广播。
还有一类误用是“适配器模式乱wrap”。框架返回的对象,本来直接能用,有人非要包一层自己的适配器,美其名曰“隔离第三方依赖”。结果是每次框架升级,适配器代码要跟着改,平白无故多了一层维护成本。适配器模式用在不兼容的接口对接上,不是为了“以防以后要换框架”这种虚构的假设服务的。
4.3 什么时候真的不用模式
这里给大家吃个定心丸:很多项目根本不需要刻意使用设计模式。单体小系统、内部工具、脚本、原型验证,直接用最简单直白的写法反而最高效。模式是给“复杂度”准备的武器,不是给“面子”准备的装饰。
我个人的经验线是:
- 代码不超过几千行、改动频率极低 → 不用模式,怎么简单怎么来;
- 某个维度确认会持续变化(比如支付渠道会持续增加)→ 针对这个维度上模式,其余保持简单;
- 团队协作人数多、模块边界清晰 → 在模块边界上使用模式,模块内部保持简单。
模式的选择和架构分层一样,都是在“管理复杂度”,而不是“展示技术”。
5. 真实项目中的避坑经验与问题排查实录
5.1 单例模式的线程安全问题
前面代码里用了volatile加双重检查,但在实际项目中,我最推荐的方式不是自己写单例,而是用枚举或者静态内部类。枚举单例是《Effective Java》作者推荐的写法,代码简洁,而且天然防止反序列化破坏单例。
public enum ConfigManager { INSTANCE; private Map<String, String> configMap = new HashMap<>(); public String getConfig(String key) { return configMap.get(key); } }这段代码相比手写双检锁,不仅短,而且线程安全机制由JVM保证。除非你需要继承某个类(枚举不能继承),否则优先用枚举单例。这是很多设计模式教程里不会强调的实战细节。
5.2 观察者模式的异常隔离与内存泄漏
我在订单系统里用观察者模式时,遇到过一个问题:某个观察者抛了异常,导致整个payOrder事务回滚,用户钱扣了但订单状态没更新,客诉电话直接爆掉。后来所有通知逻辑都加了try-catch,并且把短信、邮件这些非核心通知改成了异步执行。
还有内存泄漏问题。如果你把一个观察者注册到全局的事件管理中心,但忘记在对象销毁时调用unregisterListener,那这个对象永远被事件中心持有,垃圾回收器无法回收它。这就是“隐性内存泄漏”。在Web应用里,Spring生命周期管理的Bean通常没问题,但如果你手动new了一个观察者并注册进去,用完后一定要记得反注册。
5.3 策略模式与if-else的边界
策略模式的注册表方式也踩过坑。我见过一种写法:把策略实例放进Map,key是字符串类型,用Spring的@Component自动注入。听着很优雅,但一旦策略多了,定位“哪个策略生效”就变成了查Map的Key,在代码里全局搜字符串,调试体验很差。
我的建议是:策略的编号都用枚举表示,不要用裸字符串。这样编译器能帮你检查,IDE能帮你跳转,重构的时候也安全得多。枚举加上对应的策略字段,一个枚举项对应一个策略实现,查找和匹配一目了然。
public enum DiscountType { NEW_USER(new NewUserDiscount()), FULL_REDUCTION(new FullReductionDiscount(300, 50)), VIP(new VipDiscount(0.8)); private final DiscountStrategy strategy; DiscountType(DiscountStrategy strategy) { this.strategy = strategy; } public DiscountStrategy getStrategy() { return strategy; } }5.4 快速排查清单
最后整理一个我在设计模式评审、代码复查时常用的自查表,分享出来给大家参考:
| 检查项 | 可能的问题 | 自查方法 |
|---|---|---|
| 单例是否线程安全 | 懒加载未加锁或未用volatile | 双检锁是否用了volatile;是否可用枚举替代 |
| 观察者是否异常隔离 | 一个观察者崩溃影响主流程 | 通知逻辑是否有try-catch;是否异步化 |
| 观察者是否反注册 | 长期运行导致内存泄漏 | 对象销毁路径是否调用unregister |
| 工厂是否收敛 | 调用方仍散落new具体类 | 全局搜索关键实现类的new关键字 |
| 策略是否可扩展 | 新策略需要改动旧代码 | 新增策略是否只需新增类,不改动调用方 |
| 装饰器层级是否合理 | 嵌套过深,调用链难以追踪 | 装饰层数是否超过3层,能否用AOP替代 |
把这些检查项当成代码评审的“体检表”,每次提交前过一遍,能省下不少线上事故的排查时间。
聊到这儿,设计模式的“老”与“新”其实已经打通了。我做多Agent系统时最大的体会是:技术框架、热词会一直变,但“如何管理复杂度”这件事,几十年来内核一直没有变。你去看那些被称赞“代码写得好”的项目,大概率不是因为它用了多少花哨模式,而是它在正确的地方,用了恰到好处的模式,把复杂问题拆成了人能理解的结构。如果你正在学设计模式,我的建议很简单:把这篇文章里的五个模式先跑通,再去找个真实项目里“代码坏味道”明显的地方,尝试用对应模式重构一遍。踩过坑、动过手,这些模式才真正算是你的。