# SDK 设计

本文探讨了SDK的设计原则和设计模式的应用。介绍了如何通过模块化、策略模式、工厂模式、适配器模式和模板方法模式等来构建一个可扩展、解耦和易于维护的SDK。展示了SDK的模块化分层设计,包括核心功能模块、平台相关实现、API模块等,并提供了一个示例项目结构。讨论了如何通过静态代码分析工具(如ts-morph)来实现一个canIUse功能,以判断SDK中的API、回调、参数、组件等在当前版本中的可用性。介绍了如何使用TypeDoc工具从JSDoc注释中生成API文档,并提供了tsconfig.json的配置示例和如何在代码中使用JSDoc注释的示例。

# 设计原则

以下是设计原则的汇总表格,涵盖了常见的设计原则、目的、应用场景和示例:

设计原则 定义 目的 应用场景 示例
单一职责原则 (SRP) 一个类应该只有一个职责,且每个类应该仅有一个引起它变化的原因。 提高类的可维护性,减少类的复杂性。 类有多个职责时,修改一个功能可能会影响其他功能。 将报告生成和打印分离到不同的类:
class Report { generateReport() {} }
class ReportPrinter { printReport(report) {} }
开放封闭原则 (OCP) 软件实体(类、模块、函数等)应该对扩展开放,对修改封闭。 提高系统的灵活性和可扩展性。 需要在不修改原有代码的情况下,增加新功能时。 使用继承和多态来扩展功能,而不是修改原有类的代码:
class Shape { area() {} }
class Circle extends Shape { area() {} }
里氏替换原则 (LSP) 子类对象应该可以替换父类对象出现在任何地方,并且不影响程序的正确性。 确保继承关系合理,避免修改父类行为破坏子类行为。 子类和父类之间存在继承关系,确保子类可以正确替换父类使用。 子类 Penguin 不应覆盖父类 Bird 的行为,导致异常:
class Penguin extends Bird { fly() { throw "Cannot fly!" } }
接口隔离原则 (ISP) 不应强迫一个类依赖它不需要的接口。 使接口保持简洁,避免不必要的依赖。 类依赖过大的接口,导致不需要的功能被实现或依赖时。 将接口拆分成小接口:
class Workable { work() {} }
class Eatable { eat() {} }
依赖倒转原则 (DIP) 高层模块不应依赖低层模块,二者应依赖抽象;抽象不应依赖细节,细节应依赖抽象。 提高系统灵活性和可维护性。 高层模块和低层模块紧耦合时,难以扩展和维护。 使用接口或抽象类来替代具体实现类:
class Switch { constructor(device) { this.device = device; } operate() { this.device.turnOn(); } }
合成复用原则 (CRP) 尽量使用对象组合,而不是继承来复用代码。 提高代码的灵活性,避免继承关系的复杂性。 当对象之间是“拥有关系”而不是“是一个关系”时。 使用组合而非继承来复用代码:
class Engine { start() {} }
class Car { constructor() { this.engine = new Engine(); } start() { this.engine.start(); } }
最小知识原则 (LoD) 一个对象应该对其他对象有尽可能少的了解。 减少系统间的耦合,增加模块独立性。 当类之间有过多依赖关系时,避免暴露内部细节。 避免多次调用 getB().getC().doSomething(),而是通过间接方法调用:
class A { getB() { return new B(); } }
class B { doSomething() {} }
避免重复代码原则 (DRY) 同一功能的代码不应在系统中重复出现。 提高代码的可维护性和可复用性。 当相同的代码在多个地方出现时,避免冗余代码。 将公共方法提取到一个函数中来减少代码重复:
function calculateArea(radius) { return Math.PI * radius * radius; }
function calculateVolume(radius, height) { return calculateArea(radius) * height; }

# 设计模式

下面是一个设计模式汇总表格,包括每种设计模式的目的应用场景示例代码等关键信息,帮助更清晰地理解和应用这些模式。

