二十三种设计模式

2 工厂方法模式

工厂模式(Factory Pattern)是 Java 中最常用的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。

  • 在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。

《设计模式》一书中,工厂模式被分为了三种:简单工厂、工厂方法和抽象工厂。(不过,在书中作者将简单工厂模式看作是工厂方法模式的一种特例。)

接下来我会介绍三种工厂模式的原理及使用

  • 简单工厂模式(不属于GOF的23种经典设计模式)
  • 工厂方法模式
  • 抽象工厂模式

2.1 需求: 模拟发放奖品业务

需求: 为了让我们的案例更加贴近实际开发, 这里我们来模拟一下互联网电商中促销拉新下的业务场景, 新用户注册立即参与抽奖活动 ,奖品的种类有: 打折券, 免费优酷会员,小礼品.

2.2 原始开发方式

不考虑设计原则,不使用设计模式的方式进行开发

在不考虑任何代码的可扩展性的前提下,只为了尽快满足需求.我们可以这样去设计这个业务的代码结构:

1) 实体类

名称描述
AwardInfo获奖信息对应实体类
DiscountInfo打折券信息对应实体类
YouKuMember优酷会员对应实体类
SmallGiftInfo小礼品信息对应实体类
DiscountResult打折券操作响应结果封装类
public class AwardInfo {private String uid; //用户唯一IDprivate Integer awardType; //奖品类型: 1 打折券 ,2 优酷会员,3 小礼品private String awardNumber; //奖品编号Map<String, String> extMap; //额外信息}public class DiscountInfo {//属性信息省略......
}public class YouKuMember {//属性信息省略......
}public class SmallGiftInfo {private String userName;              // 用户姓名private String userPhone;             // 用户手机private String orderId;               // 订单IDprivate String relAddress;            // 收货地址}public class DiscountResult {private String status; // 状态码private String message; // 信息
}

2) 服务层

名称功能描述
DiscountServiceDiscountResult sendDiscount(String uid,String number)模拟打折券服务
YouKuMemberServicevoid openMember(String bindMobile , String number)模拟赠送优酷会员服务
SmallGiftServiceBoolean giveSmallGift(SmallGiftInfo smallGiftInfo)模拟礼品服务
public class DiscountService {public DiscountResult sendDiscount(String uid, String number){System.out.println("向用户发放打折券一张: " + uid + " , " + number);return new DiscountResult("200","发放打折券成功");}
}public class YouKuMemberService {public void openMember(String bindMobile , String number){System.out.println("发放优酷会员: " + bindMobile + " , " + number);}
}public class SmallGiftService {public Boolean giveSmallGift(SmallGiftInfo smallGiftInfo){System.out.println("小礼品已发货,获奖用户注意查收! " + JSON.toJSON(smallGiftInfo));return true;}
}

3) 控制层

