Files
structrail-design/.trae/documents/sdk-initialization-patterns.md
2026-02-04 13:33:23 +08:00

6.1 KiB
Raw Blame History

多语言 SDK 初始化设计规范

背景

为了简化 SDK 的使用流程并统一 API 设计体验,我们决定将 Tracer 的初始化数据Initial Data合并到创建Create阶段。这意味着用户在实例化 Tracer 时,可以直接传入初始数据,而无需单独调用 preset 方法。

这一设计模式Construct as Initialize在大多数现代编程语言中都有成熟的最佳实践。本文档旨在为不同语言的 SDK 实现提供具体的代码范式参考。

核心原则

  1. 优先使用构造函数参数:如果语言支持(如 Python, Kotlin, Swift优先使用带默认值的命名参数。
  2. 利用语言特性
    • 重载 (Overloading):适用于 Java, C++, C#。
    • 配置对象 (Options Object):适用于 TS, JS, Lua。
    • Builder / Functional Options:适用于 Go, Rust。
  3. 保持协议底层一致:无论上层 API 如何设计,底层生成的 create 指令 JSON 必须包含 array (或对应数据字段) 参数。

各语言实现参考

1. TypeScript / JavaScript (当前基准)

利用接口Interface定义配置对象简洁且扩展性强。

// 定义
interface ArrayTracerOptions<T> {
  description: string;
  array?: T[]; // 可选初始化数据
}

export const createArrayTracer = <T>(options: ArrayTracerOptions<T>) => { ... }

// 调用
const t1 = createArrayTracer({ description: "Empty" });
const t2 = createArrayTracer({ 
  description: "My Array", 
  array: [1, 2, 3] 
});

2. Python (Keyword Arguments)

利用 **kwargs 或显式关键字参数,非常符合 Pythonic 风格。

class ArrayTracer:
    def __init__(self, description: str, data: list = None):
        self.description = description
        if data:
            self._emit_create(data)

# 调用
t1 = ArrayTracer(description="Empty")
t2 = ArrayTracer(description="My Array", data=[1, 2, 3])

3. Java (Constructor Overloading)

利用构造函数重载提供多种初始化路径。

public class ArrayTracer<T> {
    // 构造函数 1: 仅描述
    public ArrayTracer(String description) {
        this(description, null);
    }

    // 构造函数 2: 描述 + 数据
    public ArrayTracer(String description, List<T> data) {
        // ... implementation
    }
}

// 调用
var t1 = new ArrayTracer<Integer>("Empty");
var t2 = new ArrayTracer<Integer>("My Array", Arrays.asList(1, 2, 3));

4. C++ (Overloading & Initializer List)

利用 std::initializer_list 支持花括号初始化,语法极其简洁。

template <typename T>
class ArrayTracer {
public:
    // 基础构造
    ArrayTracer(std::string description) { ... }
    
    // 带数据构造 (支持 vector)
    ArrayTracer(std::string description, const std::vector<T>& data) { ... }
    
    // 带数据构造 (支持 {1,2,3} 字面量)
    ArrayTracer(std::string description, std::initializer_list<T> data) { ... }
};

// 调用
ArrayTracer<int> t1("Empty");
ArrayTracer<int> t2("My Array", {1, 2, 3});

5. Go (Functional Options Pattern)

Go 社区处理复杂构造参数的标准模式。

type Option func(*ArrayTracer)

func WithData(data []interface{}) Option {
    return func(t *ArrayTracer) {
        t.initialData = data
    }
}

func NewArrayTracer(desc string, opts ...Option) *ArrayTracer {
    t := &ArrayTracer{Description: desc}
    for _, opt := range opts {
        opt(t)
    }
    return t
}

// 调用
t1 := NewArrayTracer("Empty")
t2 := NewArrayTracer("My Array", WithData([]interface{}{1, 2, 3}))

6. Rust (Builder Pattern)

利用 Builder 模式处理构造参数,保证类型安全和可读性。

struct ArrayTracerBuilder { ... }

impl ArrayTracer {
    pub fn builder(description: &str) -> ArrayTracerBuilder { ... }
}

impl ArrayTracerBuilder {
    pub fn with_data(mut self, data: Vec<i32>) -> Self { ... }
    pub fn build(self) -> ArrayTracer { ... }
}

// 调用
let t = ArrayTracer::builder("My Array")
    .with_data(vec![1, 2, 3])
    .build();

7. C# (Optional Arguments)

类似于 TypeScript 和 KotlinC# 支持命名参数和默认值。

public class ArrayTracer<T> {
    public ArrayTracer(string description, IEnumerable<T> data = null) {
        // ...
    }
}

// 调用
var t1 = new ArrayTracer<int>("Empty");
var t2 = new ArrayTracer<int>("My Array", data: new[] { 1, 2, 3 });

8. C 语言 (Special Case)

C 语言不支持重载,且缺乏自省能力,因此建议提供两种创建模式:

方案 A: 基础数据类型 (使用宏或特定后缀) 对于 int, float 等基础类型,提供专用函数。

// 基础创建
tracer_t* tracer_create_array(const char* desc);

// 带数据创建 (Explicit is better than implicit)
tracer_t* tracer_create_array_with_data(const char* desc, void* data, size_t len);

// 调用
tracer_t* t1 = tracer_create_array("Empty");
int nums[] = {1, 2, 3};
tracer_t* t2 = tracer_create_array_with_data("My Array", nums, 3);

方案 B: 自定义结构体 (Callback 模式) 对于用户自定义的 struct,采用类似 qsort 的回调函数模式,让用户提供序列化逻辑。

// 定义序列化回调: 将 elem 转换为 JSON 字符串写入 buffer
typedef void (*serializer_func)(const void* elem, char* buffer);

/**
 * @param serializer 用户提供的序列化函数
 */
tracer_t* tracer_create_array_custom(
    const char* desc, 
    const void* data, 
    size_t len, 
    size_t elem_size, 
    serializer_func serializer
);

// 用户代码示例
typedef struct { int x; int y; } Point;

// 用户编写序列化逻辑
void point_serializer(const void* elem, char* buffer) {
    const Point* p = (const Point*)elem;
    sprintf(buffer, "{\"x\":%d,\"y\":%d}", p->x, p->y);
}

// 调用
Point pts[] = {{1,2}, {3,4}};
tracer_t* t = tracer_create_array_custom("Points", pts, 2, sizeof(Point), point_serializer);

总结

通过统一采用**“构造即初始化”**的设计模式我们能够在几乎所有主流编程语言中提供一致、简洁且符合语言习惯Idiomatic的 SDK 使用体验。这不仅降低了用户的学习成本,也使得代码更加紧凑和易读。