设计模式 目的 应用场景 示例代码简要
单例模式 确保一个类只有一个实例,并提供一个全局访问点。 配置管理、日志管理、数据库连接池等。 通过静态变量确保类的唯一实例,且构造函数私有化,防止外部创建多个实例。
工厂方法模式 定义一个创建对象的接口,由子类决定实例化哪个类。 系统中有多个相似的对象,并希望将对象的创建与使用解耦。 定义一个抽象工厂类,通过子类重写工厂方法来创建具体的产品。
抽象工厂模式 提供一个接口,用于创建相关或依赖对象的家族,而无需明确指定具体类。 当系统需要独立于其产品的创建、组合和表示时。 提供多个工厂方法创建不同类型的产品系列,避免直接实例化具体类。
适配器模式 将一个类的接口转换成客户端希望的另一个接口,使得不兼容的接口可以一起工作。 当你希望将一个已有的类与一个不兼容的接口进行适配时。 通过一个适配器类将现有类的接口转换为目标接口,解耦原始类和使用类。
装饰器模式 动态地给一个对象添加一些额外的职责。 需要扩展对象功能,但又不想影响原始类的代码或多个类的功能。 通过装饰器类扩展原有对象的行为,可以动态添加新的功能。
观察者模式 定义对象间的一对多依赖关系,当一个对象改变状态时,所有依赖它的对象都会自动得到通知并更新。 事件系统、数据绑定、UI更新等。 定义一个主题类,当状态变化时,通知所有观察者对象执行更新操作。
策略模式 定义一系列算法,将每一个算法封装起来,并让它们可以互换。 当多个算法之间需要互换,且希望避免大量条件判断时。 定义一个策略接口,不同算法实现该接口,客户端根据需要选择策略执行。
状态模式 允许对象在内部状态改变时改变其行为,仿佛改变了其类。 当对象的行为依赖于其状态,并且必须在运行时根据状态变化时。 将不同状态封装为状态类,状态变化时通过上下文切换不同状态的行为。
代理模式 为其他对象提供一种代理以控制对这个对象的访问。 当需要控制对某个对象的访问,或需要在访问前后做一些处理时(如懒加载、缓存、权限检查等)。 创建一个代理类,委托实际的业务处理给真实对象,代理类可在实际处理之前或之后插入额外的逻辑。
命令模式 将请求封装为一个对象,从而使你可以用不同的请求对客户进行参数化。 解耦请求发送者和接收者,尤其是在涉及多个请求的情境中。 将请求封装为命令对象,命令对象可以被请求发送者调用,支持撤销等功能。
中介者模式 通过一个中介对象来封装一系列对象之间的交互,避免对象间的直接依赖。 复杂的对象交互,尤其是当多个对象之间有很多相互依赖时。 创建一个中介者类,通过它管理多个对象之间的通信,避免对象间的紧耦合。
责任链模式 允许多个处理者处理同一个请求,每个处理者决定是否将请求转发给下一个处理者。 当有多个对象可以处理一个请求时,需要动态选择一个合适的处理者。 创建一个处理者链,每个处理者处理自己关心的部分,然后将请求传递给下一个处理者。
桥接模式 将抽象与实现分离,使得它们可以独立变化。 需要在多个维度上扩展系统时,比如将不同的实现与抽象组合成不同的变体。 抽象类和实现类分开,并通过桥接类将它们连接,支持多维度扩展。
组合模式 使客户端可以统一处理单个对象和对象集合。 需要处理树形结构的对象,例如文件系统、UI组件树等。 将对象组织成树形结构,客户端可以以统一的方式处理单个对象和对象集合。
迭代器模式 提供一种方法顺序访问集合对象,而不暴露该对象的内部表示。 需要遍历集合对象,并且集合内部结构可能发生变化时。 创建一个迭代器类,实现对集合的遍历,客户端通过迭代器类访问集合中的元素。
模板方法模式 在一个方法中定义算法的骨架,将具体步骤的实现延迟到子类。 当算法的结构不变,而某些步骤的实现可能变化时。 创建一个模板方法,在父类中定义算法的步骤,子类实现具体的步骤。
访问者模式 允许在不修改元素类的前提下,定义作用于这些元素的新操作。 系统有很多不同的元素类,并且需要对这些元素做各种不同操作。 定义一个访问者类,提供不同的操作,而每个元素类只需要接受一个访问者并调用相应的方法。
享元模式 通过共享技术有效地支持大量细粒度对象的复用。 当需要创建大量相似对象,且这些对象可以共享时(例如图形、文本字符等)。 将共享部分提取出来,集中管理,避免为每个对象创建新实例,而是复用已有的实例。
外观模式 为复杂子系统提供一个简单接口,使得客户端可以方便地访问复杂的系统。 系统复杂且有多个子模块,需要提供一个统一的访问入口。 创建一个外观类,封装系统中多个模块的调用,使得客户端通过外观类与系统交互。
备忘录模式 在不暴露对象实现细节的情况下,保存对象的状态,以便以后恢复。 需要保存对象的状态并支持回滚或恢复操作。 通过备忘录对象保存状态,发起者可以请求保存状态,或恢复为某个历史状态。

# 单例模式(Singleton)

目的:确保一个类只有一个实例,并提供一个全局访问点。

应用场景:例如配置管理类、日志管理类、数据库连接池等。

// 单例类
class Singleton {
  // 静态属性来保存唯一的实例
  static instance = null;

  // 构造函数私有化,防止外部通过 new 创建实例
  constructor() {
    if (Singleton.instance) {
      return Singleton.instance;  // 如果实例已存在,则直接返回该实例
    }
    Singleton.instance = this;  // 否则保存当前实例
    this.data = []; // 初始化数据
  }

  addData(data) {
    this.data.push(data);
  }

  getData() {
    return this.data;
  }
}

// 测试单例模式
const instance1 = new Singleton();
instance1.addData("Item 1");

const instance2 = new Singleton();
instance2.addData("Item 2");

console.log(instance1.getData());  // ["Item 1", "Item 2"]
console.log(instance1 === instance2); // true,验证两个实例是相同的
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

# 工厂方法模式(Factory Method)

目的:定义一个创建对象的接口,由子类决定实例化哪个类。

应用场景:当系统中有多个相似的对象,并且希望将对象的创建与使用解耦时。

// 抽象产品类
class Product {
  operation() {
    throw new Error("This method must be overridden.");
  }
}

// 具体产品 A
class ConcreteProductA extends Product {
  operation() {
    return "Product A operation";
  }
}

// 具体产品 B
class ConcreteProductB extends Product {
  operation() {
    return "Product B operation";
  }
}

// 抽象工厂类
class Creator {
  createProduct() {
    return new Product(); // 工厂方法
  }
}

// 具体工厂 A
class ConcreteCreatorA extends Creator {
  createProduct() {
    return new ConcreteProductA();
  }
}

// 具体工厂 B
class ConcreteCreatorB extends Creator {
  createProduct() {
    return new ConcreteProductB();
  }
}

