refactor(array-tracer): 移除 preset 指令并简化类型参数

- 将 preset 功能合并到 create 指令中,简化 API 设计
- 移除 ArrayTracerPresetCommand 类型及相关处理逻辑
- 调整 createArrayTracer 泛型参数以直接接受数组类型
- 更新相关文档以反映新的初始化模式
This commit is contained in:
2026-02-04 13:31:54 +08:00
parent e874d1bb21
commit b67115b50c
5 changed files with 34 additions and 45 deletions

View File

@@ -4,6 +4,8 @@
Structrail 平台的核心目标是提供一套**语言无关**的数据结构可视化协议。这意味着我们的 SDK 需要在不同特性的编程语言中(从高动态的 Python/JS 到静态底层的 C/C++)提供一致的功能体验。 Structrail 平台的核心目标是提供一套**语言无关**的数据结构可视化协议。这意味着我们的 SDK 需要在不同特性的编程语言中(从高动态的 Python/JS 到静态底层的 C/C++)提供一致的功能体验。
**注意,我们不再需要 `preset` 指令了,但是即便 `preset` 的参数被合并到 `create` 指令中,这篇文档对于如何在不同语言中实现 `preset` 功能的说明仍然是有价值的。**
其中,`preset` 指令(一次性同步数组/容器状态)在静态类型语言(特别是 C 语言)中的实现是最大的挑战。本文档旨在论证其可行性,并为未来各语言 SDK 的实现提供参考规范。 其中,`preset` 指令(一次性同步数组/容器状态)在静态类型语言(特别是 C 语言)中的实现是最大的挑战。本文档旨在论证其可行性,并为未来各语言 SDK 的实现提供参考规范。
## 核心挑战:数组的异构性 ## 核心挑战:数组的异构性
@@ -17,20 +19,17 @@ Structrail 平台的核心目标是提供一套**语言无关**的数据结构
为了抹平差异,我们建议 SDK 接口设计遵循\*\*“渐进式暴露”\*\*原则: 为了抹平差异,我们建议 SDK 接口设计遵循\*\*“渐进式暴露”\*\*原则:
1. **Level 1: 智能推断接口**(针对 TS, Python, Java 等) 1. **Level 1: 智能推断接口**(针对 TS, Python, Java 等)
- 用户直接传数组对象。
- SDK 内部自动获取长度、遍历序列化。
* 用户直接传数组对象。
* SDK 内部自动获取长度、遍历序列化。
2. **Level 2: 显式元数据接口**(针对 C++, Go, Rust 等) 2. **Level 2: 显式元数据接口**(针对 C++, Go, Rust 等)
- 用户传递数据指针 + 长度。
- 利用泛型/模板自动推导元素序列化逻辑。
* 用户传递数据指针 + 长度。
* 利用泛型/模板自动推导元素序列化逻辑。
3. **Level 3: 手动序列化接口**(针对 C 语言) 3. **Level 3: 手动序列化接口**(针对 C 语言)
- 用户传递数据指针 + 长度 + 元素大小 + 序列化策略。
* 用户传递数据指针 + 长度 + 元素大小 + 序列化策略。 ---
***
## 各语言实现参考 ## 各语言实现参考
@@ -156,7 +155,7 @@ tracer_preset_cb(t, nums, 3, sizeof(int), my_int_serializer);
"tracer": "uuid-...", "tracer": "uuid-...",
"action": "preset", "action": "preset",
"params": { "params": {
"data": [1, 2, 3, 4] // 或者是 [{"x":1,"y":2}, ...] "data": [1, 2, 3, 4] // 或者是 [{"x":1,"y":2}, ...]
} }
} }
``` ```
@@ -167,4 +166,3 @@ tracer_preset_cb(t, nums, 3, sizeof(int), my_int_serializer);
1. **`preset`** **指令是可移植的**:即使在最底层的 C 语言中,通过适当的 API 封装(宏或格式化串),也能实现与高级语言近似的开发体验。 1. **`preset`** **指令是可移植的**:即使在最底层的 C 语言中,通过适当的 API 封装(宏或格式化串),也能实现与高级语言近似的开发体验。
2. **不要为了 C 语言阉割协议**:不需要因为 C 语言处理数组麻烦,就放弃 `preset` 而强迫所有语言都用 `patch` 循环。SDK 应该把复杂性封装在内部,留给用户简洁的接口。 2. **不要为了 C 语言阉割协议**:不需要因为 C 语言处理数组麻烦,就放弃 `preset` 而强迫所有语言都用 `patch` 循环。SDK 应该把复杂性封装在内部,留给用户简洁的接口。

View File

