设计模式实战:C++ 装饰者模式与适配器模式的应用
C++ 中装饰者模式与适配器模式的核心思想及实现细节。通过电商订单优惠系统和支付网关案例,展示了如何利用装饰者模式动态扩展对象功能,避免继承导致的类爆炸;利用适配器模式解决接口不兼容问题,实现第三方库集成。内容涵盖角色划分、代码示例、智能指针管理、优缺点分析及避坑指南,帮助开发者在实际业务场景中灵活运用两种结构型设计模式,构建灵活可扩展的系统架构。

C++ 中装饰者模式与适配器模式的核心思想及实现细节。通过电商订单优惠系统和支付网关案例,展示了如何利用装饰者模式动态扩展对象功能,避免继承导致的类爆炸;利用适配器模式解决接口不兼容问题,实现第三方库集成。内容涵盖角色划分、代码示例、智能指针管理、优缺点分析及避坑指南,帮助开发者在实际业务场景中灵活运用两种结构型设计模式,构建灵活可扩展的系统架构。

重点: 装饰者模式的动态扩展逻辑、适配器模式的接口适配技巧、两种模式的组合使用场景
在 C++ 开发中,结构型设计模式专注于解决'类与对象的组合方式'问题,通过合理的结构组合,实现功能复用、接口兼容或功能扩展。相比行为型模式关注'对象交互',结构型模式更侧重'对象组装'。
实际开发中,我们常遇到以下结构相关的问题:
结构型模式通过标准化的组合方式,将'核心功能'与'扩展功能''现有接口'与'目标接口'分离,例如装饰者模式通过组合实现功能动态扩展,适配器模式通过转换实现接口兼容,从而解决上述问题。
结构型模式的设计始终围绕以下原则展开,这也是本章两种模式的核心指导思想:
装饰者模式(Decorator Pattern)又称包装器模式(Wrapper Pattern),其核心思想是'动态地给一个对象添加一些额外的职责,就像在墙上刷油漆一样,不改变对象本身的结构,却能增强其功能'。
抽象装饰者与具体组件继承自同一抽象组件,确保装饰者与被装饰者类型一致,可相互替换;抽象装饰者持有抽象组件的引用,通过组合方式将装饰者与被装饰者关联,实现'装饰者包裹被装饰者'的嵌套结构,从而动态添加功能。
以'电商订单优惠系统'为例:订单(具体组件)可享受多种优惠(具体装饰者),如优惠券抵扣、积分抵扣、满减优惠,且优惠可灵活组合(如同时使用优惠券 + 积分),使用装饰者模式可动态扩展优惠功能。
#include <iostream>
#include <string>
using namespace std;
// 抽象组件:订单接口
class OrderComponent {
public:
// 纯虚函数:计算最终价格
virtual float CalculateFinalPrice() const = 0;
// 纯虚函数:获取订单描述
virtual string GetOrderDescription() const = 0;
virtual ~OrderComponent() {}
};
// 具体组件:基础订单(无任何优惠)
class BasicOrder : public OrderComponent {
private:
string orderId; // 订单 ID
float originalPrice; // 商品原价
public:
BasicOrder(const string& id, float price) : orderId(id), originalPrice(price) {
if (price < 0) {
throw invalid_argument("商品原价不能为负数!");
}
}
// 计算最终价格(无优惠,返回原价)
float CalculateFinalPrice() const override {
return originalPrice;
}
// 获取订单描述
string GetOrderDescription() const override {
return "订单 [" + orderId + "] 基础价格:" + to_string(originalPrice) + "元";
}
// 获取原价(供装饰者使用)
float GetOriginalPrice() const {
return originalPrice;
}
};
// 抽象装饰者:优惠装饰器
class DiscountDecorator : public OrderComponent {
private:
// 持有抽象组件的引用(核心:组合方式关联被装饰对象)
OrderComponent* component;
protected:
// 保护方法:获取被装饰对象(供子类使用)
OrderComponent* GetComponent() const {
return component;
}
public:
// 构造函数:传入被装饰的订单组件
DiscountDecorator(OrderComponent* comp) : component(comp) {
if (comp == nullptr) {
throw invalid_argument("被装饰的订单组件不能为空!");
}
}
// 析构函数:不负责销毁被装饰对象(由客户端管理)
~DiscountDecorator() {}
// 转发计算价格的调用(子类可扩展)
float CalculateFinalPrice() const override {
return component->CalculateFinalPrice();
}
// 转发订单描述的调用(子类可扩展)
string GetOrderDescription() const override {
return component->GetOrderDescription();
}
};
// 具体装饰者 1:优惠券抵扣(固定金额抵扣)
class CouponDecorator : public DiscountDecorator {
private:
float couponAmount; // 优惠券金额
public:
CouponDecorator(OrderComponent* comp, float amount) : DiscountDecorator(comp), couponAmount(amount) {
if (amount <= 0) {
throw invalid_argument("优惠券金额必须大于 0!");
}
}
// 计算最终价格:基础价格 - 优惠券金额(最低为 0)
float CalculateFinalPrice() const override {
float basePrice = GetComponent()->CalculateFinalPrice();
float finalPrice = basePrice - couponAmount;
return finalPrice < 0 ? 0 : finalPrice;
}
// 扩展订单描述:添加优惠券信息
string GetOrderDescription() const override {
return GetComponent()->GetOrderDescription() + " + 优惠券抵扣" + to_string(couponAmount) + "元";
}
};
// 具体装饰者 2:积分抵扣(100 积分抵扣 1 元)
class PointsDecorator : public DiscountDecorator {
private:
int points;
:
(OrderComponent* comp, pts) : (comp), (pts) {
(pts <= ) {
();
}
}
{
basePrice = ()->();
discountAmount = points / ;
finalPrice = basePrice - discountAmount;
finalPrice < ? : finalPrice;
}
{
()->() + + (points) + + (points / ) + ;
}
};
: DiscountDecorator {
:
fullAmount;
reduceAmount;
:
(OrderComponent* comp, full, reduce) : (comp), (full), (reduce) {
(full <= || reduce <= ) {
();
}
(reduce >= full) {
();
}
}
{
basePrice = ()->();
finalPrice = basePrice;
(basePrice >= fullAmount) {
finalPrice = basePrice - reduceAmount;
}
finalPrice < ? : finalPrice;
}
{
()->() + + (fullAmount) + + (reduceAmount) + ;
}
};
int main() {
try {
// 1. 创建基础订单(原价 1500 元)
OrderComponent* basicOrder = new BasicOrder("ORD20240502001", 1500.0f);
cout << "=== 基础订单 ===" << endl;
cout << basicOrder->GetOrderDescription() << endl;
cout << "最终价格:" << basicOrder->CalculateFinalPrice() << "元" << endl;
// 2. 仅使用优惠券(抵扣 200 元)
OrderComponent* couponOrder = new CouponDecorator(basicOrder, 200.0f);
cout << "\n=== 基础订单 + 优惠券 ===" << endl;
cout << couponOrder->GetOrderDescription() << endl;
cout << "最终价格:" << couponOrder->CalculateFinalPrice() << "元" << endl;
// 3. 优惠券 + 积分抵扣(500 积分=5 元)
OrderComponent* couponPointsOrder = new PointsDecorator(couponOrder, 500);
cout << "\n=== 基础订单 + 优惠券 + 积分 ===" << endl;
cout << couponPointsOrder->GetOrderDescription() << endl;
cout << "最终价格:" << couponPointsOrder->CalculateFinalPrice() << "元" << endl;
// 4. 优惠券 + 积分 + 满 1000 减 150
OrderComponent* fullReduceOrder = new FullReduceDecorator(couponPointsOrder, 1000.0f, 150.0f);
cout << "\n=== 基础订单 + 优惠券 + 积分 + 满减 ===" << endl;
cout << fullReduceOrder->GetOrderDescription() << endl;
cout << "最终价格:" << fullReduceOrder->() << << endl;
OrderComponent* onlyFullReduceOrder = (basicOrder, , );
cout << << endl;
cout << onlyFullReduceOrder->() << endl;
cout << << onlyFullReduceOrder->() << << endl;
fullReduceOrder;
couponPointsOrder;
couponOrder;
onlyFullReduceOrder;
basicOrder;
} ( exception& e) {
cout << << e.() << endl;
}
;
}
=== 基础订单 === 订单 [ORD20240502001] 基础价格:1500.000000 元 最终价格:1500 元
=== 基础订单 + 优惠券 === 订单 [ORD20240502001] 基础价格:1500.000000 元 + 优惠券抵扣 200.000000 元 最终价格:1300 元
=== 基础订单 + 优惠券 + 积分 === 订单 [ORD20240502001] 基础价格:1500.000000 元 + 优惠券抵扣 200.000000 元 + 500 积分抵扣 5.000000 元 最终价格:1295 元
=== 基础订单 + 优惠券 + 积分 + 满减 === 订单 [ORD20240502001] 基础价格:1500.000000 元 + 优惠券抵扣 200.000000 元 + 500 积分抵扣 5.000000 元 + 满 1000 减 150.000000 元 最终价格:1145 元
=== 基础订单 + 满减 === 订单 [ORD20240502001] 基础价格:1500.000000 元 + 满 1000 减 150.000000 元 最终价格:1350 元
基础实现中,手动管理装饰者层级的内存容易出错(如遗漏释放、重复释放),且装饰者功能相对简单。以下优化方案使用智能指针自动管理内存,并新增'限时折扣'装饰者,支持更复杂的多层嵌套。
#include <memory>
int main() {
try {
// 使用 shared_ptr 自动管理内存,无需手动释放
shared_ptr<OrderComponent> basicOrder = make_shared<BasicOrder>("ORD20240502002", 2000.0f);
// 组合优惠:优惠券(300 元)+ 限时折扣(9 折)+ 满 1500 减 200
shared_ptr<OrderComponent> couponOrder = make_shared<CouponDecorator>(basicOrder.get(), 300.0f);
shared_ptr<OrderComponent> discountOrder = make_shared<DiscountDecorator>(couponOrder.get(), [](float basePrice) { return basePrice * 0.9f; });
shared_ptr<OrderComponent> fullReduceOrder = make_shared<FullReduceDecorator>(discountOrder.get(), 1500.0f, 200.0f);
cout << "=== 智能指针管理:多层优惠组合 ===" << endl;
cout << fullReduceOrder->GetOrderDescription() << endl;
cout << "最终价格:" << fullReduceOrder->CalculateFinalPrice() << "元" << endl;
// 智能指针自动析构,无需手动 delete
} catch (const exception& e) {
cout << "错误:" << e.what() << endl;
}
return 0;
}
// 具体装饰者 4:限时折扣(支持动态设置折扣率)
class TimeLimitDiscountDecorator : public DiscountDecorator {
private:
float discountRate; // 折扣率(如 0.9 表示 9 折)
public:
TimeLimitDiscountDecorator(OrderComponent* comp, float rate) : DiscountDecorator(comp), discountRate(rate) {
if (rate <= 0 || rate >= 1) {
throw invalid_argument("折扣率必须在 (0,1) 之间!");
}
}
// 计算最终价格:基础价格 * 折扣率
float CalculateFinalPrice() const override {
float basePrice = GetComponent()->CalculateFinalPrice();
return basePrice * discountRate;
}
string GetOrderDescription() const override {
return GetComponent()->GetOrderDescription() + " + 限时" + to_string(discountRate * 10) + "折";
}
// 动态修改折扣率(扩展功能)
void SetDiscountRate(float rate) {
if (rate <= 0 || rate >= 1) {
();
}
discountRate = rate;
}
};
{
shared_ptr<OrderComponent> basicOrder = <BasicOrder>(, );
shared_ptr<TimeLimitDiscountDecorator> timeLimitOrder = <TimeLimitDiscountDecorator>(basicOrder.(), );
cout << << endl;
cout << timeLimitOrder->() << endl;
cout << << timeLimitOrder->() << << endl;
timeLimitOrder->();
cout << << endl;
cout << timeLimitOrder->() << endl;
cout << << timeLimitOrder->() << << endl;
;
}
=== 限时折扣测试 === 订单 [ORD20240502003] 基础价格:1000.000000 元 + 限时 9.000000 折 9 折后价格:900 元
修改为 8 折后:订单 [ORD20240502003] 基础价格:1000.000000 元 + 限时 8.000000 折 8 折后价格:800 元
shared_ptr 自动管理装饰者和被装饰对象的生命周期,避免手动释放错误;适配器模式(Adapter Pattern)的核心思想是'将一个类的接口转换成客户希望的另一个接口,使原本由于接口不兼容而不能一起工作的那些类可以一起工作'。
实战建议: C++ 中优先使用对象适配器,避免多重继承带来的菱形继承、二义性等问题,且组合方式更灵活,支持适配多个适配者。
以'第三方支付接口适配'为例:项目现有支付接口(目标接口)要求 Pay(float amount) 方法,而引入的第三方支付库(适配者)的接口为 DoPayment(float money, string orderId),使用对象适配器将第三方接口转换为项目所需接口。
#include <iostream>
#include <string>
using namespace std;
// 目标接口:项目现有支付接口
class PaymentTarget {
public:
virtual bool Pay(float amount) = 0; // 客户期望的接口:支付指定金额
virtual string GetPaymentName() const = 0; // 获取支付方式名称
virtual ~PaymentTarget() {}
};
// 适配者:第三方支付库(接口与目标接口不兼容)
class ThirdPartyPayment {
public:
// 第三方接口:支付金额 + 订单 ID,返回是否成功
bool DoPayment(float money, const string& orderId) {
cout << "第三方支付库执行支付:订单 ID=" << orderId << ",金额=" << money << "元" << endl;
// 模拟支付成功
return true;
}
// 第三方额外功能:查询支付状态
string QueryPaymentStatus(const string& orderId) {
return "订单" + orderId + "支付成功";
}
};
// 对象适配器:将第三方支付接口转换为目标接口
class PaymentAdapter : public PaymentTarget {
private:
ThirdPartyPayment* adaptee; // 持有适配者的引用(组合方式)
string orderId; // 订单 ID(适配时需要)
public:
// 构造函数:传入适配者和订单 ID
PaymentAdapter(ThirdPartyPayment* adapteeObj, const string& id) : adaptee(adapteeObj), orderId(id) {
if (adapteeObj == nullptr) {
throw invalid_argument("适配者对象不能为空!");
}
}
~PaymentAdapter() {
// 适配器不负责销毁适配者(由客户端管理)
}
// 实现目标接口:将 Pay 调用转换为 DoPayment 调用
bool Pay(float amount) override {
if (amount <= 0) {
cout << "支付金额必须大于 0!" << endl;
return false;
}
// 委托适配者执行支付
return adaptee->DoPayment(amount, orderId);
}
string GetPaymentName() const override {
return "第三方支付(适配器转换)";
}
// 扩展功能:通过适配器调用适配者的额外方法
string QueryStatus() {
return adaptee->QueryPaymentStatus(orderId);
}
};
// 客户端代码:只依赖目标接口,不关心具体实现
void ProcessPayment(PaymentTarget* payment, float amount) {
cout << "=== 执行支付 ===" << endl;
cout << "支付方式:" << payment->GetPaymentName() << endl;
bool success = payment->Pay(amount);
if (success) {
cout << "支付成功!" << endl;
} else {
cout << "支付失败!" << endl;
}
}
int main() {
try {
// 1. 创建适配者(第三方支付库)
ThirdPartyPayment* thirdPartyPay = new ThirdPartyPayment();
// 2. 创建适配器(传入适配者和订单 ID)
PaymentAdapter* adapter = new PaymentAdapter(thirdPartyPay, "ORD20240503001");
// 3. 客户端通过目标接口调用(无需知道第三方接口细节)
ProcessPayment(adapter, 899.0f);
// 4. 调用适配器扩展的查询功能
cout << "\n查询支付状态:" << adapter->QueryStatus() << endl;
// 5. 释放资源
delete adapter;
delete thirdPartyPay;
} catch (const exception& e) {
cout << "错误:" << e.what() << endl;
}
return 0;
}
=== 执行支付 === 支付方式:第三方支付(适配器转换) 第三方支付库执行支付:订单 ID=ORD20240503001,金额=899 元 支付成功! 查询支付状态:订单 ORD20240503001 支付成功
同样以'第三方支付接口适配'为例,使用类适配器实现(继承适配者和目标接口):
// 类适配器:继承适配者和目标接口(多重继承)
class PaymentClassAdapter : public PaymentTarget, public ThirdPartyPayment {
private:
string orderId;
public:
PaymentClassAdapter(const string& id) : orderId(id) {}
// 实现目标接口:调用父类(适配者)的 DoPayment 方法
bool Pay(float amount) override {
if (amount <= 0) {
cout << "支付金额必须大于 0!" << endl;
return false;
}
// 直接调用继承自 ThirdPartyPayment 的 DoPayment 方法
return DoPayment(amount, orderId);
}
string GetPaymentName() const override {
return "第三方支付(类适配器)";
}
// 扩展功能:调用继承的 QueryPaymentStatus 方法
string QueryStatus() {
return QueryPaymentStatus(orderId);
}
};
int main() {
// 类适配器无需创建适配者对象,直接使用适配器
PaymentClassAdapter* classAdapter = new PaymentClassAdapter("ORD20240503002");
ProcessPayment(classAdapter, 1299.0f);
cout << "\n查询支付状态:" << classAdapter->QueryStatus() << endl;
delete classAdapter;
return 0;
}
=== 执行支付 === 支付方式:第三方支付(类适配器) 第三方支付库执行支付:订单 ID=ORD20240503002,金额=1299 元 支付成功! 查询支付状态:订单 ORD20240503002 支付成功
| 对比维度 | 类适配器 | 对象适配器 |
|---|---|---|
| 实现方式 | 多重继承(目标接口 + 适配者) | 组合(实现目标接口 + 持有适配者引用) |
| 灵活性 | 差:只能适配特定适配者,无法适配其子类 | 好:可适配适配者及其子类,支持多个适配者 |
| 耦合度 | 高:与适配者类强耦合 | 低:与适配者接口弱耦合 |
| 扩展能力 | 差:新增适配者需修改或新增适配器 | 好:可通过构造函数传入不同适配者 |
| 访问适配者方法 | 直接访问(继承) | 间接访问(委托) |
| 适用场景 | 适配者类稳定,无子类扩展 | 适配者可能有子类,需灵活适配 |
实战结论: 对象适配器在灵活性、低耦合度上优势明显,是 C++ 开发中的首选方案;类适配器仅适用于适配者类稳定、无扩展需求的简单场景。
开发一个统一支付网关系统,核心需求如下:
Pay(float amount));PaymentTarget);#include <iostream>
#include <string>
#include <memory>
#include <ctime>
using namespace std;
// -------------------------- 部分 1:目标接口与基础支付类 --------------------------
// 目标接口:统一支付接口
class PaymentTarget {
public:
virtual bool Pay(float amount) = 0;
virtual string GetPaymentName() const = 0;
virtual ~PaymentTarget() {}
};
// 基础支付类 1:微信支付(已符合目标接口,无需适配)
class WeChatPayment : public PaymentTarget {
public:
bool Pay(float amount) override {
cout << "[微信支付] 发起支付请求,金额:" << amount << "元" << endl;
// 模拟支付流程
return true;
}
string GetPaymentName() const override {
return ;
}
};
: PaymentTarget {
:
{
cout << << amount << << endl;
;
}
{
;
}
};
{
:
{
cout << << orderNo << << money << << token << endl;
;
}
{
+ orderNo;
}
};
: PaymentTarget {
:
shared_ptr<ThirdPartyPayLib> adaptee;
string orderNo;
string token;
:
( string& no, string& tk) : (no), (tk) {
adaptee = <ThirdPartyPayLib>();
}
{
(amount <= ) ;
adaptee->(orderNo, amount, token);
}
{
;
}
{
adaptee->(orderNo);
}
};
: PaymentTarget {
:
shared_ptr<PaymentTarget> component;
:
{
component;
}
:
(shared_ptr<PaymentTarget> comp) : (comp) {
(!component) ();
}
{
component->(amount);
}
{
component->();
}
};
: PaymentDecorator {
:
(shared_ptr<PaymentTarget> comp) : (comp) {}
{
now = ();
cout << << (&now) << << ()->() << << amount << << endl;
success = ()->(amount);
cout << << (success ? : ) << endl;
success;
}
{
()->() + ;
}
};
: PaymentDecorator {
:
string appSecret;
:
(shared_ptr<PaymentTarget> comp, string& secret) : (comp), (secret) {}
{
cout << << appSecret << endl;
verifySuccess = ;
(!verifySuccess) {
cout << << endl;
;
}
cout << << endl;
()->(amount);
}
{
()->() + ;
}
};
: PaymentDecorator {
:
string callbackUrl;
:
(shared_ptr<PaymentTarget> comp, string& url) : (comp), (url) {}
{
success = ()->(amount);
(success) {
cout << << callbackUrl << << endl;
} {
cout << << callbackUrl << << endl;
}
success;
}
{
()->() + ;
}
};
{
:
{
cout << << endl;
cout << << payment->() << endl;
cout << << amount << << endl;
success = payment->(amount);
cout << << endl;
success;
}
};
{
{
shared_ptr<PaymentTarget> wechatPay = <WeChatPayment>();
wechatPay = <LogPaymentDecorator>(wechatPay);
wechatPay = <SignaturePaymentDecorator>(wechatPay, );
wechatPay = <CallbackPaymentDecorator>(wechatPay, );
PaymentGateway::(wechatPay, );
shared_ptr<PaymentTarget> thirdPartyPay = <ThirdPartyPayAdapter>(, );
thirdPartyPay = <LogPaymentDecorator>(thirdPartyPay);
thirdPartyPay = <CallbackPaymentDecorator>(thirdPartyPay, );
PaymentGateway::(thirdPartyPay, );
shared_ptr<PaymentTarget> alipay = <AlipayPayment>();
PaymentGateway::(alipay, );
: PaymentTarget {
:
{
cout << << amount << << endl;
;
}
{
;
}
};
shared_ptr<PaymentTarget> unionPay = <UnionPayPayment>();
unionPay = <LogPaymentDecorator>(unionPay);
PaymentGateway::(unionPay, );
} ( exception& e) {
cout << << e.() << endl;
}
;
}
PaymentTarget 接口,新增扩展功能只需新增装饰者类,无需修改现有核心代码。==================== 支付网关 ==================== 支付方式:微信支付(带日志)(带签名验证)(带回调) 支付金额:699 元 [支付日志] Wed May 4 10:00:00 2024 开始支付,方式:微信支付(带签名验证)(带回调),金额:699 元 [签名验证] 正在验证签名,appSecret:wechat_secret_123 [签名验证] 签名成功,继续支付... [微信支付] 发起支付请求,金额:699 元 [回调通知] 向 https://api.example.com/wechat/callback 发送支付成功回调 [支付日志] 支付结果:成功 ================================================== ==================== 支付网关 ==================== 支付方式:第三方支付(适配器)(带日志)(带回调) 支付金额:1599 元 [支付日志] Wed May 4 10:00:00 2024 开始支付,方式:第三方支付(适配器)(带回调),金额:1599 元 [第三方支付库] 订单号:ORDER_20240504_001,金额:1599 元,token:token_abc123 [回调通知] 向 https://api.example.com/thirdparty/callback 发送支付成功回调 [支付日志] 支付结果:成功 ================================================== ==================== 支付网关 ==================== 支付方式:支付宝支付 支付金额:399 元 [支付宝支付] 发起支付请求,金额:399 元 ================================================== ==================== 支付网关 ==================== 支付方式:银联支付(带日志) 支付金额:899 元 [支付日志] Wed May 4 10:00:00 2024 开始支付,方式:银联支付,金额:899 元 [银联支付] 发起支付请求,金额:899 元 [支付日志] 支付结果:成功 ==================================================
本章重点讲解了 C++ 开发中常用的两种结构型设计模式:装饰者模式和适配器模式,核心要点总结如下:
通过本章学习,你应能熟练运用装饰者模式和适配器模式解决实际开发中的'功能扩展'和'接口兼容'问题,结合两种模式的组合使用,编写灵活、可扩展、低耦合的 C++ 代码。后续章节将继续讲解其他结构型设计模式(如代理模式、组合模式),进一步完善你的设计模式知识体系。

微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online
将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online
通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online