09 最佳实践 - ZeroHawkeye/wordZero GitHub Wiki

最佳实践

本章汇总了使用 WordZero 开发时的最佳实践,包括性能优化、错误处理、代码组织和常见问题的解决方案。

🚀 性能优化

1. 批量操作优化

// ✅ 推荐:批量添加内容
func createLargeDocument() {
    doc := document.New()
    
    // 预分配切片容量
    data := make([]string, 0, 1000)
    for i := 0; i < 1000; i++ {
        data = append(data, fmt.Sprintf("段落 %d 的内容", i))
    }
    
    // 批量添加段落
    for _, text := range data {
        para := doc.AddParagraph(text)
        para.SetStyle(style.StyleNormal)
    }
}

// ❌ 避免:频繁的小操作
func inefficientCreation() {
    doc := document.New()
    
    for i := 0; i < 1000; i++ {
        // 每次都重新格式化字符串
        text := fmt.Sprintf("段落 %d 的内容", i)
        para := doc.AddParagraph(text)
        para.SetStyle(style.StyleNormal)
        // 每次都单独保存(错误做法)
        // doc.Save(fmt.Sprintf("temp_%d.docx", i))
    }
}

2. 内存管理

// ✅ 推荐:合理管理文档生命周期
func processMultipleDocuments(files []string) error {
    for _, file := range files {
        func() {
            doc := document.New()
            defer func() {
                // 确保文档资源被释放
                doc = nil
            }()
            
            // 处理文档
            processDocument(doc, file)
            
            // 保存并立即释放
            if err := doc.Save(file); err != nil {
                log.Printf("保存文档失败: %v", err)
            }
        }()
    }
    return nil
}

3. 样式重用

// ✅ 推荐:创建样式常量
var (
    HeaderFormat = &document.TextFormat{
        FontName:  "Microsoft YaHei",
        FontSize:  14,
        Bold:      true,
        FontColor: "2F5496",
    }
    
    BodyFormat = &document.TextFormat{
        FontName: "Microsoft YaHei",
        FontSize: 11,
    }
    
    EmphasisFormat = &document.TextFormat{
        Bold:      true,
        FontColor: "FF0000",
    }
)

func addFormattedContent(doc *document.Document) {
    // 重复使用预定义格式
    title := doc.AddParagraph("")
    title.AddFormattedText("标题", HeaderFormat)
    
    content := doc.AddParagraph("")
    content.AddFormattedText("正文", BodyFormat)
    content.AddFormattedText("重点", EmphasisFormat)
}

🔧 代码组织

1. 模块化设计

// 文档构建器模式
type DocumentBuilder struct {
    doc    *document.Document
    config *BuilderConfig
}

type BuilderConfig struct {
    Title       string
    Author      string
    PageSize    document.PageSize
    PageMargins [4]int // 上、右、下、左
}

func NewDocumentBuilder(config *BuilderConfig) *DocumentBuilder {
    return &DocumentBuilder{
        doc:    document.New(),
        config: config,
    }
}

func (b *DocumentBuilder) SetupPage() *DocumentBuilder {
    b.doc.SetPageSize(b.config.PageSize)
    b.doc.SetPageMargins(
        b.config.PageMargins[0],
        b.config.PageMargins[1], 
        b.config.PageMargins[2],
        b.config.PageMargins[3],
    )
    return b
}

func (b *DocumentBuilder) AddTitle() *DocumentBuilder {
    title := b.doc.AddParagraph(b.config.Title)
    title.SetStyle(style.StyleTitle)
    return b
}

func (b *DocumentBuilder) AddContent(content string) *DocumentBuilder {
    para := b.doc.AddParagraph(content)
    para.SetStyle(style.StyleNormal)
    return b
}

func (b *DocumentBuilder) Build() *document.Document {
    return b.doc
}

// 使用示例
func createStandardDocument() {
    config := &BuilderConfig{
        Title:    "标准文档",
        Author:   "WordZero",
        PageSize: document.PageSizeA4,
        PageMargins: [4]int{72, 72, 72, 72},
    }
    
    doc := NewDocumentBuilder(config).
        SetupPage().
        AddTitle().
        AddContent("文档内容...").
        Build()
        
    doc.Save("standard_document.docx")
}

2. 配置管理

// 配置文件结构
type DocumentConfig struct {
    Document DocumentSettings `json:"document"`
    Styles   StyleSettings    `json:"styles"`
    Page     PageSettings     `json:"page"`
}

