2013-08-08 2 views
3

저는 스칼라를 가지고 놀고 있으며 클래스를 디자인하는 방법에 대한 모범 사례를 찾아 내려고했습니다. (1 주일 정도 지나서 스칼라를 시험해 본다.)인터페이스 기반 또는 인터페이스가없는 액터 기반 클래스

내 얼랭 시간 이후로 나는 메시지 전달과 배우 기반 소프트웨어의 엄청난 팬이다.

object Foo 
object Bar 
class MyActor extends Actor { 
    def receive = { 
    case Foo => ... 
    case Bar => ... 
    case _ => ... 
    } 
} 

을하지만 내 객체 지향 (인터페이스와 다형성) 캐리어는이 개념은 매우 유연 아니라고 하더군요에서 배운 대부분의 스칼라 예에서 배우 클래스는 다음과 같이 구현됩니다.

MyActor는 MyAdvancedActor로 대체 될 수 있지만 MyActor 구현이 구현해야하는 메시지를 정의하는 계약이 없습니다.

저는 스칼라에서 액터를 쓰는 것에 대해 생각할 때 몇 가지 방법을 지정하는 특성을 쓰는 경향이 있습니다. MyActor 구현은 자신의 비공개 메시지를 자신에게 보낼 수있는이 메서드를 구현해야합니다. 이 접근 방식을 통해 우리는 지정된 인터페이스를 가지며 MyActor 구현을 유형 안전 방식으로 대체 할 수 있습니다.

스칼라 튜토리얼과 예제를 읽는 시간에는 그런 수업 디자인을 접하지 못했습니다. 스칼라에서 상식이 아니거나 더 나은 방법이 있습니까? 또는 이러한 자습서는 이러한 주제를 다루기 위해 작습니까?

답변

5

일반적인 관행은 이러한 경우에 대수 데이터 형식을 사용하는 것입니다 :이 같은 모든 메시지에 대한 sealed 기본 유형을 만들 수 있습니다

sealed trait MyActorMessages 
object Foo extends MyActorMessages 
object Bar extends MyActorMessages 

그러나 계약의 이러한 종류의 컴파일러에 의해 적용되지 않습니다. 이 계약을 집행하기 위해 사용 Typed Channels을 사용할 수

class MyActor extends Actor with Channels[TNil, (MyActorMessages, MyActorReply) :+: TNil] { 
    channel[MyActorMessages] { (req, snd) ⇒ 
    req match { 
     case Foo ⇒ ... 
     case Bar ⇒ ... // You'll get a warning if you forget about `Bar` 
    } 
    } 
} 

컴파일러는 (이 경우 MyActorMessages의 모든 하위 유형) 가능한 모든 메시지 유형을 처리하기 위해 (경고)을 강제하고, 보낸 사람은 유효한 보내도록 강요 당할 것이다 <-!- 방법을 사용하는 메일 (컴파일 오류 포함).

발신자는 안전하지 않은 방법 !을 사용하여 잘못된 메시지를 보낼 수도 있습니다.

+0

채널이 새로운 주제이지만 내 채널이하는 일을 이해하고 있다고 생각합니다. 타입 안전은 요즘별로 중요하지 않습니까? 정적으로 입력 된 언어의 유용한 기능이라고 생각했습니다. 바이너리가 출하되기 전에 오류가 감지 될 수 있습니다. – r2p2

+0

@ r2p2 : 유형 안전성이 중요하므로 Typed Channels 및 [Typed Actors] (http://doc.akka.io/docs/akka/2.2.0/scala/typed-actors.html)와 같은 것들이 존재합니다 , 액터를 유형에 관계없이 안전하게 사용하는 것은 어렵습니다. '될 '것 같은 것들이 있고 어떤 발신자에게나 회신 할 길을 가져야합니다. 이 주제에 관해서는 많은 토론이 있습니다. 예를 들어 [왜 akka 액터에 메시지가 입력되지 않습니까?] (http://stackoverflow.com/q/5547947/406435). – senia

2

@senia의 솔루션을 정말 좋아합니다. Akka의 새로운 Typed Channels 기능을 효과적으로 사용합니다. 그러나 그 해결책이 당신에게 어울리지 않는다면, 저는 OO 세계에 좀 더 전통적인 것을 제공 할 수 있습니다. 이 솔루션에서는 액터가 구성되는 전략 구현을 통해 액터에 대한 실제 메시지 처리 동작을 지정합니다. 코드는 다음과 같을 것이다 : 여기

val regFooRef = system.actorOf(Props(classOf[FooActor], new RegularFoo)) 
val advFooRef = system.actorOf(Props(classOf[FooActor], new AdvancedFoo)) 

이점 중 하나는 당신이 정상 메시지 처리 동작의에서 배우의 비즈니스 로직을 분리하는 것을입니다 : 다음

//Strategy definition 
trait FooStrategy{ 
    def foo1(s:String):String 
    def foo2(i:Int):Int 
} 

//Regular impl 
class RegularFoo extends FooStrategy{ 
    def foo1(s:String) = ... 
    def foo2(i:Int) = ... 
} 

//Other impl 
class AdvancedFoo extends FooStrategy{ 
    def foo1(s:String) = ... 
    def foo2(i:Int) = ... 
} 

//Message classes for the actor 
case class Foo1(s:String) 
case class Foo2(i:Int) 

//Actor class taking the strategy in the constructor 
class FooActor(strategy:FooStrategy) extends Actor{  
    def receive = { 
    case Foo1(s) => sender ! strategy.foo1(s)   
    case Foo2(i) => sender ! strategy.foo2(i) 
    } 
} 

이 배우의 인스턴스를 생성 . 액터 클래스가 액터 물건 (사서함에서 수신, 발신자에게 응답 등)을 보내 게하고 실제 비즈니스 로직을 특성으로 캡슐화합니다. 또한 특성에 대한 단위 테스트를 통해 비즈니스 로직을 개별적으로 쉽게 테스트 할 수 있습니다. 특성의 액터 유형이 필요하다면 FooStrategy의 메서드에 암시 적으로 ActorContext을 지정할 수 있지만 액터 및 비즈니스 논리를 완벽하게 분리 할 수는 없습니다.

앞서 말했듯이, 나는 @senia의 해결책을 좋아합니다. 나는 단지 당신에게 전통적인 OO 일 수있는 다른 옵션을주고 싶었습니다.

+0

좋은 속임수. 인터페이스가 동일하게 유지되는 동안 동작을 변경하는 방법에 대해서는 생각해 본 적이 없습니다. 또한 상태 시스템을 구현하는 멋진 방법입니다. – r2p2

관련 문제