2015-07-01 70 views
2

我希望能夠不太嚴格地解組yaml文件。也就是說,我的庫有yaml文件必須具有的預定義數量的選項。然後,用戶應該能夠擴展它以包含任何自定義選項。Golang結構向下轉換列表

這裏是我有什麼

package main 

import (
    "net/http" 

    "yamlcms" 

    "github.com/julienschmidt/httprouter" 
) 

type Page struct { 
    *yamlcms.Page 
    Title string 
    Date string 
} 

func getBlogRoutes() { 
    pages := []*Page{} 
    yamlcms.ReadDir("html", pages) 
} 

// This section is a work in progress, I only include it for loose context 
func main() { 
    router := httprouter.New() 
    //blogRoutes := getBlogRoutes() 
    //for _, blogRoute := range *blogRoutes { 
    // router.Handle(blogRoute.Method, blogRoute.Pattern, 
    //  func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {}) 
    //} 
    http.ListenAndServe(":8080", router) 
} 

這裏是yamlcms包:

package yamlcms 

import (
    "io/ioutil" 
    "os" 
    "strings" 

    "gopkg.in/yaml.v2" 
) 

type Page struct { 
    Slug string `yaml:"slug"` 
    File string `yaml:"file"` 
} 

func (page *Page) ReadFile(file string) (err error) { 
    fileContents, err := ioutil.ReadFile(file) 
    if err != nil { 
     return 
    } 

    err = yaml.Unmarshal(fileContents, &page) 
    return 
} 

func isYamlFile(fileInfo os.FileInfo) bool { 
    return !fileInfo.IsDir() && strings.HasSuffix(fileInfo.Name(),  ".yaml") 
} 

func ReadDir(dir string, pages []*Page) (err error) { 
    filesInfo, err := ioutil.ReadDir(dir) 
    if err != nil { 
     return 
    } 

    for i, fileInfo := range filesInfo { 
     if isYamlFile(fileInfo) { 
      pages[i].ReadFile(fileInfo.Name()) 
     } 
    } 

    return 
} 

有一個編譯器的問題在這裏:

src/main.go:19: cannot use pages (type []*Page) as type []*yamlcms.Page in argument to yamlcms.ReadDir 

我在這個問題的主要目的是學習在Go中做這種事的習慣方式。其他第三方解決方案可能存在,但我並不立即對它們感興趣,因爲我經常在Go中遇到類似繼承等問題。所以,根據我所介紹的內容,我怎樣才能最好(慣用)完成我的目標?

編輯:

所以我做了一些修改建議。現在我有這樣的:

type FileReader interface { 
    ReadFile(file string) error 
} 

func ReadDir(dir string, pages []*FileReader) (err error) { 
    filesInfo, err := ioutil.ReadDir(dir) 
    if err != nil { 
     return 
    } 

    for i, fileInfo := range filesInfo { 
     if isYamlFile(fileInfo) { 
      (*pages[i]).ReadFile(fileInfo.Name()) 
     } 
    } 

    return 
} 

但是,我仍然得到了類似的編譯器錯誤:

src/main.go:19: cannot use pages (type []*Page) as type []*yamlcms.FileReader in argument to yamlcms.ReadDir 

即使main.Page應該是的FileReader,因爲它嵌入yamlcms.Page。

+2

「我在Go中經常遇到像繼承問題一樣的問題」 - 這是因爲在繼承中沒有繼承。 – JimB

回答

1

編輯:我忘了接口切片不能這樣工作。您需要分配一個新的分片,將所有頁面轉換爲FileReaders,調用該函數並將其轉換回來。

另一種可能的解決方案是重構yamlcms.ReadDir返回文件的內容,使他們能夠在以後解組:

// In yamlcms. 
func ReadYAMLFilesInDir(dir string) ([][]byte, error) { ... } 

// In client code. 
files := yamlcms.ReadYAMLFilesInDir("dir") 
for i := range pages { 
    if err := yaml.Unmarshal(files[i], &pages[i]); err != nil { return err } 
} 

原來的答案:

有沒有這樣的事情繼承或鑄造在Go。更喜歡設計中的組合和界面。在你的情況下,你可以重新定義你的yamlcms.ReadDir接受一個接口,FileReader

type FileReader interface { 
    ReadFile(file string) error 
} 

兩個yamlcms.Pagemain.Page將實現這一點,因爲後者嵌入前者。

+0

我明白了,但是我不喜歡的是,yamlcms.Page和main.Page都必須實現ReadFile函數......除簽名外,它們將完全相同。 – eatonphil

+0

@eatonphil:不,它們將是完全相同的簽名,但類型除外。 'Page'實現'FileReader',因爲它嵌入了'* yamlcms.Page'。 – JimB

+0

@eatonphil這就是你困惑的地方。因爲你在'main.Page'中嵌入了'yamlcms.Page','main.Page'將被識別爲實現這個接口。您可以直接從'main.Page'的實例調用ReadFile。其實,我強烈建議不要重新實施該方法。嵌入不是繼承,嵌入另一種類型的覆蓋方法將是一個非常糟糕的設計方法。把它看作傳統OOP中的組合,但你不必引用你組成的東西,而是可以直接調用嵌入類型的方法。 – evanmcdonnal