IO 子系统的需求与初始架构

接口是业务的抽象,同时也是它与使用方的耦合方式。在业务分解的过程中,我们需要认真审视模块的接口,发现其中 “过度的(或多余的)” 约束条件,把它提高到足够通用的、普适的场景来看。

IO DOM 模式


type IoSpan interface {
  Text() []byte
  Attributes() IoSpanAttrs
}

type IoSpans interface {
  Len() int
  Elem(i int) IoSpan
}

type IoParagraph interface {
  Spans() IoSpans
  Attributes() IoParagraphAttrs
}

type IoParagraphs interface {
  Len() int
  Elem(i int) IoParagraph
}

type IoDocument interface {
  Paragraphs() IoParagraphs
  Attributes() IoDocumentAttrs
}

func NewIoDocument() IoDocument 

type Document struct {
  ...
  Io() IoDocument
}

func NewDocument() *Document

func SaveWord(stg IStorage, doc IoDocument) error
func SaveRTF(f *os.File, doc IoDocument) error
func SaveFile(file string, format string, doc IoDocument) error

func LoadWord(stg IStorage, doc IoDocument) error
func LoadRTF(f *os.File, doc IoDocument) error
func LoadFile(file string, doc IoDocument) error

在这个架构,我们认为有两套 DOM

一套是 IO DOM,即 IoDocument 接口及其相关的接口。

一套是核心系统自己的 DOM,也就是 Document 类及其相关的接口

RTF 文件转为 Word 文件



func ConvRTF2Word(rtf *os.File, word IStorage) error {
  doc := NewIoDocument()
  err := LoadRTF(rtf, doc)
  if err != nil {
    return err
  }
  return SaveWord(word, doc)
}

加载一个 Word 文件


func LoadWordDocument(stg IStorage) (*Document, error) {
  doc := NewDocument()
  err := LoadWord(stg, doc.Io())
  if err != nil {
    return nil, err
  }
  return doc, nil
}

IO 子系统在特定的场景下,其实与排版与绘制子系统相关,包括:

  • 屏幕绘制(onPaint);
  • 打印(onPrint)。

通过排版(Render)功能,可以渲染出 View 层所需的显示数据,我们不妨称之为 View DOM。

func Render(doc IoDocument) (ViewDocument, error)

func SavePDF(f *os.File, doc ViewDocument) error
func SavePS(f *os.File, doc ViewDocument) error

不断重新审视边界

io.Reader/Writer 来表示

func SaveRTF(f *os.File, doc IoDocument) error
func LoadRTF(f *os.File, doc IoDocument) error

func SavePDF(f *os.File, doc ViewDocument) error
func SavePS(f *os.File, doc ViewDocument) error

改编为

func SaveRTF(f io.Writer, doc IoDocument) error
func LoadRTF(f io.Reader, doc IoDocument) error

func SavePDF(f io.Writer, doc ViewDocument) error
func SavePS(f io.Writer, doc ViewDocument) error

在我们模块提高到足够通用的、普适的场景来看时,实际上并不需要剪贴板这样具体的用户场景,也能够及时地发现这种过度约束。

我们的 IO 子系统的入口级的接口:

func SaveFile(file string, format string, doc IoDocument) error
func LoadFile(file string, doc IoDocument) error

其一,LoadFile 方法我们可能希望知道加载的文件具体是文档格式,所以应该改为

func LoadFile(file string, doc IoDocument) (format string, err error)

我们输入的数据源不一定是文件,还可能是 io.ReaderIStorage 等,在 Windows 平台下有 STGMEDIUM 结构体来表达通用的介质类型,可以参考。从跨平台的角度,也可以考虑直接用 Go 语言中的任意类型。如下:

func Save(src interface{}, format string, doc IoDocument) error
func Load(src interface{}, doc IoDocument) (format string, err error)

用了 interface{} 这样的任意类型,就意味着我们需要在文档层面上补充清楚我们都支持些什么,不支持些什么,避免在团队共识上遇到麻烦。

考虑 PDF/PS 这类非流式文档的支持,我们不能用 IoDocument 作为输入文档的类型。也就是说,以下接口:

func Save(dest interface{}, format string, doc IoDocument) error

标签: 架构, 模块, 系统, 核心, 功能, 接口

知识共享许可协议
本作品采用知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议进行许可。

发送一条友善的评论

  • 目录