2013-09-25 2 views
3

스칼라에서 외부 라이브러리를 사용하고 있습니다. 스칼라에서는 일련의 특성을 사용하여 복잡한 구성 옵션을 다른 방법으로 전달합니다. 이것은 Highcharts Scala API이지만 문제는 좀 더 일반적인 것으로 보입니다.스칼라 (Highcharts lib)의 메소드에서 반환 된 값 수정하기

라이브러리는 여러 필드를 저장하고 전달할 수있는 데이터 전송 객체 인 특성 (실제 사용의 경우 HighchartsOptions)을 정의합니다. 간단하고 명확하게 일반화 된 코드는 다음과 같습니다

trait Opts { 
    def option1: Int = 3 
    def option2: String = "abc" 
    //Many more follow, often of more complex types 
} 

는만큼 옵션의 전체 집합을 한 곳에서 생성 할 수 있습니다,이 깔끔한 구문을 허용 :

val opts = new Opts() { 
    override val option1 = 5 
    //And so on for more fields 
} 
doSomething(opts) 

그러나,있다 하나의 코드가 그러한 구성을 준비하지만 다른 코드는 하나의 옵션을 추가로 조정해야하는 몇 가지 상황이 있습니다. 일부 Opts 인스턴스를 메소드에 전달하고 메소드가 값을 2 개 수정하게하는 것이 좋을 것입니다.

원래 특성은 vars가 아닌 defs를 기반으로하므로 위의 예와 같이 개체 유형이 알려진 경우에만 옵션의 값을 쉽게 재정의 할 수 있습니다. 메서드가 일부 익명 하위 클래스 Opts의 인스턴스 만받는 경우 어떻게 다른 인스턴스를 만들거나받은 인스턴스를 수정하여 예를 들어 option2이 다른 값을 반환 할 수 있습니까? 원하는 작업은 Mockito's spy과 비슷하지만 조롱 프레임 워크를 사용하여이 효과를 얻는 것보다 다소 인위적인 방법이 있어야한다고 생각합니다.

추신 : 사실 저는 도서관의 저자가 그러한 인터페이스를 사용하여 약간 놀랐습니다. 그래서 아마도 저는 뭔가를 놓치고 있으며, 단일 옵션 집합을 만드는 목표를 달성하는 완전히 다른 방법이 있습니다. 코드의 여러 다른 장소 (예 : 변경 가능하고 완성 된 HighchartsOptions 대신 전달할 수있는 일부 빌더 객체)?

답변

1

가장 짧은, 가장 간단한 방법 (이 몇 가지 작업을해야 할 수도 있습니다, 그래서 미안 컴파일러없이 입력 한).무료로 귀하의 옵션

object OptsConverter { 

    implicit def toOptions(opts: Opts) = Options(
    option1 = opts.option1, 
    option2 = opts.option2 
    /* ... */ 
) 
} 

(컴파일러에 의해 생성 된) 모든 복사 방법을 얻을 그 방법에 OPTS에서

case class Options(
    option1: Int, 
    option2: String 
    /* ... */ 
) extends Opts 

및 암시 적 변환 :

는 경우 클래스를 정의합니다. 옵션 OPTS 연장,

import OptsConverter.toOptions 

def usage(opts: Opts) = { 
    val improvedOpts = opts.copy(option2 = "improved") 
    /* ... */ 
} 

주, 그래서 OPTS이 필요할 때마다 당신은 그것을 사용할 수 있습니다 당신은 그런 식으로 사용할 수 있습니다. 암시 적 변환을 가져 오는 모든 장소에서 복사를 호출하여 수정 된 Opts 인스턴스를 얻을 수 있습니다.

+0

이 솔루션이 가장 우아하다고 생각합니다. 매크로가있는 것도 유혹을 불러 일으키고 더 적은 코드가 필요하지만 표준이 아닌 (지금은) 확장에 달려 있습니다. –

2

우선 Opts 특성 (전적으로)을 사용하는 것이 절대적 필요성인지 먼저 확인합니다. 바라기를 바란다면, 당신이 말했듯이, vars로 defs를 오버라이드하는 특성을 확장하십시오.

OPTS는 필수이며 일부 필드를 수정 복사 할 것을 당신이 해당 인스턴스가있는 경우, 여기 당신이 할 수있는 작업은 다음과 같습니다

이 OPTS를 확장 OPTS의 래퍼를 작성하지만, 포장 OPTS 호출 할 때마다 대표 수정하려는 필드는 제외됩니다. 해당 필드를 원하는 값으로 설정하십시오. 넓은 인터페이스 특성을위한 래퍼를 작성하는 것은 지루한 작업 일 수 있으므로 http://www.warski.org/blog/2013/09/automatic-generation-of-delegate-methods-with-macro-annotations/을 사용하여 매크로가 매크로를 자동 생성하도록 고려할 수 있습니다.

1

가장 간단한 해결책은 특성이 자신의 "복사"방법을 정의하도록 허용하고 서브 클래스 (또는 기본 클래스)가이를 사용하도록 허용하는 것입니다. 그러나 나중에 다시 캐스팅하지 않는 한 매개 변수는 실제로 기본 클래스와 만 일치 할 수 있습니다. 덧붙여 말하자면 이것은 "혼합"으로 작동하지 않으므로 루트도 추상 클래스 일 수 있지만 같은 방식으로 작동합니다. 이 점은 서브 클래스 유형이 복사 될 때 계속 전달된다는 점입니다.

trait A { 

    type myType<:A 

    def option1: Int 
    def option2: String 

    def copyA(option1_:Int=option1, option2_String=option2):myType = new A { 
     def option1 = option_1 
     def option2 = option_2 
    } 

} 

    trait B extends A { me=> 
     type myType = B 
     def option3: Double 

    //callable from A but properly returns B 

     override def copyA(option1_:Int=option1, option2_:String=option2):myType = new B { 
      def option1 = option_1 
      def option2 = option_2 
      def option3 = me.option3    
     } 



//this is only callable if you've cast to type B 
      def copyB(option1_:Int=option1, option2_:String=option2, option3_:Double=option3):myType = new B { 
       def option1 = option_1 
       def option2 = option_2 
       def option3 = option_3   
      } 


     } 
+0

감사합니다. 그러나이 솔루션은 원래 특성을 수정해야합니다.이 특성은 외부 라이브러리의 일부이기 때문에 실제로 수정할 수 없습니다. –

+0

당신은 그 특성을 추상 클래스 (A - 위)로 확장 할 수 없으며, 아래에서 A로 작업하는 모든 것을 파생시킬 수 없습니까? – LaloInDublin

+0

이론에서는 그렇습니다. 그러나 특성이 내 코드에서 사용되는 모든 장소를 변경해야하며 수정할 수없는 다른 사람의 구현을받는 경우를 처리하지 않습니다. –

관련 문제