// 测试工厂方法模式
const creatorA = new ConcreteCreatorA();
console.log(creatorA.createProduct().operation()); // "Product A operation"

const creatorB = new ConcreteCreatorB();
console.log(creatorB.createProduct().operation()); // "Product B operation"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48

# 适配器模式(Adapter)

目的:将一个类的接口转换成客户端希望的另一个接口,使得不兼容的接口可以一起工作。

应用场景:当你希望将一个已有的类与一个不兼容的接口进行适配时。

// 目标接口
class Target {
  request() {
    throw new Error("This method must be overridden.");
  }
}

// 现有类,接口不兼容
class Adaptee {
  specificRequest() {
    return "Special request from Adaptee";
  }
}

// 适配器类
class Adapter extends Target {
  constructor(adaptee) {
    super();
    this.adaptee = adaptee;
  }

  request() {
    return this.adaptee.specificRequest();  // 将特定请求适配成通用请求
  }
}

// 测试适配器模式
const adaptee = new Adaptee();
const adapter = new Adapter(adaptee);
console.log(adapter.request());  // "Special request from Adaptee"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

# 装饰器模式(Decorator)

目的:动态地给一个对象添加一些额外的职责。

应用场景:当你需要扩展对象的功能时,可以使用装饰器模式,而无需修改原有类。

// 基本组件类
class Coffee {
  cost() {
    return 5;  // 基础咖啡价格
  }
}

// 装饰器基类
class CoffeeDecorator {
  constructor(coffee) {
    this.coffee = coffee;
  }

  cost() {
    return this.coffee.cost();
  }
}

// 具体装饰器类
class MilkDecorator extends CoffeeDecorator {
  cost() {
    return this.coffee.cost() + 2;  // 加奶装饰,价格增加
  }
}

class SugarDecorator extends CoffeeDecorator {
  cost() {
    return this.coffee.cost() + 1;  // 加糖装饰,价格增加
  }
}

// 测试装饰器模式
let coffee = new Coffee();
console.log(coffee.cost());  // 5

coffee = new MilkDecorator(coffee);  // 添加奶
console.log(coffee.cost());  // 7

coffee = new SugarDecorator(coffee);  // 再加糖
console.log(coffee.cost());  // 8
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40

# 观察者模式(Observer)

目的:定义对象间的一对多依赖关系,当一个对象改变状态时,所有依赖它的对象都会自动得到通知并更新。

应用场景:例如事件系统、数据绑定等。

// 主题类,管理所有观察者
class Subject {
  constructor() {
    this.observers = [];
  }

  addObserver(observer) {
    this.observers.push(observer);
  }

  removeObserver(observer) {
    const index = this.observers.indexOf(observer);
    if (index > -1) {
      this.observers.splice(index, 1);
    }
  }

  notifyObservers() {
    this.observers.forEach(observer => observer.update());
  }
}

// 观察者类
class Observer {
  constructor(name) {
    this.name = name;
  }

  update() {
    console.log(`${this.name} received an update!`);
  }
}

// 测试观察者模式
const subject = new Subject();

const observer1 = new Observer("Observer 1");
const observer2 = new Observer("Observer 2");

subject.addObserver(observer1);
subject.addObserver(observer2);

subject.notifyObservers();
// Output:
// Observer 1 received an update!
// Observer 2 received an update!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46

# 策略模式(Strategy)

目的:定义一系列算法,将每一个算法封装起来,并让它们可以互换。

应用场景:当你需要在不同算法之间进行选择时,使用策略模式可以避免使用大量的条件判断。

// 策略接口
class PaymentStrategy {
  pay(amount) {
    throw new Error("This method must be overridden.");
  }
}

// 具体策略:支付宝支付
class AlipayStrategy extends PaymentStrategy {
  pay(amount) {
    console.log(`Paid ${amount} using Alipay`);
  }
}

// 具体策略:微信支付
class WeChatStrategy extends PaymentStrategy {
  pay(amount) {
    console.log(`Paid ${amount} using WeChat`);
  }
}

// 上下文类,持有策略并委托处理支付
class PaymentContext {
  constructor(strategy) {
    this.strategy = strategy;
  }

  executePayment(amount) {
    this.strategy.pay(amount);  // 委托给具体策略
  }
}

// 测试策略模式
const alipay = new AlipayStrategy();
const weChat = new WeChatStrategy();

const context = new PaymentContext(alipay);
context.executePayment(100);  // "Paid 100 using Alipay"

context.strategy = weChat;  // 切换策略
context.executePayment(200);  // "Paid 200 using WeChat"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41

# 状态模式(State)

目的:允许对象在内部状态改变时改变其行为,仿佛改变了其类。

应用场景:当对象的行为依赖于其状态,并且必须在运行时根据状态变化时。

// 状态接口
class State {
  handle() {
    throw new Error("This method must be overridden.");
  }
}

// 具体状态 A
class ConcreteStateA extends State {
  handle() {
    console.log("Handling in State A");
  }
}

// 具体状态 B
class ConcreteStateB extends State {
  handle() {
    console.log("Handling in State B");
  }
}

// 上下文类,保存当前状态并根据状态切换行为
class Context {
  constructor(state) {
    this.state = state;
  }

  setState(state) {
    this.state = state;  // 切换状态
  }

  request() {
    this.state.handle();  // 根据当前状态执行行为
  }
}

// 测试状态模式
const context = new Context(new ConcreteStateA());
context.request();  // "Handling in State A"

context.setState(new ConcreteStateB());
context.request();  // "Handling in State B"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42