名称功能描述
DeliverControllerResponseResult awardToUser(AwardInfo awardInfo)按照类型的不同发放商品
奖品类型: 1 打折券 ,2 优酷会员,3 小礼品
public class DeliverController {/*** 按照类型的不同发放商品*     奖品类型: 1 打折券 ,2 优酷会员,3 小礼品*/public void awardToUser(AwardInfo awardInfo){if(awardInfo.getAwardType() == 1){ //打折券DiscountService discountService = new DiscountService();DiscountResult result = discountService.sendDiscount(awardInfo.getUid(), awardInfo.getAwardNumber());System.out.println("打折券发放成功!"+ JSON.toJSON(result));}else if(awardInfo.getAwardType() == 2){ //优酷会员//获取用户手机号String bindMobile = awardInfo.getExtMap().get("phone");//调用serviceYouKuMemberService youKuMemberService = new YouKuMemberService();youKuMemberService.openMember(bindMobile,awardInfo.getAwardNumber());System.out.println("优酷会员发放成功!");}else if(awardInfo.getAwardType() == 3){ /*小礼品封装收货用户信息*/SmallGiftInfo smallGiftInfo = new SmallGiftInfo();smallGiftInfo.setUserName(awardInfo.getExtMap().get("username"));smallGiftInfo.setOrderId(UUID.randomUUID().toString());smallGiftInfo.setRelAddress(awardInfo.getExtMap().get("adderss"));SmallGiftService smallGiftService = new SmallGiftService();Boolean isSuccess = smallGiftService.giveSmallGift(smallGiftInfo);System.out.println("小礼品发放成功!" + isSuccess);}}}

4) 测试

通过单元测试,来对上面的接口进行测试,验证代码质量.

public class TestApi01 {//测试发放奖品接口@Testpublic void test01(){DeliverController deliverController = new DeliverController();//1. 发放打折券优惠AwardInfo info1 = new AwardInfo();info1.setUid("1001");info1.setAwardType(1);info1.setAwardNumber("DEL12345");deliverController.awardToUser(info1);//2. 发放优酷会员AwardInfo info2 = new AwardInfo();info2.setUid("1002");info2.setAwardType(2);info2.setAwardNumber("DW12345");Map<String,String> map = new HashMap<>();map.put("phone","13512341234");info2.setExtMap(map);deliverController.awardToUser(info2);//2. 发放小礼品AwardInfo info3 = new AwardInfo();info3.setUid("1003");info3.setAwardType(3);info3.setAwardNumber("SM12345");Map<String,String> map2 = new HashMap<>();map2.put("username","大远");map2.put("phone","13512341234");map2.put("address","北京天安门");info3.setExtMap(map2);deliverController.awardToUser(info3);}
}

对于上面的实现方式,如果我们有想要添加的新的奖品时,势必要改动DeliverController的代码,违反开闭原则.而且如果有的抽奖接口出现问题,那么对其进行重构的成本会非常高.

除此之外代码中有一组if分支判断逻辑,现在看起来还可以,但是如果经历几次迭代和拓展,后续ifelse肯定还会增加.到时候接手这段代码的研发将会十分痛苦.

2.3 简单工厂模式

2.3.1 简单工厂模式介绍

简单工厂不是一种设计模式,反而比较像是一种编程习惯。简单工厂模式又叫做静态工厂方法模式(static Factory Method pattern),它是通过使用静态方法接收不同的参数来返回不同的实例对象.

实现方式:

​ 定义一个工厂类,根据传入的参数不同返回不同的实例,被创建的实例具有共同的父类或接口。

适用场景:
  (1)需要创建的对象较少。
  (2)客户端不关心对象的创建过程。

2.3.2 简单工厂原理

简单工厂包含如下角色:

  • 抽象产品 :定义了产品的规范,描述了产品的主要特性和功能。
  • 具体产品 :实现或者继承抽象产品的子类
  • 具体工厂 :提供了创建产品的方法,调用者通过该方法来获取产品。

2.3.3 简单工厂模式重构代码

1) service

/*** 免费商品发放接口* @author spikeCong* @date 2022/9/8**/
public interface IFreeGoods {ResponseResult sendFreeGoods(AwardInfo awardInfo);}
/*** 模拟打折券服务* @author spikeCong* @date 2022/9/8**/
public class DiscountFreeGoods implements IFreeGoods {@Overridepublic ResponseResult sendFreeGoods(AwardInfo awardInfo) {System.out.println("向用户发放一张打折券: " + awardInfo.getUid() + " , " + awardInfo.getAwardNumber());return new ResponseResult("200","打折券发放成功!");}
}/*** 小礼品发放服务* @author spikeCong* @date 2022/9/8**/
public class SmallGiftFreeGoods implements IFreeGoods {@Overridepublic ResponseResult sendFreeGoods(AwardInfo awardInfo) {SmallGiftInfo smallGiftInfo = new SmallGiftInfo();smallGiftInfo.setUserPhone(awardInfo.getExtMap().get("phone"));smallGiftInfo.setUserName(awardInfo.getExtMap().get("username"));smallGiftInfo.setAddress(awardInfo.getExtMap().get("address"));smallGiftInfo.setOrderId(UUID.randomUUID().toString());System.out.println("小礼品发放成,请注意查收: " + JSON.toJSON(smallGiftInfo));return new ResponseResult("200","小礼品发送成功",smallGiftInfo);}
}/*** 优酷 会员服务* @author spikeCong* @date 2022/9/8**/
public class YouKuMemberFreeGoods implements IFreeGoods {@Overridepublic ResponseResult sendFreeGoods(AwardInfo awardInfo) {String phone = awardInfo.getExtMap().get("phone");System.out.println("发放优酷会员成功,绑定手机号: " + phone);return new ResponseResult("200","优酷会员发放成功!");}
}

2) factory