type DocumentSettings struct {
    Title       string `json:"title"`
    Author      string `json:"author"`
    Subject     string `json:"subject"`
    Description string `json:"description"`
}

type StyleSettings struct {
    DefaultFont     string `json:"default_font"`
    DefaultFontSize int    `json:"default_font_size"`
    HeaderFont      string `json:"header_font"`
    HeaderFontSize  int    `json:"header_font_size"`
}

type PageSettings struct {
    Size        string `json:"size"`
    Orientation string `json:"orientation"`
    TopMargin   int    `json:"top_margin"`
    RightMargin int    `json:"right_margin"`
    BottomMargin int    `json:"bottom_margin"`
    LeftMargin  int    `json:"left_margin"`
}

// 从配置文件加载
func LoadConfig(filename string) (*DocumentConfig, error) {
    data, err := ioutil.ReadFile(filename)
    if err != nil {
        return nil, err
    }
    
    var config DocumentConfig
    err = json.Unmarshal(data, &config)
    return &config, err
}

// 应用配置
func ApplyConfig(doc *document.Document, config *DocumentConfig) error {
    // 设置文档属性
    doc.SetDocumentProperty("Title", config.Document.Title)
    doc.SetDocumentProperty("Author", config.Document.Author)
    doc.SetDocumentProperty("Subject", config.Document.Subject)
    
    // 设置页面属性
    pageSize := document.PageSizeA4
    if config.Page.Size == "A3" {
        pageSize = document.PageSizeA3
    }
    doc.SetPageSize(pageSize)
    
    doc.SetPageMargins(
        config.Page.TopMargin,
        config.Page.RightMargin,
        config.Page.BottomMargin,
        config.Page.LeftMargin,
    )
    
    return nil
}

❌ 错误处理

1. 统一错误处理

// 自定义错误类型
type DocumentError struct {
    Operation string
    Err       error
}

func (e *DocumentError) Error() string {
    return fmt.Sprintf("文档操作 '%s' 失败: %v", e.Operation, e.Err)
}

// 错误包装函数
func wrapError(operation string, err error) error {
    if err == nil {
        return nil
    }
    return &DocumentError{
        Operation: operation,
        Err:       err,
    }
}

// 安全的文档操作
func SafeDocumentOperation(filename string, operation func(*document.Document) error) error {
    doc := document.New()
    if doc == nil {
        return wrapError("创建文档", errors.New("文档创建失败"))
    }
    
    // 执行操作
    if err := operation(doc); err != nil {
        return wrapError("执行操作", err)
    }
    
    // 保存文档
    if err := doc.Save(filename); err != nil {
        return wrapError("保存文档", err)
    }
    
    return nil
}

// 使用示例
func CreateReport() error {
    return SafeDocumentOperation("report.docx", func(doc *document.Document) error {
        title := doc.AddParagraph("报告标题")
        title.SetStyle(style.StyleTitle)
        
        content := doc.AddParagraph("报告内容...")
        content.SetStyle(style.StyleNormal)
        
        return nil
    })
}

2. 验证和检查

// 输入验证
func ValidateInput(text string, maxLength int) error {
    if text == "" {
        return errors.New("文本不能为空")
    }
    if len(text) > maxLength {
        return fmt.Errorf("文本长度超过限制 %d", maxLength)
    }
    return nil
}

// 文档状态检查
func CheckDocumentState(doc *document.Document) error {
    if doc == nil {
        return errors.New("文档未初始化")
    }
    
    // 检查是否有内容
    // 这里可以添加更多检查逻辑
    
    return nil
}

// 安全的文本添加
func SafeAddParagraph(doc *document.Document, text string) error {
    if err := CheckDocumentState(doc); err != nil {
        return err
    }
    
    if err := ValidateInput(text, 10000); err != nil {
        return err
    }
    
    para := doc.AddParagraph(text)
    if para == nil {
        return errors.New("段落创建失败")
    }
    
    return nil
}

📊 表格最佳实践

1. 大型表格处理