设计模式 (opens new window)

# 支付 SDK

这里以封装一个支付 SDK,支持多端,多渠道支付为例,理解开发一个 SDK 设计原则和思想:

# 架构设计原则

  • 模块化:将 SDK 分为多个模块(支付策略、平台适配、支付上下文、API 交互等),每个模块职责单一,遵循单一职责原则(SRP)。
  • 可扩展性:通过策略模式、工厂模式和适配器模式实现了高度的灵活性和可扩展性。可以方便地扩展新的支付渠道(如微信支付、支付宝、苹果支付等)和支持新的平台(H5、React Native、PC 等)。
  • 解耦性:支付渠道、平台适配和支付逻辑都被解耦成不同的模块,避免了对 SDK 核心功能的影响,增加了 SDK 的灵活性和可维护性。
  • 统一接口:通过统一的支付上下文(PaymentContext),不同的支付方式和平台都遵循同样的接口规范,保证 SDK 在调用过程中的一致性。

# 设计模式的应用

  • 策略模式(Strategy Pattern):支付渠道(微信支付、支付宝等)通过策略模式实现不同支付方式的封装。每个支付方式都是独立的策略,遵循同一接口,便于后续的扩展和替换。

    • PaymentStrategy 接口定义了支付的基本操作(如生成订单、发起支付、查询支付状态、退款等)。
    • 每个支付渠道实现对应的支付策略类,如 WeChatPayStrategyAlipayStrategy 等。
  • 工厂模式(Factory Pattern)PaymentFactory 用于创建具体的支付策略对象。根据支付类型,选择相应的支付渠道策略。

    • 通过工厂方法来动态选择支付渠道,避免了大量条件判断,使得扩展支付渠道时只需要添加相应的策略类和工厂方法。
  • 适配器模式(Adapter Pattern):通过适配器模式实现对不同平台(H5、React Native、PC 等)的适配。每个平台有对应的适配器类,将支付流程与平台的具体实现分离。

    • 适配器模式帮助统一了各个平台的调用接口,开发者无需关心不同平台的差异。
  • 模板方法模式(Template Method Pattern):通过支付上下文(PaymentContext)封装了支付流程的模板,支付流程中涉及的步骤由具体的支付策略类来实现。PaymentContext 作为模板类,定义了支付流程的基本步骤,具体的支付渠道通过策略模式来实现每个步骤的细节。

# 模块化分层设计

/payment-sdk
├── /src
│   ├── /core               # 核心功能模块,如支付上下文、支付策略、工厂等
│   │   ├── /strategies      # 支付策略(微信支付、支付宝支付、苹果支付等)
│   │   ├── /factories      # 支付工厂(生成支付策略)
│   │   ├── /adapters       # 支付适配器(处理平台适配,如H5、React Native等)
│   │   ├── /context        # 支付上下文(执行支付流程)
│   │   ├── /models         # 数据模型(订单、支付状态等)
│   │   └── /utils          # 工具类(签名、加密、时间等工具函数)
│   ├── /platforms          # 针对不同平台的实现(H5、React Native、PC等)
│   │   ├── /h5             # H5平台实现
│   │   ├── /react-native   # React Native平台实现
│   │   └── /pc             # PC平台实现
│   ├── /api                # 与支付服务端交互的 API 模块
│   │   ├── /wechat         # 微信支付相关 API
│   │   ├── /alipay         # 支付宝支付相关 API
│   │   └── /apple          # 苹果支付相关 API
│   ├── /config             # 配置文件(支付方式、平台信息等)
│   └── /index.ts           # SDK 入口文件
├── /test                   # 单元测试文件夹
│   ├── /core               # 核心功能测试
│   ├── /platforms          # 平台相关测试
│   ├── /api                # API 测试
│   └── /utils              # 工具类测试
├── /docs                   # SDK 文档
│   └── /usage.md           # SDK 使用文档
├── /examples               # SDK 示例项目
├── /build                  # 构建输出文件夹
├── /package.json           # npm 配置文件
└── /tsconfig.json          # TypeScript 配置文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

# 核心模块(/core)

核心模块包含了 SDK 的主要逻辑,包括支付策略、支付上下文、支付工厂和适配器等内容

  • 支付策略(/strategies):根据不同的支付渠道(如微信支付、支付宝支付、苹果支付等)实现各自的支付流程。每种支付方式都有一个策略类,负责支付过程的具体实现。支付策略类需要实现统一的接口,如 generateOrder、initiatePayment、checkPaymentStatus、refund 等
  • 支付工厂(/factories):支付工厂负责根据支付方式(如微信支付、支付宝支付等)来创建不同的支付策略对象。
  • 支付上下文(/context):支付上下文类协调支付策略的调用,负责顺序执行支付流程中的各个步骤。
  • 支付适配器(/adapters): 适配器模式用于处理不同平台(如 H5、React Native、PC)之间的差异性。每个平台的实现都可以调用相应的支付策略,并封装平台特定的调用逻辑。

# 支付策略(/strategies)

根据不同的支付渠道(如微信支付、支付宝支付、苹果支付等)实现各自的支付流程。每种支付方式都有一个策略类,负责支付过程的具体实现。支付策略类需要实现统一的接口,如 generateOrder、initiatePayment、checkPaymentStatus、refund 等

// /src/core/strategies/WeChatPayStrategy.ts
export class WeChatPayStrategy implements PaymentStrategy {
  async generateOrder(orderDetails: any): Promise<string> {
    // 微信支付订单生成逻辑
    return 'wechat_order_str_with_signature';
  }

