0

Scala で以下のビルダー パターンを使用します。簡単にするために、 orのみを含み、 orへの接続を持たないAそのような3 つのインスタンスを使用しています。問題は、私が使用しなければならないコードのどこでも、呼び出しが潜在的に安全ではないことです。たとえば、で失敗します。それを守るために、ケースとオプションを一致させ、None ケースを処理する必要があります。instance1field1field2field3val s = A.instance1.field1.get; doSomething(s)getA.instance1.field2.getNone.get

object A {
  val instance1 = new ABuilder().withField1("abc").build1
  val instance2 = new ABuilder().withField1("abc").withField2("def").build2
  val instance3 = new ABuilder().withField1("abc").withField3("def").build1
}

case class A(builder: ABuilder) {
  val field1: Option[String] = builder.field1
  val field2: Option[String] = builder.field2
  val field3: Option[String] = builder.field3
}

class ABuilder {
  var field1: Option[String] = None
  var field2: Option[String] = None
  var field3: Option[String] = None
  def withField1(f: String): ABuilder = {
    this.field1 = Some(f)
    this
  }
  def withField2(f: String): ABuilder = {
    this.field2 = Some(f)
    this
  }
  def withField3(f: String): ABuilder = {
    this.field3 = Some(f)
    this
  }
  def build1: A = {
    require(field1.isDefined, "field 1 must not be None")
    A(this)
  }
  def build2: A = {
    require(field1.isDefined, "field 1 must not be None")
    require(field2.isDefined, "field 2 must not be None")
    A(this)
  }
}

別の解決策は、ファントム型とも呼ばれるパラメーター化された型を使用することです。そのテーマに関する優れたチュートリアルはほとんど見つかりませんでした。また、Scala でファントム型と実際のデータ (または状態) を使用してタイプ セーフなビルダー パターンを実装する方法を見つけることができませんでした。すべての例はメソッドのみを説明しています。

私の例でファントム型を使用して、実行時None例外を回避し、適切な型不一致例外のみを取得するにはどうすればよいですか? 言及されているすべてのクラスとメソッドをパラメーター化し、封印された特性を使用しようとしていますが、これまでのところ成功していません。

4

2 に答える 2

1

本当にファントムタイプを使用したい場合は、次のことができます

object PhantomExample {
  sealed trait BaseA
  class BaseAWith1 extends BaseA
  final class BaseAWith12 extends BaseAWith1

  object A {
    val instance1 = new ABuilder().withField1("abc").build1
    val instance2 = new ABuilder().withField1("abc").withField2("def").build2
  }

  case class A[AType <: BaseA](builder: ABuilder) {
    def field1[T >: AType <: BaseAWith1] = builder.field1.get
    def field2[T >: AType <: BaseAWith12] = builder.field2.get
  }

  class ABuilder {
    var field1: Option[String] = None
    var field2: Option[String] = None
    def withField1(f: String): ABuilder = {
      this.field1 = Some(f)
      this
    }
    def withField2(f: String): ABuilder = {
      this.field2 = Some(f)
      this
    }
    def build1: A[BaseAWith1] = {
      require(field1.isDefined, "field 1 must not be None")
      A(this)
    }
    def build2: A[BaseAWith12] = {
      require(field1.isDefined, "field 1 must not be None")
      require(field2.isDefined, "field 2 must not be None")
      A(this)
    }
  }

  val x = A.instance1.field1                      //> x  : String = abc
  val x2 = A.instance2.field1                     //> x2  : String = abc
  val x3 = A.instance2.field2                     //> x3  : String = def

  // This gives compilation error
  //val x2 = A.instance1.field2
}

ただし、この種のコードを本番環境で使用することはお勧めしません。見栄えが悪く、コンパイルエラーが不可解に見え、私見は最善の解決策ではありません。インスタンスが非常に異なる場合、それらは同じ具象クラスのインスタンスでさえないのではないでしょうか?

trait BaseA {
  def field1
}
class A1 extends BaseA { }
class A2 extends BaseA { ... def field2 = ... }
于 2016-02-23T15:34:10.713 に答える
0

これがあなたの望むものかどうかはわかりませんが、ベースとして使用できると思います。

まずAクラス:

case class A(field1: String = "", 
              field2: String = "",
              field3: String = "")

ケース クラスには、空の文字列のデフォルト値があります。これにより、 None 値を気にせずに任意のフィールド値が割り当てられた A オブジェクトを作成できます。

例えば:

val b2 = A("abc", "def")
> b2: A = A(abc,def,)

val b1 = A("abc")
> b1: A = A(abc,,)

val notValidB = A(field2 = "xyz")
> notValidB: A = A(,xyz,)

ご覧のとおり、b2 と b1 は有効なオブジェクトであり、オブジェクトには field1 が必要なため、notValidB は有効ではありません。

パターン マッチングを使用して A オブジェクトを検証する別の関数を作成し、決定的なアクションに進むことができます。

def determineAObj(obj: A): Unit = obj match {
  case A(f1, f2, _) if !f1.isEmpty && !f2.isEmpty => println("Is build2")
  case A(f1, _, _) if !f1.isEmpty => println("Is build1")
  case _ => println("This object doesn't match (build1 | build2)")
}

そして実行します:

determineAObj(b1)
> "Is build1"

determineAObj(b2)
> "Is build2"

determineAObj(notValidB)
> "This object doesn't match (build1 | build2)"
于 2016-02-23T17:00:37.080 に答える