2017-01-31 6 views
2

私はJavaでデコレータパターンを使用するとかなり簡単に何かをしようとしましたが、Scalaでスタック可能な変更を行いたいと思って失敗しました。抽象的なvalの性格上の健全性チェック

私の使用例は次のとおりです。簡単な自動補完機能を実装しています。それらのいくつかは、トライのカスタム実装に基づいて、具体的な実装である

trait AutoCompleter { 
    def topSuggestions(prefix: String): Iterator[String] 
    def update(sentence: String*): Unit 

    final def topSuggestions(prefix: String, n: Int): List[String] = topSuggestions(prefix).take(n).toList 
} 

/** 
    * This auto-completer will try returning some suggestions when the prefix did not match any known word by dropping the 
    * last character until it finds a suggestion 
    */ 
trait TolerantAutoCompleter extends AutoCompleter { 
    def MaxRetries: Int 

    abstract override def topSuggestions(prefix: String): Iterator[String] = { 
    if (MaxRetries < 1) throw new IllegalArgumentException("Should allow 1 retry minimum, but max retries was: " + MaxRetries) 
    for (attempt <- 0 to Math.min(prefix.length, MaxRetries)) { 
     val suggestions = super.topSuggestions(prefix.substring(0, prefix.length - attempt)) 
     if (suggestions.hasNext) return suggestions 
    } 
    Iterator() 
    } 
} 

/** 
    * This auto-completer learns from the user's input, and therefore does not require a preliminary dictionary. 
    */ 
class ParrotAutoCompleter extends AutoCompleter { 
    private val trie = new WeightedTrie[Char, String]() 

    override def topSuggestions(prefix: String): Iterator[String] = trie.prefixedBy(prefix) 
    override def update(sentence: String*): Unit = sentence.foreach(trie += _) 
} 

そして、いくつかの他のスタッカブル変更がある彼らはすべての基本特性を実装します私は次のように使います:

val autoCompleter = new ParrotAutoCompleter with TolerantAutoCompleter { override val MaxRetries: Int = 5 } 

この実装はすべて問題なく動作しますが、欠陥があります。MaxRetriesで実行されたサニティチェックは、自動コンプリータを使用する場合のみ作成するのではなく、遅れて行われます。より逸話的に言えば、たった1回ではなく毎回実行されます。

valまたはdefとして宣言されているかどうかにかかわらず、MaxRetriesがオーバーライドされる前でも、特性のメソッド外のコードがすぐに実行されるという問題があります。

ビルド時、オーバーライド後、スタック可能な変更のプロパティを失うことなく、サニティチェックを実行するにはどうすればよいですか?

答えて

2

override val MaxRetries = 5の場合、匿名クラスのコンストラクタでのみ初期化されるフィールドを作成します。

<jvm>: Initialize MaxRetries field to 0 
<anon>: call super 
TolerantAutoCompleter: call super 
... 
TolerantAutoCompleter: sanity check MaxRetries (still 0!) 
TolerantAutoCompleter: return 
<anon>: set MaxRetries to 5 
<anon>: return 

代わりに使用

def付き)
new ParrotAutoCompleter with TolerantAutoCompleter { override def MaxRetries = 5 } 

:コンストラクタの流れは次のようになります。

+0

あなたの答えは正しいと思われますが、あなたがそれをフレーム化した方法は、その説明が不明瞭であると思います。私はあなたがこの流れを説明しているコードを正確には知らないのです(ただし、あなたが私のスニペットについて話しているのは、あなたが特性の身体検査を動かすことを除いては想像していますが)。 。この2点を明確にしてください。 – Dici

+0

再計算を避けるために 'lazy val'を使用する方がより標準的です(ただし、リテラルの特定の場合は、' def'が改善されます)。 –

+0

@AlexeyRomanov私は実際に特性宣言で 'lazy'を使用しようとしましたが、コンパイルされませんでしたが、私はそれを具体的な使い方で使用すべきでした。それは問題を解決することができますか(今試みることはできません)?もしそうなら、この文脈で初期化順序の規則を説明する答えを追加できますか? – Dici

関連する問題