  async initiatePayment(orderStr: string): Promise<string> {
    // 微信支付发起预下单
    return 'wechat_perpay_id';
  }

  async checkPaymentStatus(perpayId: string): Promise<PaymentStatus> {
    // 查询微信支付状态
    return { status: 'success', transactionId: 'wechat_txn_12345' };
  }

  async refund(orderDetails: any): Promise<boolean> {
    // 微信支付退款
    return true;
  }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

# 支付工厂(/factories)

支付工厂负责根据支付方式(如微信支付、支付宝支付等)来创建不同的支付策略对象。

// /src/core/factories/PaymentFactory.ts
export class PaymentFactory {
  static getStrategy(paymentType: 'wechat' | 'alipay' | 'apple'): PaymentStrategy {
    switch (paymentType) {
      case 'wechat':
        return new WeChatPayStrategy();
      case 'alipay':
        return new AlipayStrategy();
      case 'apple':
        return new ApplePayStrategy();
      default:
        throw new Error('Unsupported payment type');
    }
  }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# 支付上下文(/context)

支付上下文类协调支付策略的调用,负责顺序执行支付流程中的各个步骤。

// /src/core/context/PaymentContext.ts
import { PaymentStrategy } from '../strategies/PaymentStrategy';
import { PaymentStatus } from '../models/PaymentStatus';

export class PaymentContext {
  private strategy: PaymentStrategy;

  constructor(strategy: PaymentStrategy) {
    this.strategy = strategy;
  }

  setStrategy(strategy: PaymentStrategy) {
    this.strategy = strategy;
  }

  // 生成订单
  async generateOrder(orderDetails: any): Promise<string> {
    try {
      const orderStr = await this.strategy.generateOrder(orderDetails);
      return orderStr;
    } catch (error) {
      throw new Error('订单生成失败:' + error.message);
    }
  }

  // 发起支付
  async initiatePayment(orderStr: string): Promise<string> {
    try {
      const perpayId = await this.strategy.initiatePayment(orderStr);
      return perpayId;
    } catch (error) {
      throw new Error('支付发起失败:' + error.message);
    }
  }

  // 查询支付状态
  async checkPaymentStatus(perpayId: string): Promise<PaymentStatus> {
    try {
      const status = await this.strategy.checkPaymentStatus(perpayId);
      return status;
    } catch (error) {
      throw new Error('支付状态查询失败:' + error.message);
    }
  }

  // 发起退款
  async refundPayment(orderDetails: any): Promise<boolean> {
    try {
      const isRefundSuccessful = await this.strategy.refund(orderDetails);
      return isRefundSuccessful;
    } catch (error) {
      throw new Error('退款失败:' + error.message);
    }
  }

  // 一站式支付流程:生成订单 -> 发起支付 -> 查询状态
  async startPayment(orderDetails: any): Promise<PaymentStatus> {
    try {
      const orderStr = await this.generateOrder(orderDetails);
      const perpayId = await this.initiatePayment(orderStr);
      const paymentStatus = await this.checkPaymentStatus(perpayId);
      return paymentStatus;
    } catch (error) {
      throw new Error('支付流程发生错误:' + error.message);
    }
  }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68

# 支付适配器(/adapters)

适配器模式用于处理不同平台(如 H5、React Native、PC)之间的差异性。每个平台的实现都可以调用相应的支付策略,并封装平台特定的调用逻辑。

// /src/core/adapters/H5PaymentAdapter.ts
export class H5PaymentAdapter implements PaymentAdapter {
  async pay(amount: number): Promise<boolean> {
    // H5 支付特定实现
    console.log(`H5支付: ¥${amount}`);
    return true;
  }
}
1
2
3
4
5
6
7
8

# 模型(/models)

数据模型类,用于表示订单信息、支付状态等。通过这些模型类,SDK 可以与支付服务端进行交互。

// /src/core/models/PaymentStatus.ts
export interface PaymentStatus {
  status: 'success' | 'failed' | 'pending';
  transactionId: string;
}

1
2
3
4
5
6

# 工具类(/utils)

工具类包含一些常用的功能,如签名生成、加密、解密等。

// /src/core/utils/Signature.ts
export class Signature {
  static generateSignature(data: any, secretKey: string): string {
    // 签名生成逻辑
    return 'generated_signature';
  }
}

1
2
3
4
5
6
7
8

# 平台相关实现(/platforms)

包含了平台特定的代码,处理不同平台的支付交互方式

# H5 支付实现(/h5)

// /src/platforms/h5/PaymentService.ts
import { H5PaymentAdapter } from '../../core/adapters/H5PaymentAdapter';

export class H5PaymentService {
  private adapter: H5PaymentAdapter;

  constructor() {
    this.adapter = new H5PaymentAdapter();
  }

  async pay(amount: number): Promise<boolean> {
    return this.adapter.pay(amount);
  }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# React Native 支付实现(/react-native)

// /src/platforms/react-native/PaymentService.ts
import { RNPamentAdapter } from '../../core/adapters/RNPamentAdapter';

export class RNPaymentService {
  private adapter: RNPamentAdapter;

  constructor() {
    this.adapter = new RNPamentAdapter();
  }

  async pay(amount: number): Promise<boolean> {
    return this.adapter.pay(amount);
  }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# API 模块(/api)

封装了与支付服务端的交互,调用不同支付平台的支付接口(如微信、支付宝、苹果支付等)。

// /src/api/wechat/WechatAPI.ts
export class WechatAPI {
  async generateOrder(orderDetails: any): Promise<string> {
    // 调用微信支付 API 生成订单
    return 'wechat_order_str_with_signature';
  }

