-
3.7 访问者模式
-
代码实现
原课程中讲解访问者模式的时候用到了继承和函数重载这两个 Go 中没有的特性,接下来的呢,会通过继承实现。
注意由于没有函数重载,所以我们并不知道传递过来的对象是什么类型,这个时候只能采用类型断言的方式来对不同的类型做不同的操作,但是正式由于没有函数重载,所以其实完全可以不用访问者模式直接传入参数就好了。
以前我们经常说不要用写其他语言的方式来写 Go,Go 不需要太多的设计模式,这个就是一个比较鲜明的例子
Code
package visitor import ( "fmt" "path" ) // Visitor 访问者 type Visitor interface { Visit(IResourceFile) error } // IResourceFile IResourceFile type IResourceFile interface { Accept(Visitor) error } // NewResourceFile NewResourceFile func NewResourceFile(filepath string)(IResourceFile, error) { switch path.Ext(filepath) { case ".ppt": return &PPTFile { path: filepath }, nil case ".pdf": return &PdfFile { path: filepath }, nil default: return nil, fmt.Errorf("not found file type: %s", filepath) } } // PdfFile PdfFile type PdfFile struct { path string } // Accept Accept func(f * PdfFile) Accept(visitor Visitor) error { return visitor.Visit(f) } // PPTFile PPTFile type PPTFile struct { path string } // Accept Accept func(f * PPTFile) Accept(visitor Visitor) error { return visitor.Visit(f) } // Compressor 实现压缩功能 type Compressor struct {} // Visit 实现访问者模式方法 // 我们可以发现由于没有函数重载,我们只能通过断言来根据不同的类型调用不同函数 // 但是我们即使不采用访问者模式,我们其实也是可以这么操作的 // 并且由于采用了类型断言,所以如果需要操作的对象比较多的话,这个函数其实也会膨胀的比较厉害 // 后续可以考虑按照命名约定使用 generate 自动生成代码 // 或者是使用反射简化 func(c * Compressor) Visit(r IResourceFile) error { switch f: = r.(type) { case *PPTFile: return c.VisitPPTFile(f) case *PdfFile: return c.VisitPDFFile(f) default: return fmt.Errorf("not found resource typr: %#v", r) } } // VisitPPTFile VisitPPTFile func(c * Compressor) VisitPPTFile(f * PPTFile) error { fmt.Println("this is ppt file") return nil } // VisitPDFFile VisitPDFFile func(c * Compressor) VisitPDFFile(f * PdfFile) error { fmt.Println("this is pdf file") return nil }
单元测试
package visitor import ( "testing" "github.com/stretchr/testify/require" ) func TestCompressor_Visit(t * testing.T) { tests: = [] struct { name string path string wantErr string } { { name: "pdf", path: "./xx.pdf", }, { name: "ppt", path: "./xx.ppt", }, { name: "404", path: "./xx.xx", wantErr: "not found file type", }, } for _, tt: = range tests { t.Run(tt.name, func(t * testing.T) { f, err: = NewResourceFile(tt.path) if tt.wantErr != "" { require.Error(t, err) require.Contains(t, err.Error(), tt.wantErr) return } require.NoError(t, err) compressor: = & Compressor {} f.Accept(compressor) }) } } // 不用 Accept 其实也是可以的 func TestCompressor_Visit2(t * testing.T) { tests: = [] struct { name string path string wantErr string } { { name: "pdf", path: "./xx.pdf", }, { name: "ppt", path: "./xx.ppt", }, { name: "404", path: "./xx.xx", wantErr: "not found file type", }, } for _, tt: = range tests { t.Run(tt.name, func(t * testing.T) { f, err: = NewResourceFile(tt.path) if tt.wantErr != "" { require.Error(t, err) require.Contains(t, err.Error(), tt.wantErr) return } require.NoError(t, err) compressor: = & Compressor {} compressor.Visit(f) }) } }
- 留下你的读书笔记
- 你还没登录,点击这里
-
用户笔记留言