/*** 具体工厂: 生成免费商品* @author spikeCong* @date 2022/9/9**/
public class FreeGoodsFactory {public static IFreeGoods getInstance(Integer awardType){IFreeGoods iFreeGoods = null;if(awardType == 1){  //打折券iFreeGoods = new DiscountFreeGoods();}else if(awardType == 2){ //优酷会员iFreeGoods = new YouKuMemberFreeGoods();}else if(awardType == 3){ //小礼品iFreeGoods = new SmallGiftFreeGoods();}return iFreeGoods;}
}

3)controller

public class DeliverController {//发放奖品public ResponseResult awardToUser(AwardInfo awardInfo){try {IFreeGoods freeGoods = FreeGoodsFactory.getInstance(awardInfo.getAwardTypes());ResponseResult responseResult = freeGoods.sendFreeGoods(awardInfo);return responseResult;} catch (Exception e) {e.printStackTrace();return new ResponseResult("201","奖品发放失败!");}}
}

4) 测试

通过单元测试,来对上面的接口进行测试,验证代码质量.

public class TestApi02 {DeliverController deliverController = new DeliverController();@Testpublic void test01(){//1. 发放打折券优惠AwardInfo info1 = new AwardInfo();info1.setUid("1001");info1.setAwardTypes(1);info1.setAwardNumber("DEL12345");ResponseResult result = deliverController.awardToUser(info1);System.out.println(result);}@Testpublic void test02(){//2. 发放优酷会员AwardInfo info2 = new AwardInfo();info2.setUid("1002");info2.setAwardTypes(2);info2.setAwardNumber("DW12345");Map<String,String> map = new HashMap<>();map.put("phone","13512341234");info2.setExtMap(map);ResponseResult result1 = deliverController.awardToUser(info2);System.out.println(result1);}@Testpublic void test03(){//3. 发放小礼品AwardInfo info3 = new AwardInfo();info3.setUid("1003");info3.setAwardTypes(3);info3.setAwardNumber("SM12345");Map<String,String> map2 = new HashMap<>();map2.put("username","大远");map2.put("phone","13512341234");map2.put("address","北京天安门");info3.setExtMap(map2);ResponseResult result2 = deliverController.awardToUser(info3);System.out.println(result2);}
}
2.3.4 简单工厂模式总结

优点:

封装了创建对象的过程,可以通过参数直接获取对象。把对象的创建和业务逻辑层分开,这样以后就避免了修改客户代码,如果要实现新产品直接修改工厂类,而不需要在原代码中修改,这样就降低了客户代码修改的可能性,更加容易扩展。

缺点:

增加新产品时还是需要修改工厂类的代码,违背了“开闭原则”。

2.4 工厂方法模式

2.4.1 工厂方法模式介绍

工厂方法模式 Factory Method pattern,属于创建型模式.

概念: 定义一个用于创建对象的接口,让子类决定实例化哪个产品类对象。工厂方法使一个产品类的实例化延迟到其工厂的子类。

2.4.2 工厂方法模式原理

工厂方法模式的目的很简单,就是封装对象创建的过程,提升创建对象方法的可复用性

工厂方法模式的主要角色:

  • 抽象工厂:提供了创建产品的接口,调用者通过它访问具体工厂的工厂方法来创建产品。
  • 具体工厂:主要是实现抽象工厂中的抽象方法,完成具体产品的创建。
  • 抽象产品:定义了产品的规范,描述了产品的主要特性和功能。
  • 具体产品:实现了抽象产品角色所定义的接口,由具体工厂来创建,它同具体工厂之间一一对应。

我们直接来看看工厂方法模式的 UML 图:

2.4.3 工厂方法模式重构代码

为了提高代码扩展性,我们需要将简单工厂中的if分支逻辑去掉,通过增加抽象工厂(生产工厂的工厂)的方式,让具体工厂去进行实现,由具体工厂来决定实例化哪一个具体的产品对象.

抽象工厂

public interface FreeGoodsFactory {IFreeGoods getInstance();
}

具体工厂

public class DiscountFreeGoodsFactory implements FreeGoodsFactory {@Overridepublic IFreeGoods getInstance() {return new DiscountFreeGoods();}
}public class SmallGiftFreeGoodsFactory implements FreeGoodsFactory {@Overridepublic IFreeGoods getInstance() {return new SmallGiftFreeGoods();}
}

Controller

public class DeliverController {/*** 按照类型的不同发放商品*/public ResponseResult awardToUser(AwardInfo awardInfo){FreeGoodsFactory freeGoodsFactory = null;if(awardInfo.getAwardType() == 1){freeGoodsFactory = new DiscountFreeGoodsFactory();}else if(awardInfo.getAwardType() == 2){freeGoodsFactory = new SmallGiftFreeGoodsFactory();}IFreeGoods freeGoods = freeGoodsFactory.getInstance();System.out.println("=====工厂方法模式========");ResponseResult result = freeGoods.sendFreeGoods(awardInfo);return result;}}

从上面的代码实现来看,工厂类对象的创建逻辑又耦合进了 awardToUser() 方法中,跟我们最初的代码版本非常相似,引入工厂方法非但没有解决问题,反倒让设计变得更加复杂了。

那怎么 来解决这个问题呢?

我们可以为工厂类再创建一个简单工厂,也就是工厂的工厂,用来创建工厂类对象。

/*** 用简单方法模式实现: 工厂的工厂,作用是不需要每次创建新的工厂对象* @author spikeCong* @date 2022/9/9**/
public class FreeGoodsFactoryMap {private static final Map<Integer,FreeGoodsFactory> cachedFactories = new HashMap<>();static{cachedFactories.put(1, new DiscountFreeGoodsFactory());cachedFactories.put(2, new SmallGiftFreeGoodsFactory());}public static FreeGoodsFactory getParserFactory(Integer type){if(type == 1){FreeGoodsFactory freeGoodsFactory = cachedFactories.get(1);return freeGoodsFactory;}else if(type ==2){FreeGoodsFactory freeGoodsFactory = cachedFactories.get(2);return freeGoodsFactory;}return null;}
}

Controller

/*** 发放奖品接口* @author spikeCong* @date 2022/9/7**/
public class DeliverController {/*** 按照类型的不同发放商品*/public ResponseResult awardToUser(AwardInfo awardInfo){//根据类型获取工厂FreeGoodsFactory goodsFactory = FreeGoodsFactoryMap.getParserFactory(awardInfo.getAwardType());//从工厂中获取对应实例IFreeGoods freeGoods = goodsFactory.getInstance();System.out.println("=====工厂方法模式========");ResponseResult result = freeGoods.sendFreeGoods(awardInfo);return result;}
}

现在我们的代码已经基本上符合了开闭原则,当有新增的产品时,我们需要做的事情包括:

  1. 创建新的产品类,并且让该产品实现抽象产品接口
  2. 创建产品类对应的具体工厂,并让具体工厂实现抽象工厂
  3. 将新的具体工厂对象,添加到FreeGoodsFactoryMap的cachedFactories中即可,需要改动的代码改动的非常少.
2.4.4 工厂方法模式总结

工厂方法模优缺点

优点:

  • 用户只需要知道具体工厂的名称就可得到所要的产品,无须知道产品的具体创建过程;
  • 在系统增加新的产品时只需要添加具体产品类和对应的具体工厂类,无须对原工厂进行任何修改,满足开闭原则;

缺点:

  • 每增加一个产品就要增加一个具体产品类和一个对应的具体工厂类,这增加了系统的复杂度。

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