  async initiatePayment(orderStr: string): Promise<string> {
    // 调用微信支付 API 发起预下单
    return 'wechat_perpay_id';
  }

  async checkPaymentStatus(perpayId: string): Promise<PaymentStatus> {
    // 查询微信支付状态
    return { status: 'success', transactionId: 'wechat_txn_12345' };
  }

  async refund(orderDetails: any): Promise<boolean> {
    // 调用微信支付 API 发起退款
    return true;
  }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

# SDK 入口文件(index.ts)

// /src/index.ts
// SDK 入口文件,提供支付接口
import { PaymentFactory } from './core/factories/PaymentFactory';
import { PaymentContext } from './core/context/PaymentContext';
import { PaymentStrategy } from './core/strategies/PaymentStrategy';

// 初始化支付工厂和支付上下文
const paymentType = 'wechat'; // 假设选择了微信支付
const strategy: PaymentStrategy = PaymentFactory.getStrategy(paymentType);
const paymentContext = new PaymentContext(strategy);

// 使用 SDK 调用支付
async function startPayment() {
  const orderDetails = { amount: 100 }; // 订单信息
  const orderStr = await paymentContext.generateOrder(orderDetails);
  console.log('订单字符串:', orderStr);

  const perpayId = await paymentContext.initiatePayment(orderStr);
  console.log('预支付 ID:', perpayId);

  // 查询支付状态
  const status = await paymentContext.checkPaymentStatus(perpayId);
  console.log('支付状态:', status);
}

// 发起支付
startPayment();

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

# Rollup 打包

// rollup.config.js
import typescript from 'rollup-plugin-typescript2';
import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import { terser } from 'rollup-plugin-terser';
import path from 'path';

export default {
  input: 'src/index.ts', // SDK 入口文件
  output: [
    {
      file: 'dist/your-payment-sdk.esm.js', // ESM 格式,适用于现代前端框架
      format: 'esm', // ES Module 格式
      sourcemap: true, // 启用源映射,便于调试
    },
    {
      file: 'dist/your-payment-sdk.cjs.js', // CommonJS 格式,适用于 Node.js 环境
      format: 'cjs',
      sourcemap: true,
    },
    {
      file: 'dist/your-payment-sdk.umd.js', // UMD 格式,兼容浏览器和 Node.js
      format: 'umd',
      name: 'PaymentSDK', // UMD 格式的全局变量名称
      sourcemap: true,
    },
    {
      file: 'dist/your-payment-sdk.min.js', // 压缩后的文件
      format: 'umd',
      name: 'PaymentSDK', // UMD 格式的全局变量名称
      sourcemap: true,
      plugins: [terser()], // 压缩代码
    },
  ],
  external: ['axios', 'dayjs'], // 外部依赖,防止将其打包进 SDK,减少体积
  plugins: [
    resolve(), // 解析 Node.js 模块
    commonjs(), // 支持 CommonJS 模块
    typescript({
      tsconfig: path.resolve(__dirname, 'tsconfig.json'), // 使用 TypeScript 配置文件
      useTsconfigDeclarationDir: true, // 指定声明文件输出目录
    }),
  ],
  treeshake: {
    moduleSideEffects: false, // 禁用模块副作用,启用 tree-shaking
  },
  preserveModules: true, // 保留模块结构,便于按需加载
  // 如果你的 SDK 依赖了动态导入,可以设置输出目录
  output: {
    dir: 'dist',
    format: 'esm',
    chunkFileNames: 'chunks/[name]-[hash].js', // 按需加载的文件命名
  },
};

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55

# 使用

import { PaymentFactory } from 'your-payment-sdk';
import { PaymentContext } from 'your-payment-sdk';

// 配置支付类型,假设是微信支付
const paymentType = 'wechat'; // 或 'alipay'
const strategy = PaymentFactory.getStrategy(paymentType);
const paymentContext = new PaymentContext(strategy);

// 调用支付流程
async function makePayment() {
  const orderDetails = { amount: 100, orderId: '123456789' };

  try {
    const paymentStatus = await paymentContext.startPayment(orderDetails);
    if (paymentStatus.status === 'success') {
      console.log('支付成功!');
    } else {
      console.log('支付失败');
    }
  } catch (error) {
    console.error('支付失败,错误信息:', error.message);
  }
}

// 发起支付
makePayment();

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

# canIUse 兼容性设计

本质上是提供的一个 API 检测工具、兼容性雷达,它能帮助开发者快速判断当前 APP 版本或环境是否支持某个特定的接口或者组件。开发者可以避免在不支持该功能的基础库中使用它,或根据查询结果在代码中进行降级处理,提高应用的兼容性和稳定性。

以微信与微信小程序为例,微信基础库是小程序运行的必要环境,它封装了微信和手机的原生能力,并提供给小程序开发者使用。你可以理解为小程序的“操作系统”,它为小程序提供了各种组件、API 和工具,让开发者能够快速构建出丰富多彩的小程序应用。微信会定期发布新的基础库版本,引入新的功能和修复已知问题。不同的基础库版本支持的 API 和组件可能不同,开发者在开发小程序时需要注意兼容性问题。微信版本迭代频繁,新版本会不断推出新的 API 和组件。开发者使用 caniuse 可以确保自己的小程序在不同微信版本上都能正常运行,避免因不支持某个功能而导致小程序出现异常。

通过在微信客户端内部维护一个 API 支持矩阵来实现 caniuse 功能。这个矩阵记录了每个微信版本支持的 API、组件、以及它们对应的属性和方法。这个矩阵可以是一个多维数组或者对象,第一维度是微信版本号,第二维度是 API 或组件的名称,第三维度是具体的属性或方法,矩阵中存储了每个 API 或组件在不同微信版本中的支持情况,通常用布尔值表示,每次微信版本更新时,这个 API 支持矩阵也会随之更新,以反映最新的 API 支持情况。为了提高查询效率,可能会采用一些优化手段,比如使用哈希表来存储矩阵数据,或者对矩阵进行索引。

// 假设 API 支持矩阵是一个对象
const apiSupportMatrix = {
  '3.2.1': {
    'button.open-type.contact': true,
    'chooseLocation': true,
    // ... 其他 API
  },
  '3.1.0': {
    // ... 其他 API
  }
};

function canIUse(apiName) {
  // 获取当前微信版本号(假设为 3.2.1)
  const currentVersion = '3.2.1';

  // 在矩阵中查找
  return apiSupportMatrix[currentVersion]?.[apiName] ?? false;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

可以看到核心是 apiSupportMatrix 的生成和维护,版本更新越频繁,矩阵维护的频率就越高。API 数量越多,复杂度越高,生成和维护矩阵的难度就越大。

  • 手动维护:灵活性强,可以针对性地添加或修改 API 信息,工作量巨大,容易出错,维护成本高,适合数量少不频繁变动的 API。
  • 自动化生成:通过静态代码分析工具提取 API 的定义、参数和返回值等信息。

可利用 ts-morph 来实现支付 SDK 的 caniuse 功能,主要的目标是基于 TypeScript 代码的分析,判断 SDK 中的 API、回调、参数、组件 等是否在当前版本中可用。我们可以通过 ts-morph 对 SDK 的 TypeScript 代码进行静态分析,ts-morph 可以生成抽象语法树 (AST),方便我们遍历和分析代码结构,识别出不同功能的支持情况,并根据版本信息来判断该功能是否可用。

# 目标

  1. API:支付 SDK 中的接口方法(如:createOrder, processPayment)。
  2. 回调:SDK 中的回调函数或事件(如:onSuccess, onError)。
  3. 参数:SDK 方法或回调中使用的参数(如:amount, currency)。
  4. 组件:SDK 中的组件(如:支付按钮、订单面板等)。

# 基本设计思路

我们需要实现以下几个步骤:

  1. 定义支持版本的注释:通过在 SDK 中的 API、回调、参数、组件等上添加版本注释,指明该功能从哪个版本开始支持。
  2. 静态分析 SDK 代码:使用 ts-morph 来解析 TypeScript 代码,分析 SDK 中的类、方法、参数、回调等。
  3. 提取支持版本信息:从代码中的注释中提取版本信息,并生成 canIUseFeatures 数据结构。
  4. 判断功能是否可用:通过动态检测当前平台的 SDK 版本,并根据 canIUseFeatures 来判断功能是否可用。

# 实现示例

import { Project } from 'ts-morph';
import * as fs from 'fs';

// 假设 SDK 文件路径
const sdkFilePath = 'path/to/PaymentAPI.d.ts';
const outputFilePath = 'path/to/apiSupportMatrix.json';

/**
 * @description 用于表示每个功能的支持信息
 */
interface APIVersionInfo {
  introduced: string;  // 引入版本
  deprecated?: string; // 废弃版本
}

// 初始化项目
const project = new Project();
const sourceFile = project.addSourceFileAtPath(sdkFilePath);

// 解析文件,生成支持矩阵
const buildApiSupportMatrix = (sourceFile: any) => {
  const apiSupportMatrix: Record<string, Record<string, boolean>> = {};

  // 解析注释,获取版本信息
  const parseDocComments = (docComment: string | undefined): APIVersionInfo | null => {
    if (!docComment) return null;
    const introducedMatch = docComment.match(/@version (\d+\.\d+)/);
    const deprecatedMatch = docComment.match(/@deprecated (\d+\.\d+)/);
    return introducedMatch
      ? { introduced: introducedMatch[1], deprecated: deprecatedMatch?.[1] }
      : null;
  };

  // 记录支持的 API 路径和版本信息
  const recordApiSupport = (apiPath: string, versionInfo: APIVersionInfo) => {
    const { introduced, deprecated } = versionInfo;
    const versions = Object.keys(apiSupportMatrix);
    
    // 遍历版本,填充支持矩阵
    for (const version of versions) {
      if (version >= introduced && (!deprecated || version < deprecated)) {
        apiSupportMatrix[version] ??= {};
        apiSupportMatrix[version][apiPath] = true;
      }
    }
  };

  // 处理函数
  sourceFile.getFunctions().forEach(fn => {
    const functionName = fn.getName();
    const versionInfo = parseDocComments(fn.getJsDoc()[0]?.getComment());
    if (versionInfo) recordApiSupport(functionName, versionInfo);
  });

  // 处理类中的方法
  sourceFile.getClasses().forEach(cls => {
    const className = cls.getName();
    cls.getMethods().forEach(method => {
      const methodName = `${className}.${method.getName()}`;
      const versionInfo = parseDocComments(method.getJsDoc()[0]?.getComment());
      if (versionInfo) recordApiSupport(methodName, versionInfo);
    });
  });

  // 处理接口中的属性
  sourceFile.getInterfaces().forEach(intf => {
    const interfaceName = intf.getName();
    intf.getProperties().forEach(prop => {
      const propName = `${interfaceName}.${prop.getName()}`;
      const versionInfo = parseDocComments(prop.getJsDoc()[0]?.getComment());
      if (versionInfo) recordApiSupport(propName, versionInfo);
    });
  });

  return apiSupportMatrix;
};

// 初始化支持矩阵的版本
const initializeMatrixVersions = (versions: string[]): Record<string, Record<string, boolean>> => {
  return versions.reduce((acc, version) => {
    acc[version] = {};
    return acc;
  }, {} as Record<string, Record<string, boolean>>);
};

// 假设我们支持以下微信小程序版本范围
const supportedVersions = ['3.0.0', '3.1.0', '3.2.0', '3.2.1'];
const apiSupportMatrix = initializeMatrixVersions(supportedVersions);

// 生成 API 支持矩阵
const generatedMatrix = buildApiSupportMatrix(sourceFile);

// 将矩阵写入 JSON 文件
fs.writeFileSync(outputFilePath, JSON.stringify(generatedMatrix, null, 2), 'utf8');
console.log('API 支持矩阵已生成:', outputFilePath);


// 假设从文件中加载生成的支持矩阵
import * as fs from 'fs';
const apiSupportMatrix = JSON.parse(fs.readFileSync('path/to/apiSupportMatrix.json', 'utf8'));

/**
 * @description canIUse 函数,用于检查 API 是否在当前版本中可用。
 * @param {string} apiName - 需要检查的 API 名称,例如 'processPayment'
 * @param {string} currentVersion - 当前应用的版本
 * @returns {boolean} - 返回是否支持
 */
function canIUse(apiName: string, currentVersion: string): boolean {
  // 检查版本是否在支持矩阵中
  if (!apiSupportMatrix[currentVersion]) {
    console.warn(`版本 ${currentVersion} 不在支持范围内`);
    return false;
  }

  // 查找 API 的支持情况
  return apiSupportMatrix[currentVersion][apiName] ?? false;
}

// 测试用例
const apiToTest = 'processPayment';
const versionToTest = '3.2.1';
console.log(`API '${apiToTest}' 在版本 ${versionToTest} 中可用:`, canIUse(apiToTest, versionToTest));

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123

# JSDOC 注释

// PaymentAPI.d.ts

/**
 * @version 3.0
 * 处理支付请求的 API,使用 `method` 参数指定支付方式(如信用卡、PayPal)。
 * @param {number} amount - 支付金额
 * @param {string} currency - 支付货币(支持 USD、EUR 等)
 * @param {string} method - 支付方式(如信用卡、PayPal)
 * @returns {void} - 无返回值
 * @deprecated 从版本 3.1 开始,`method` 参数已被废弃,使用 `methods` 替代。
 * 
 * 示例:
 * ```ts
 * PaymentAPI.processPayment(100, 'USD', 'credit_card');
 * ```
 */
export function processPayment(amount: number, currency: string, method: string): void;

/**
 * @version 3.1
 * 处理支付请求的 API,使用 `methods` 参数指定支付方式。
 * 支持多个支付方式的并行选择,改为接受一个数组。
 * @param {number} amount - 支付金额
 * @param {string} currency - 支付货币(支持 USD、EUR 等)
 * @param {string[]} methods - 支付方式数组(如 `['credit_card', 'paypal']`)
 * @returns {void} - 无返回值
 * 
 * 示例:
 * ```ts
 * PaymentAPI.processPayment(100, 'USD', ['credit_card', 'paypal']);
 * ```
 */
export function processPayment(amount: number, currency: string, methods: string[]): void;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

# TypeDoc 文档生成

# tsconfig.json 配置

在 tsconfig.json 文件中配置 TypeDoc 相关的设置。这是推荐的方式,便于管理项目的编译和文档生成设置。

{
  "out": "./docs",                  // 输出文档的目录
  "exclude": "**/test/**",           // 排除测试目录
  "excludePrivate": true,            // 排除私有成员
  "excludeProtected": true,          // 排除受保护成员
  "includeDeclarations": false,      // 是否包括声明文件
  "theme": "default",                // 使用默认主题
  "readme": "none",                  // 不包括 README 文件
  "hideGenerator": true,             // 隐藏生成器信息
  "ignoreCompilerErrors": true,      // 忽略 TypeScript 编译错误
  "name": "Payment SDK API"          // 文档标题
}
1
2
3
4
5
6
7
8
9
10
11
12

# 在代码中使用 JSDoc 注释

/**
 * 处理支付的 API。
 * @version 3.0
 * @example
 * ```ts
 * PaymentAPI.processPayment(100, 'USD', 'credit_card');
 * ```
 */
export class PaymentAPI {
  
  /**
   * 处理支付请求。
   * @param amount 支付金额
   * @param currency 支付货币(如 USD、EUR)
   * @param method 支付方式(如信用卡、PayPal)
   * @returns 无返回值
   * @throws {Error} 如果支付失败则抛出错误
   * @version 3.0
   */
  public static processPayment(amount: number, currency: string, method: string): void {
    if (amount <= 0) {
      throw new Error("Invalid amount");
    }
    // 处理支付逻辑
  }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

# 通过命令行生成文档

npx typedoc --out docs --exclude **/test/** --excludePrivate --excludeProtected --name "Payment SDK API" src

1
2