Goでジェネリックスを本当に実行したい場合は、インターフェースが必要です。このインターフェースは、チーム用とプレーヤー用の2つのタイプで実装されます。スコア関数は、このインターフェイスタイプのオブジェクトを引数として取り、チームまたはプレーヤーのどちらで機能しているかを知らなくても、一般的なスコアリングロジックを実装します。これが概要です。詳細は次のとおりです。
インターフェイスのメソッドセットは、スコア関数が必要とする関数とまったく同じになります。必要と思われる2つの方法から始めましょう。
type scoreable interface {
stats(scID) StatLine // get statLine for ID
score(scID, int) // set score for ID
}
および一般的なスコアリング可能なIDタイプ、
type scID int
このインターフェースを実装できるタイプは、TeamIDとPlayerIDではなく、それらのマップを保持するタイプです。さらに、これらの各タイプには、マップ、StatLineおよびスコアマップの両方が必要になります。構造体はこれで機能します:
type teamScores struct {
stats map[TeamID]StatLine
scores map[TeamID]int
}
スコアリング可能な実装、
func (s *teamScores) stats(id scID) StatLine {
return s.stats[TeamID(id)]
}
func (s *teamScores) score(id scID, sc int) {
s.scores[TeamID(id)] = sc
}
待って、あなたは言うかもしれません。scIDからTeamIDへのその型変換。安全ですか?TeamIDさえも持たないという低エンジニアリングのアプローチを採用したほうがよいでしょうか。まあ、これらの方法が賢明に使われている限り、それは安全です。構造体teamScoresは、TeamIDのマップをTeamIDの別のマップに関連付けます。間もなく作成されるジェネリック関数スコアは、この構造体を引数として取り、正しい関連付けが与えられます。TeamIDとPlayerIDを混同することはできません。これは価値があり、十分な規模のプログラムではこの手法を正当化できます。
PlayerIDについても同じことを行い、同様の構造体を定義して、2つのメソッドを追加します。
スコア関数を1回記述します。このように開始します:
func score(s scoreable) {
for
おっと、反復する方法が必要です。便利な解決策は、IDのリストを取得することです。そのメソッドを追加しましょう:
type scoreable interface {
ids() []scID // get list of all IDs
stats(scID) StatLine // get statLine for ID
score(scID, int) // set score for ID
}
およびteamScoresの実装:
func (s *teamScores) ids() (a []scID) {
for tid := range s.stats {
a = append(a, scID(tid))
}
return
}
今、私たちはどこにいましたか?
func score(s scoreable) {
// lets say we need some intermediate value
sum := make(map[scID]float64)
// for each id for which we have a statLine,
for _, id := range s.ids() { // note method call
// get the statLine
stats := s.stats() // method call
// compute intermediate value
sum[id] = 0.
for _, statValue := range stats {
sum[id] += statValue
}
}
// now compute the final scores
for id, s := range sum {
score := int(s) // stub computation
s.score(id, score) // method call
}
}
この関数は、* scoreableのようなインターフェースへのポインターではなく、scoreableのインターフェースタイプをとることに注意してください。インターフェイスは、ポインタ型*teamScoresを保持できます。インターフェイスへの追加のポインタは適切ではありません。
最後に、ジェネリック関数を呼び出すには、teamScores型のオブジェクトが必要です。おそらくすでにリーグ統計を持っていますが、スコアマップをまだ作成していない可能性があります。このように一度にすべてを行うことができます:
ts := &teamScores{
stats: ...your existing map[TeamID]Statline,
scores: make(map[TeamID]int),
}
電話:
score(ts)
そして、チームのスコアはts.scoresになります。