私は Go を初めて使用します。最初にやりたいことの 1 つは、小さなマークアップ ページ生成ライブラリを Go に移植することです。主な実装は Ruby であり、その設計は非常に「古典的なオブジェクト指向」です (少なくとも、アマチュア プログラマーの観点から OO を理解している限り)。これは、マークアップされたドキュメント タイプ間の関係をどのように見るかをモデル化したものです。
Page
/ \
HTML Page Wiki Page
/ \
HTML 5 Page XHTML Page
小さなプロジェクトの場合、次のようなことをするかもしれません (現在必要な Go に変換されます)。
p := dsts.NewHtml5Page()
p.Title = "A Great Title"
p.AddStyle("default.css")
p.AddScript("site_wide.js")
p.Add("<p>A paragraph</p>")
fmt.Println(p) // Output a valid HTML 5 page corresponding to the above
大規模なプロジェクトの場合、たとえば「Egg Sample」という Web サイトの場合、既存のページ タイプの 1 つをサブクラス化し、より深い階層を作成します。
HTML 5 Page
|
Egg Sample Page
/ | \
ES Store Page ES Blog Page ES Forum Page
これは、従来のオブジェクト指向設計にうまく適合します。サブクラスは無料で多くのものを取得し、親クラスとは異なるいくつかの部分にのみ焦点を当てています。たとえば、EggSamplePage は、すべての Egg Sample ページに共通するいくつかのメニューとフッターを追加できます。
ただし、Go には型の階層という概念がありません。クラスはなく、型の継承もありません。メソッドの動的なディスパッチもありません(これは上記から続くようです; Go 型HtmlPage
は Go 型の「種類」ではありませんPage
)。
Go は以下を提供します。
- 埋め込み
- インターフェース
これらの 2 つのツールは、私が望むものを得るのに十分なはずですが、何度か間違ったスタートを切った後、私は困惑し、イライラしています。私の推測では、私はそれについて間違って考えていると思います.誰かがこれを行う方法について正しい方向に私を向けることができることを望んでいます.
これは私が抱えている特定の実際の問題です。そのため、より広範な質問に対処せずに特定の問題を解決するための提案を歓迎します。しかし、それを回避するものではなく、「構造体、埋め込み、インターフェイスをこのように組み合わせることで、簡単に思い通りの動作を実現できる」という形で答えてくれることを願っています。古典的なオブジェクト指向言語から Go に移行する多くの新参者は、同様の混乱の時期を経験する可能性が高いと思います。
通常、壊れたコードをここに表示しますが、いくつかのバージョンがあり、それぞれに問題があります。それらを含めることで、すでにかなり長くなった私の質問が実際に明確になるとは思いません。もちろん、有用であることが判明した場合は、コードを追加します。
私がやったこと:
- The Go FAQ の多くを読む(特に関連があると思われる部分)
- Effective Goの多くを読む(特に関連があると思われる部分)
- 多くの検索語の組み合わせで Google を検索した
- golang-nutに関するさまざまな投稿を読む
- 非常に不適切な Go コードが書かれている
- Go標準ライブラリのソースコードを調べて、似ていると思われる例を探しました
私が探しているものについてもう少し明確にするために:
このような階層を扱う慣用的なGoの方法を学びたいです。私のより効果的な試みの 1 つは、Go らしくないようです。
type page struct { Title string content bytes.Buffer openPage func() string closePage func() string openBody func() string closeBody func() string }
これは私を近づけましたが、完全ではありませんでした。現時点で私が言いたいのは、このような状況で Go プログラマーが使用するイディオムを学ぶのは失敗した機会のように思えるということです。
私は合理的である限りDRY(「Don't Repeat Yourself」)になりたいです。
text/template
各テンプレートの多くが他のテンプレートと同一である場合、ページの種類ごとに個別に作成する必要はありません。私の破棄された実装の 1 つはこのように動作しますが、上で概説したようにページ タイプのより複雑な階層を取得すると、管理できなくなるようです。html5Page
サポートするタイプ (や などxhtmlPage
) にそのまま使用でき、ライブラリを直接コピーして編集することなく、上記のように拡張できるコア ライブラリ パッケージを用意したいと考えています。(従来の OO では、たとえば、Html5Page を拡張/サブクラス化し、いくつかの微調整を行います。) 現在の試みは、これにはあまり適していないようです。
これについてのGoの考え方を説明するために、正しい答えは多くのコードを必要としないと思います。
更新:これまでのコメントと回答に基づいて、私はそれほど遠くないようです。私の問題は、私が思っていたよりも一般的なデザイン指向ではなく、私が物事をどのように行っているかについてもう少し正確に書かれているに違いありません。だからここに私が取り組んでいるものがあります:
type page struct {
Title string
content bytes.Buffer
}
type HtmlPage struct {
page
Encoding string
HeaderMisc string
styles []string
scripts []string
}
type Html5Page struct {
HtmlPage
}
type XhtmlPage struct {
HtmlPage
Doctype string
}
type pageStringer interface {
openPage() string
openBody() string
contentStr() string
closeBody() string
closePage() string
}
type htmlStringer interface {
pageStringer
openHead() string
titleStr() string
stylesStr() string
scriptsStr() string
contentTypeStr() string
}
func PageString(p pageStringer) string {
return headerString(p) + p.contentStr() + footerString(p)
}
func headerString(p pageStringer) string {
return p.openPage() + p.openBody()
}
func HtmlPageString(p htmlStringer) string {
return htmlHeaderString(p) + p.contentStr() + footerString(p)
}
func htmlHeaderString(p htmlStringer) string {
return p.openPage() +
p.openHead() + p.titleStr() + p.stylesStr() + p.scriptsStr() + p.con tentTypeStr() +
p.openBody()
}
これは機能しますが、いくつかの問題があります。
- それは本当に厄介な感じです
- 私は自分自身を繰り返しています
- それは不可能かもしれませんが、理想的には、すべての Page タイプに
String()
、関数を使用するのではなく、正しいことを行うメソッドが必要です。
私は何か間違ったことをしていて、これを改善できるGoのイディオムがあるのではないかと強く疑っています。
正しいことをするString()
メソッドが欲しいのですが、
func (p *page) String( string {
return p.headerString() + p.contentStr() + p.footerString()
}
は、 を介して使用された場合でも、常にpage
メソッドを使用HtmlPage
します。これは、インターフェイス以外では動的ディスパッチが不足しているためです。
私の現在のインターフェースベースのページ生成では、fmt.Println(p)
(どこp
にある種のページがあるのか) を行うだけでなく、 と の間で具体的に選択する必要がfmt.Println(dsts.PageString(p))
ありfmt.Println(dsts.HtmlPageString(p))
ます。それは非常に間違っていると感じます。
PageString()
そして、 /HtmlPageString()
とheaderString()
/の間でぎこちなくコードを複製していますhtmlHeaderString()
。
ですから、Go ではなく Ruby や Java で考え続けている結果として、まだ設計上の問題に苦しんでいるように感じます。私が説明したクライアント インターフェイスのようなものを備えたライブラリを構築するための、簡単で慣用的な Go の方法があることを願っています。