2012-06-25 34 views
6

Tôi đang cố gắng mô hình hóa các phản hồi từ các API REST như các lớp trường hợp mà tôi có thể sử dụng đối sánh mẫu trên đó.Tạo mô hình với trường hợp Scala

Tôi nghĩ rằng nó sẽ phù hợp với giả định thừa kế, nhưng tôi thấy rằng điều này không được chấp nhận. Tôi biết đã có các câu hỏi liên quan đến các trường hợp và thừa kế, nhưng câu hỏi của tôi là nhiều hơn về cách bạn mô hình hóa "đúng cách" sau đây mà không cần thừa kế.

tôi bắt đầu với hai lớp trường hợp sau đây, mà làm việc tốt:

case class Body(contentType: String, content: String) 
case class Response(statusCode: Int, body: Body) 

tức là một cuộc gọi REST sẽ trở lại với một cái gì đó như:

Response(200, Body("application/json", """{ "foo": "bar" }""")) 

mà tôi có thể mô hình phù hợp như:

response match { 
    case Response(200, Body("application/json", json)) => println(json) 
    case Response(200, Body("text/xml", xml)) => println(xml) 
    case Response(_,_) => println("Something unexpected") 
} 

v.v. hoạt động tốt.

đâu tôi chạy vào rắc rối là: Tôi muốn mở rộng helper cho các lớp này trường hợp, chẳng hạn như:

case class OK(body: Body) extends Response(200, body) 
case class NotFound() extends Response(404, Body("text/plain", "Not Found")) 

case class JSON(json: String) extends Body("application/json", json) 
case class XML(xml: String) extends Body("text/xml", xml) 

vì vậy mà tôi có thể làm đơn giản hóa mô hình phù hợp như thế này:

response match { 
    case OK(JSON(json)) => println(json) 
    case OK(XML(xml)) => println(xml) 
    case NotFound() => println("Something is not there") 

    // And still drop down to this if necessary: 
    case Response(302, _) => println("It moved") 
} 

và cũng sẽ cho phép mã REST của tôi trực tiếp sử dụng và trả lại:

Response(code, Body(contentType, content)) 

là e asier để xây dựng một phản ứng tự động.

như vậy ...

tôi có thể lấy nó để biên dịch (với lời cảnh báo không dùng nữa) thông qua:

case class OK(override val body: Body) extends Response(200, body) 

Tuy nhiên, điều này dường như không làm việc với mô hình kết hợp.

Response(200, Body("application/json", "")) match { 
    case OK(_) => ":-)" 
    case _ => ":-(" 
} 
res0: java.lang.String = :-(

Bất kỳ ý tưởng nào về cách thức này có thể hoạt động? Tôi mở ra các cách tiếp cận khác nhau, nhưng đây là nỗ lực của tôi để tìm cách sử dụng thực tế cho các trường hợp lớp học

Trả lời

10

Có một số lý do tại sao các trường hợp là shouldn't be subclassed. Trong trường hợp của bạn, vấn đề trở thành rằng OK là một loại khác (loại phụ của) Response, do đó kết quả không thành công (ngay cả khi đối số khớp, loại không khớp).

Thay vào đó, bạn sẽ muốn custom extractors. Ví dụ:

case class Response(code: Int, body: String) 
object OK { 
    def apply(body: String) = Response(200, body) 
    def unapply(m: Response): Option[String] = m match { 
    case Response(200, body) => Some(body) 
    case _     => None 
    } 
} 

def test(m: Response): String = m match { 
    case OK(_) => ":-)" 
    case _  => ":-(" 
} 

test(Response(300, "Hallo")) // :-(
test(Response(200, "Welt")) // :-) 
test(OK("Welt"))    // :-) 

Có vài ví dụ khác về nhổ tùy chỉnh trong this thread.

+0

Ah, cảm ơn bạn - Tôi thấy tôi đã hoàn toàn bỏ lỡ mục đích không áp dụng cho đến khi điều này; Điều này rất hữu ích. Sẽ kiểm tra điều này hoàn toàn với mã của tôi để chắc chắn rằng tôi đã bảo hiểm và sẽ chấp nhận sau tối nay. – 7zark7

+0

Câu trả lời hay @Sciss. Trình giải nén tùy chỉnh là một trong những điều tôi thích rất nhiều về Scala. – mergeconflict

+0

@ 7zark7 Lưu ý rằng khi bạn sử dụng bộ giải nén tùy chỉnh, bạn sẽ mất được sự đảm bảo đầy đủ của các lớp được niêm phong. –

1

Bạn đã xem thư viện scala chưa lọc chưa? http://unfiltered.lessis.me/ Nó có thể giúp bạn tiếp cận vấn đề của bạn. HTH

+0

Tôi đã có một cái nhìn nhưng tôi bỏ thuốc lá vì có quá nhiều slide, với 1 câu/một vài từ trên mỗi. Có lẽ bất kỳ phiên bản trang đơn nào làm rõ những gì Unfiltered là tất cả về? – KajMagnus

+0

Có thể điều này sẽ giúp tốt hơn: https://github.com/softprops/Unfiltered – AndreasScheinert

1

Trong khi các trình giải nén tùy chỉnh được đề cập bởi 0__ chắc chắn có thể được sử dụng, bạn sẽ mất các đảm bảo đầy đủ về các hệ thống phân cấp loại kín. Trong khi trong ví dụ bạn đưa ra câu hỏi thì không có gì là sealed, vấn đề rất phù hợp với họ.

Trong trường hợp đó, đề xuất của tôi chỉ đơn giản là đảm bảo rằng case class luôn ở dưới cùng của phân cấp loại và làm cho các lớp trên bình thường. Ví dụ:

sealed class Response(val statusCode: Int, val body: Body) sealed 
case class Ok(override val body: Body) extends Response(200, body) 
sealed class NotOk(statusCode: Int, body: Body) extends Response(statusCode, body) 
case object NotFound extends NotOk(404, "Not found") 
// and so on... 
+0

Cảm ơn Daniel, trong khi ấn tượng đầu tiên của tôi là điều này sẽ không hoạt động nếu tôi cũng muốn cho phép các trận đấu trên Phản hồi - tôi thấy điều này có thể hoạt động nếu tôi xác định unapply trên một đối tượng Response như Sciss đề cập đến, và có "helpers" là các lớp case. Sẽ thử cả hai cách tiếp cận ngày hôm nay và xem những gì phù hợp/hoạt động tốt nhất ở đây. – 7zark7

+0

Ý của bạn là viết 'phản hồi lớp kín'? –

+0

@Sciss Có, và 'NotOk' là tốt. Cảm ơn vì đã chỉ ra sai lầm của tôi. –

Các vấn đề liên quan