@@ -1,11 +1,13 @@
# 多语言 SDK 初始化设计规范 # 多语言 SDK 初始化设计规范
## 背景 ## 背景
为了简化 SDK 的使用流程并统一 API 设计体验,我们决定将 Tracer 的初始化数据Initial Data合并到创建Create阶段。这意味着用户在实例化 Tracer 时,可以直接传入初始数据,而无需单独调用 `preset` 方法。 为了简化 SDK 的使用流程并统一 API 设计体验,我们决定将 Tracer 的初始化数据Initial Data合并到创建Create阶段。这意味着用户在实例化 Tracer 时,可以直接传入初始数据,而无需单独调用 `preset` 方法。
这一设计模式Construct as Initialize在大多数现代编程语言中都有成熟的最佳实践。本文档旨在为不同语言的 SDK 实现提供具体的代码范式参考。 这一设计模式Construct as Initialize在大多数现代编程语言中都有成熟的最佳实践。本文档旨在为不同语言的 SDK 实现提供具体的代码范式参考。
## 核心原则 ## 核心原则
1. **优先使用构造函数参数**:如果语言支持(如 Python, Kotlin, Swift优先使用带默认值的命名参数。 1. **优先使用构造函数参数**:如果语言支持(如 Python, Kotlin, Swift优先使用带默认值的命名参数。
2. **利用语言特性** 2. **利用语言特性**
- **重载 (Overloading)**:适用于 Java, C++, C#。 - **重载 (Overloading)**:适用于 Java, C++, C#。
@@ -18,6 +20,7 @@
## 各语言实现参考 ## 各语言实现参考
### 1. TypeScript / JavaScript (当前基准) ### 1. TypeScript / JavaScript (当前基准)
利用接口Interface定义配置对象简洁且扩展性强。 利用接口Interface定义配置对象简洁且扩展性强。
```typescript ```typescript
@@ -31,13 +34,14 @@ export const createArrayTracer = <T>(options: ArrayTracerOptions<T>) => { ... }
// 调用 // 调用
const t1 = createArrayTracer({ description: "Empty" }); const t1 = createArrayTracer({ description: "Empty" });
const t2 = createArrayTracer({ const t2 = createArrayTracer({
description: "My Array", description: "My Array",
array: [1, 2, 3] array: [1, 2, 3]
}); });
``` ```
### 2. Python (Keyword Arguments) ### 2. Python (Keyword Arguments)
利用 `**kwargs` 或显式关键字参数,非常符合 Pythonic 风格。 利用 `**kwargs` 或显式关键字参数,非常符合 Pythonic 风格。
```python ```python
@@ -53,6 +57,7 @@ t2 = ArrayTracer(description="My Array", data=[1, 2, 3])
``` ```
### 3. Java (Constructor Overloading) ### 3. Java (Constructor Overloading)
利用构造函数重载提供多种初始化路径。 利用构造函数重载提供多种初始化路径。
```java ```java
@@ -74,6 +79,7 @@ var t2 = new ArrayTracer<Integer>("My Array", Arrays.asList(1, 2, 3));
``` ```
### 4. C++ (Overloading & Initializer List) ### 4. C++ (Overloading & Initializer List)
利用 `std::initializer_list` 支持花括号初始化,语法极其简洁。 利用 `std::initializer_list` 支持花括号初始化,语法极其简洁。
```cpp ```cpp
@@ -82,10 +88,10 @@ class ArrayTracer {
public: public:
// 基础构造 // 基础构造
ArrayTracer(std::string description) { ... } ArrayTracer(std::string description) { ... }
// 带数据构造 (支持 vector) // 带数据构造 (支持 vector)
ArrayTracer(std::string description, const std::vector<T>& data) { ... } ArrayTracer(std::string description, const std::vector<T>& data) { ... }
// 带数据构造 (支持 {1,2,3} 字面量) // 带数据构造 (支持 {1,2,3} 字面量)
ArrayTracer(std::string description, std::initializer_list<T> data) { ... } ArrayTracer(std::string description, std::initializer_list<T> data) { ... }
}; };
@@ -96,6 +102,7 @@ ArrayTracer<int> t2("My Array", {1, 2, 3});
``` ```
### 5. Go (Functional Options Pattern) ### 5. Go (Functional Options Pattern)
Go 社区处理复杂构造参数的标准模式。 Go 社区处理复杂构造参数的标准模式。
```go ```go
@@ -121,6 +128,7 @@ t2 := NewArrayTracer("My Array", WithData([]interface{}{1, 2, 3}))
``` ```
### 6. Rust (Builder Pattern) ### 6. Rust (Builder Pattern)
利用 Builder 模式处理构造参数,保证类型安全和可读性。 利用 Builder 模式处理构造参数,保证类型安全和可读性。
```rust ```rust
@@ -142,6 +150,7 @@ let t = ArrayTracer::builder("My Array")
``` ```
### 7. C# (Optional Arguments) ### 7. C# (Optional Arguments)
类似于 TypeScript 和 KotlinC# 支持命名参数和默认值。 类似于 TypeScript 和 KotlinC# 支持命名参数和默认值。
```csharp ```csharp
@@ -157,6 +166,7 @@ var t2 = new ArrayTracer<int>("My Array", data: new[] { 1, 2, 3 });
``` ```
### 8. C 语言 (Special Case) ### 8. C 语言 (Special Case)
C 语言不支持重载,且缺乏自省能力,因此建议提供两种创建模式: C 语言不支持重载,且缺乏自省能力,因此建议提供两种创建模式:
**方案 A: 基础数据类型 (使用宏或特定后缀)** **方案 A: 基础数据类型 (使用宏或特定后缀)**
@@ -186,10 +196,10 @@ typedef void (*serializer_func)(const void* elem, char* buffer);
* @param serializer 用户提供的序列化函数 * @param serializer 用户提供的序列化函数
*/ */
tracer_t* tracer_create_array_custom( tracer_t* tracer_create_array_custom(
const char* desc, const char* desc,
const void* data, const void* data,
size_t len, size_t len,
size_t elem_size, size_t elem_size,
serializer_func serializer serializer_func serializer
); );
@@ -210,4 +220,5 @@ tracer_t* t = tracer_create_array_custom("Points", pts, 2, sizeof(Point), point_
--- ---
## 总结 ## 总结
通过统一采用**“构造即初始化”**的设计模式我们能够在几乎所有主流编程语言中提供一致、简洁且符合语言习惯Idiomatic的 SDK 使用体验。这不仅降低了用户的学习成本,也使得代码更加紧凑和易读。 通过统一采用**“构造即初始化”**的设计模式我们能够在几乎所有主流编程语言中提供一致、简洁且符合语言习惯Idiomatic的 SDK 使用体验。这不仅降低了用户的学习成本,也使得代码更加紧凑和易读。

View File

@@ -8,7 +8,7 @@ import {
const logTracer = createLogTracer({ description: 'LogTracer' }); const logTracer = createLogTracer({ description: 'LogTracer' });
const controlTracer = createControlTracer({ description: 'ControlTracer' }); const controlTracer = createControlTracer({ description: 'ControlTracer' });
const arrayTracer = createArrayTracer<number>({ const arrayTracer = createArrayTracer({
description: 'ArrayTracer', description: 'ArrayTracer',
array: [1, 2, 3], array: [1, 2, 3],
}); });

View File

@@ -1,12 +1,12 @@
import { getTracerContext } from '../context'; import { getTracerContext } from '../context';
import type { JsonValue } from '../types'; import type { JsonValue } from '../types';
interface ArrayTracerCreateOptions<T extends JsonValue> { interface ArrayTracerCreateOptions<T extends JsonValue[]> {
description?: string; description?: string;
array?: T[]; array?: T;
} }
export const createArrayTracer = <T extends JsonValue>( export const createArrayTracer = <T extends JsonValue[]>(
options: ArrayTracerCreateOptions<T>, options: ArrayTracerCreateOptions<T>,
) => { ) => {
const { description = 'ArrayTracer', array } = options; const { description = 'ArrayTracer', array } = options;
@@ -24,17 +24,6 @@ export const createArrayTracer = <T extends JsonValue>(
}, },
}); });
const preset = (array: T[]) => {
command({
type: 'ArrayTracer',
tracer: tracer,
action: 'preset',
params: {
array: array,
},
});
};
const scale = (size: number) => { const scale = (size: number) => {
command({ command({
type: 'ArrayTracer', type: 'ArrayTracer',
@@ -68,7 +57,7 @@ export const createArrayTracer = <T extends JsonValue>(
}); });
}; };
const patch = (index: number, value: T) => { const patch = (index: number, value: T[number]) => {
command({ command({
type: 'ArrayTracer', type: 'ArrayTracer',
tracer: tracer, tracer: tracer,
@@ -92,7 +81,6 @@ export const createArrayTracer = <T extends JsonValue>(
}; };
return { return {
// preset,
scale, scale,
pick, pick,
drop, drop,

View File

@@ -12,13 +12,6 @@ type ArrayTracerCreateCommand = BaseArrayTracerCommand & {
}; };
}; };
type ArrayTracerPresetCommand = BaseArrayTracerCommand & {
action: 'preset';
params: {
array: JsonValue[];
};
};
type ArrayTracerScaleCommand = BaseArrayTracerCommand & { type ArrayTracerScaleCommand = BaseArrayTracerCommand & {
action: 'scale'; action: 'scale';
params: { params: {
@@ -57,7 +50,6 @@ type ArrayTracerUnsetCommand = BaseArrayTracerCommand & {
export type ArrayTracerCommand = export type ArrayTracerCommand =
| ArrayTracerCreateCommand | ArrayTracerCreateCommand
| ArrayTracerPresetCommand
| ArrayTracerScaleCommand | ArrayTracerScaleCommand
| ArrayTracerPickCommand | ArrayTracerPickCommand
| ArrayTracerDropCommand | ArrayTracerDropCommand