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)
}
}
📝 总结
遵循这些最佳实践能够帮助您:
- 提高性能: 通过批量操作和内存管理优化
- 增强可维护性: 通过模块化设计和配置管理
- 提高可靠性: 通过完善的错误处理和验证
- 确保安全性: 通过并发控制和资源管理
- 便于调试: 通过日志记录和测试
记住,好的代码不仅要功能正确,还要易于理解、维护和扩展。
下一步
持续改进和优化您的代码,让WordZero发挥最大的潜力!