// 分批处理大型表格数据
func CreateLargeTable(doc *document.Document, data [][]string) error {
    if len(data) == 0 {
        return errors.New("数据为空")
    }
    
    // 限制单个表格的大小
    const maxRowsPerTable = 100
    
    for i := 0; i < len(data); i += maxRowsPerTable {
        end := i + maxRowsPerTable
        if end > len(data) {
            end = len(data)
        }
        
        batch := data[i:end]
        
        config := &document.TableConfig{
            Rows:  len(batch),
            Cols:  len(batch[0]),
            Width: 8000,
            Data:  batch,
        }
        
        table := doc.AddTable(config)
        if table == nil {
            return fmt.Errorf("创建表格失败,批次: %d", i/maxRowsPerTable+1)
        }
        
        // 添加分页(如果需要)
        // 注意:AddPageBreak() 方法在最新版本中已移除
        // 目前需要通过段落属性设置分页
        // doc.AddPageBreak()
    }
    
    return nil
}

2. 表格样式统一

// 表格样式配置
type TableStyleConfig struct {
    HeaderBold       bool
    HeaderBackground string
    HeaderFontColor  string
    BorderWidth      int
    AlternateRows    bool
}

func ApplyTableStyle(table *document.Table, config *TableStyleConfig) error {
    if table == nil {
        return errors.New("表格为空")
    }
    
    // 设置边框
    table.SetBorder(true)
    table.SetBorderWidth(config.BorderWidth)
    
    // 设置表头样式
    if config.HeaderBold {
        for j := 0; j < table.GetColumnCount(); j++ {
            cell, err := table.GetCell(0, j)
            if err == nil {
                cell.SetBold(true)
                if config.HeaderBackground != "" {
                    cell.SetBackgroundColor(config.HeaderBackground)
                }
                if config.HeaderFontColor != "" {
                    cell.SetFontColor(config.HeaderFontColor)
                }
                cell.SetAlignment(document.AlignCenter)
            }
        }
    }
    
    // 交替行颜色
    if config.AlternateRows {
        for i := 1; i < table.GetRowCount(); i += 2 {
            for j := 0; j < table.GetColumnCount(); j++ {
                cell, err := table.GetCell(i, j)
                if err == nil {
                    cell.SetBackgroundColor("F2F2F2") // 浅灰色
                }
            }
        }
    }
    
    return nil
}

🔍 常见问题解决

1. 文档保存问题

// 安全的文档保存
func SafeSave(doc *document.Document, filename string) error {
    // 检查文件路径
    dir := filepath.Dir(filename)
    if err := os.MkdirAll(dir, 0755); err != nil {
        return fmt.Errorf("创建目录失败: %v", err)
    }
    
    // 检查文件权限
    if _, err := os.Stat(filename); err == nil {
        // 文件存在,检查是否可写
        file, err := os.OpenFile(filename, os.O_WRONLY, 0644)
        if err != nil {
            return fmt.Errorf("文件不可写: %v", err)
        }
        file.Close()
    }
    
    // 保存到临时文件
    tempFile := filename + ".tmp"
    if err := doc.Save(tempFile); err != nil {
        return fmt.Errorf("保存临时文件失败: %v", err)
    }
    
    // 原子性替换
    if err := os.Rename(tempFile, filename); err != nil {
        os.Remove(tempFile) // 清理临时文件
        return fmt.Errorf("替换文件失败: %v", err)
    }
    
    return nil
}

2. 内存泄漏预防

// 资源管理器
type DocumentManager struct {
    docs    map[string]*document.Document
    maxDocs int
    mutex   sync.RWMutex
}

func NewDocumentManager(maxDocs int) *DocumentManager {
    return &DocumentManager{
        docs:    make(map[string]*document.Document),
        maxDocs: maxDocs,
    }
}

func (dm *DocumentManager) CreateDocument(id string) (*document.Document, error) {
    dm.mutex.Lock()
    defer dm.mutex.Unlock()
    
    // 检查数量限制
    if len(dm.docs) >= dm.maxDocs {
        return nil, errors.New("文档数量超过限制")
    }
    
    // 检查是否已存在
    if _, exists := dm.docs[id]; exists {
        return nil, errors.New("文档ID已存在")
    }
    
    doc := document.New()
    dm.docs[id] = doc
    return doc, nil
}

func (dm *DocumentManager) CloseDocument(id string) error {
    dm.mutex.Lock()
    defer dm.mutex.Unlock()
    
    if doc, exists := dm.docs[id]; exists {
        // 这里可以添加文档清理逻辑
        _ = doc
        delete(dm.docs, id)
        return nil
    }
    
    return errors.New("文档不存在")
}

func (dm *DocumentManager) CloseAll() {
    dm.mutex.Lock()
    defer dm.mutex.Unlock()
    
    for id := range dm.docs {
        delete(dm.docs, id)
    }
}