  • 需要使用很多重复代码创建对象时,比如,DAO 层的数据对象、API 层的 VO 对象等。
  • 创建对象要访问外部信息或资源时,比如,读取数据库字段,获取访问授权 token 信息,配置文件等。
  • 创建需要统一管理生命周期的对象时,比如,会话信息、用户网页浏览轨迹对象等。
  • 创建池化对象时,比如,连接池对象、线程池对象、日志对象等。这些对象的特性是:有限、可重用,使用工厂方法模式可以有效节约资源。
  • 希望隐藏对象的真实类型时,比如,不希望使用者知道对象的真实构造函数参数等。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/70936.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基于C语言对CAPL语法基础的理解

.CAPL是基于C语言开发的&#xff0c;专门用于CANalyzer和CANoe工具环境&#xff0c;但是CAPL简化了C语言&#xff0c;移除了复杂的指针概念&#xff0c;和一些不常用的关键字。 2.CAPL 脚本是基于事件驱动的&#xff0c;任何事件都有可能触发CAPL脚本的执行&#xff0c;比如&a…

【Java SE】Java中String的内存原理

参考笔记&#xff1a; Java String 类深度解析&#xff1a;内存模型、常量池与核心机制_java stringx、-CSDN博客 解析java中String的内存原理_string s1 new string("ab");内存分析-CSDN博客 目录 1.String初识 2.字符串字面量 3.内存原理图 4. 示例验证 4.…

Prometheus + Grafana 监控

Prometheus Grafana 监控 官网介绍&#xff1a;Prometheus 是一个开源系统 监控和警报工具包最初由 SoundCloud 构建。自 2012 年成立以来&#xff0c;许多 公司和组织已经采用了 Prometheus&#xff0c;并且该项目具有非常 活跃的开发人员和用户社区。它现在是一个独立的开源…

【Python爬虫(95)】Python爬虫进阶:构建大型垂直领域爬虫系统

【Python爬虫】专栏简介:本专栏是 Python 爬虫领域的集大成之作,共 100 章节。从 Python 基础语法、爬虫入门知识讲起,深入探讨反爬虫、多线程、分布式等进阶技术。以大量实例为支撑,覆盖网页、图片、音频等各类数据爬取,还涉及数据处理与分析。无论是新手小白还是进阶开发…

Node.js定义以及性能优化

Node.js Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行时&#xff0c;广泛用于构建高性能的网络应用。以下是一些常见的 Node.js 面试题及其解答&#xff0c;帮助你准备面试&#xff1a; 1. 什么是 Node.js&#xff1f; Node.js 是一个基于 Chrome V8 引擎的 JavaSc…

开源|Documind协同文档(接入deepseek-r1、支持实时聊天)

Documind &#x1f680; 项目介绍 Documind 一个支持实时聊天和接入deepseek-r1模型AI助手的协同文档编辑项目 前端&#xff1a;NextJS React TailwindCSS ShadcnUl Tiptap Zustand后端&#xff1a;NextJS Convex Liveblocks Clerk项目预览&#xff1a;Documind 预览…

JVM内存模型详解:各个区域的作用与原理

引言 Java虚拟机&#xff08;JVM&#xff09;是Java程序运行的核心环境&#xff0c;它负责管理程序的内存、执行字节码以及提供跨平台的支持。理解JVM的内存模型对于编写高效、稳定的Java程序至关重要。本文将详细介绍JVM的内存模型&#xff0c;并深入探讨各个内存区域的作用和…

机器学习之集成学习思维导图

学习笔记—机器学习-集成学习思维导图 20250227&#xff0c;以后复习看&#xff08;周老师的集成学习&#xff09; PS&#xff1a;图片看不清&#xff0c;可以下载下来看。 往期思维导图&#xff1a; 机器学习之集成学习Bagging&#xff08;随机深林、VR-树、极端随机树&…

【http://noi.openjudge.cn/】4.3算法之图论——1538:Gopher II

[【http://noi.openjudge.cn/】4.3算法之图论——1538:Gopher II] 题目 查看提交统计提问 总时间限制: 2000ms 内存限制: 65536kB 描述 The gopher family, having averted the canine threat, must face a new predator. The are n gophers and m gopher holes, each at di…

Apache Spark中的依赖关系与任务调度机制解析

Apache Spark中的依赖关系与任务调度机制解析 在Spark的分布式计算框架中,RDD(弹性分布式数据集)的依赖关系是理解任务调度、性能优化及容错机制的关键。宽依赖(Wide Dependency)与窄依赖(Narrow Dependency)作为两种核心依赖类型,直接影响Stage划分、Shuffle操作及容…

【计算机网络】TCP协议相关总结,TCP可靠性的生动讲解

TCP 可靠性 确保快递不丢、不乱、不过载 机制作用&#xff08;快递类比&#xff09;防止的问题检验和检查包裹是否损坏&#xff0c;损坏就重新发数据出错序列号给每个包裹编号&#xff0c;按顺序整理乱序、重复确认应答每送到一件&#xff0c;就让收件人签收丢失滑动窗口控制…

Go基于协程池的延迟任务调度器

原理 通过用一个goroutine以及堆来存储要待调度的延迟任务&#xff0c;当达到调度时间后&#xff0c;将其添加到协程池中去执行。 主要是使用了chan、Mutex、atomic及ants协程池来实现。 用途 主要是用于高并发及大量定时任务要处理的情况&#xff0c;如果使用Go协程来实现每…

杰发科技AC7801——滴答定时器获取时间戳

1. 滴答定时器 杰发科技7801内部有一个滴答定时器&#xff0c;该定时器是M0核自带的&#xff0c;因此可以直接用该定时器来获取时间戳。 同样&#xff0c;7803也可以使用该方式获取时间戳。 2. 滴答定时器原理 SysTick是一个24位的递减计数器&#xff0c;它从预设的重装载值…

湖仓一体概述

湖仓一体之前&#xff0c;数据分析经历了数据库、数据仓库和数据湖分析三个时代。 首先是数据库&#xff0c;它是一个最基础的概念&#xff0c;主要负责联机事务处理&#xff0c;也提供基本的数据分析能力。 随着数据量的增长&#xff0c;出现了数据仓库&#xff0c;它存储的是…

第十五届蓝桥杯单片机组4T模拟赛三(第二套)

本套试题在4T平台中的名字为第15届蓝桥杯单片机组模拟考试三&#xff0c;不知道哪套是4T的模拟赛&#xff0c;所以两套都敲一遍练练手感。 为了代码呈现美观&#xff0c;本文章前面的各个模块在main函数中的处理函数均未添加退出处理&#xff0c;在最后给出的完整代码中体现。 …

CT技术变迁史——CT是如何诞生的?

第一代CT(平移-旋转) X线球管为固定阳极,发射X线为直线笔形束,一个探测器,采用直线和旋转扫描相结合,即直线扫描后,旋转1次,再行直线扫描,旋转180完成一层面扫描,扫描时间3~6分钟。矩阵象素256256或320320。仅用于颅脑检查。 第二代CT (平移-旋转) 与第一代无质…

Virtual Box虚拟机安装苹果Monterey和big sur版本实践

虚拟机安装苹果实践&#xff0c;在Windows10系统&#xff0c;安装Virtual Box7.1.6&#xff0c;安装虚拟苹果Monterey版本Monterey (macOS 12) 。碰到的主要问题是安装光盘不像Windows那么容易拿到&#xff0c;而且根据网上很多文章制作的光盘&#xff0c;在viritualBox里都无法…

dify基础之prompts

摘要&#xff1a;在大型语言模型&#xff08;LLM&#xff09;应用中&#xff0c;Prompt&#xff08;提示词&#xff09;是连接用户意图与模型输出的核心工具。本文从概念、组成、设计原则到实践案例&#xff0c;系统讲解如何通过Prompt解锁LLM的潜能&#xff0c;提升生成内容的…

【学写LibreCAD】0 仿写LibreCAD简介

一、LibreCAD 核心模块&#xff1a; 核心模块&#xff08;Core&#xff09; 功能&#xff1a;处理 CAD 的核心逻辑&#xff0c;如几何计算、图形对象管理、坐标系转换等。关键组件&#xff1a; 图形对象&#xff1a;如直线、圆、圆弧、多段线等。数学工具&#xff1a;向量、矩…

HTML元素,标签到底指的哪块部分?单双标签何时使用?

1. 标签&#xff08;Tag&#xff09; vs 元素&#xff08;Element&#xff09; 标签&#xff08;Tag&#xff09; 标签是 HTML 中用于定义元素的符号&#xff0c;用尖括号 < > 包裹。例如 <img> 是标签。元素&#xff08;Element&#xff09; 元素是由 标签 内容…