3. 并发安全

// 线程安全的文档操作
type SafeDocument struct {
    doc   *document.Document
    mutex sync.RWMutex
}

func NewSafeDocument() *SafeDocument {
    return &SafeDocument{
        doc: document.New(),
    }
}

func (sd *SafeDocument) AddParagraph(text string) error {
    sd.mutex.Lock()
    defer sd.mutex.Unlock()
    
    para := sd.doc.AddParagraph(text)
    if para == nil {
        return errors.New("添加段落失败")
    }
    return nil
}

func (sd *SafeDocument) Save(filename string) error {
    sd.mutex.RLock()
    defer sd.mutex.RUnlock()
    
    return sd.doc.Save(filename)
}

// 并发处理示例
func ProcessConcurrently(tasks []string) error {
    const maxWorkers = 5
    semaphore := make(chan struct{}, maxWorkers)
    var wg sync.WaitGroup
    var errorsMutex sync.Mutex
    var errors []error
    
    for i, task := range tasks {
        wg.Add(1)
        go func(index int, content string) {
            defer wg.Done()
            
            // 获取信号量
            semaphore <- struct{}{}
            defer func() { <-semaphore }()
            
            // 处理任务
            doc := NewSafeDocument()
            if err := doc.AddParagraph(content); err != nil {
                errorsMutex.Lock()
                errors = append(errors, fmt.Errorf("任务 %d 失败: %v", index, err))
                errorsMutex.Unlock()
                return
            }
            
            filename := fmt.Sprintf("output_%d.docx", index)
            if err := doc.Save(filename); err != nil {
                errorsMutex.Lock()
                errors = append(errors, fmt.Errorf("保存任务 %d 失败: %v", index, err))
                errorsMutex.Unlock()
            }
        }(i, task)
    }
    
    wg.Wait()
    
    if len(errors) > 0 {
        return fmt.Errorf("处理过程中发生 %d 个错误", len(errors))
    }
    
    return nil
}

💡 开发技巧

1. 调试和日志

// 启用调试模式
func EnableDebugMode() {
    log.SetLevel(log.DebugLevel)
}

// 操作日志记录
func LogOperation(operation string, start time.Time, err error) {
    duration := time.Since(start)
    if err != nil {
        log.Errorf("操作 %s 失败 (耗时: %v): %v", operation, duration, err)
    } else {
        log.Infof("操作 %s 成功 (耗时: %v)", operation, duration)
    }
}

// 带日志的操作包装
func WithLogging(operation string, fn func() error) error {
    start := time.Now()
    err := fn()
    LogOperation(operation, start, err)
    return err
}

// 使用示例
func CreateDocumentWithLogging() error {
    return WithLogging("创建文档", func() error {
        doc := document.New()
        title := doc.AddParagraph("测试文档")
        title.SetStyle(style.StyleTitle)
        return doc.Save("test.docx")
    })
}

2. 单元测试

// 测试辅助函数
func createTestDocument() *document.Document {
    doc := document.New()
    title := doc.AddParagraph("测试文档")
    title.SetStyle(style.StyleTitle)
    return doc
}

func TestDocumentCreation(t *testing.T) {
    doc := createTestDocument()
    if doc == nil {
        t.Fatal("文档创建失败")
    }
    
    // 测试保存
    tempFile := filepath.Join(os.TempDir(), "test.docx")
    defer os.Remove(tempFile)
    
    err := doc.Save(tempFile)
    if err != nil {
        t.Fatalf("文档保存失败: %v", err)
    }
    
    // 验证文件存在
    if _, err := os.Stat(tempFile); os.IsNotExist(err) {
        t.Fatal("保存的文件不存在")
    }
}

func BenchmarkDocumentCreation(b *testing.B) {
    for i := 0; i < b.N; i++ {
        doc := document.New()
        para := doc.AddParagraph("基准测试段落")
        para.SetStyle(style.StyleNormal)
    }
}

📝 总结

遵循这些最佳实践能够帮助您:

  1. 提高性能: 通过批量操作和内存管理优化
  2. 增强可维护性: 通过模块化设计和配置管理
  3. 提高可靠性: 通过完善的错误处理和验证
  4. 确保安全性: 通过并发控制和资源管理
  5. 便于调试: 通过日志记录和测试

记住,好的代码不仅要功能正确,还要易于理解、维护和扩展。

下一步


持续改进和优化您的代码,让WordZero发挥最大的潜力!