2011-01-27 31 views
5

Tôi đang chuyển đổi một số mã Java thành Scala, cố gắng làm cho mã đó thành thành ngữ nhất có thể.Cách thành ngữ để sử dụng Tùy chọn trong Scala

Vì vậy, bây giờ tôi có một số mã bằng cách sử dụng tùy chọn thay vì giá trị nullable, và tôi tự hỏi liệu mọi thứ là scala'ish, hoặc cho dù tôi là sai. Vậy, các bạn có thể chỉ trích đoạn mã sau đây không?

Những lĩnh vực mà tôi đặc biệt tìm kiếm thông tin phản hồi là:

  • Việc sử dụng một đối tượng đồng hành như một nhà máy, đưa ra 2 lựa chọn tùy thuộc vào việc chúng ta muốn vượt qua Options hoặc Strings: là chuỗi nhà xây dựng tốt, hoặc chúng ta nên luôn luôn phơi bày thực tế rằng nó là một lựa chọn?
  • Việc sử dụng điều kiện tiên quyết: có cách nào tốt hơn để khẳng định thực tế là alpha3Code và tên là bắt buộc hay không và một tùy chọn không null phải được chuyển cho alpha2Code? (Tôi đang sử dụng số Guava cho chuỗi utils, vì tôi chưa tìm thấy bất kỳ thứ gì trong API Scala)
  • Việc triển khai hashCode, bằng và toString. bằng và toString đại biểu để Guava một lần nữa, trong khi bằng equals sử dụng phù hợp với mô hình. Có cách nào mở rộng hơn không?
  • Tôi biết tôi có thể đã sử dụng các lớp Case, đã tạo ra các triển khai mặc định, nhưng tôi chủ yếu quan tâm đến việc học cách tôi nên triển khai các trường hợp trong trường hợp không thể sử dụng các trường hợp.

Cảm ơn rất nhiều!

package com.sirika.openplacesearch.api.language 

import com.google.common.base.Objects 
import com.google.common.base.Strings 

object Language { 
    def apply(name : String, alpha3Code : String, alpha2Code : Option[String]) = new Language(name, alpha3Code, alpha2Code) 
    def apply(name : String, alpha3Code : String, alpha2Code : String = null) = new Language(name, alpha3Code, Option(alpha2Code)) 
    def unapply(l : Language) = Some(l.name, l.alpha3Code, l.alpha2Code) 
} 


class Language(val name : String, val alpha3Code : String, val alpha2Code : Option[String]) { 
    require(!Strings.isNullOrEmpty(alpha3Code)) 
    require(!Strings.isNullOrEmpty(name)) 
    require(alpha2Code != null) 

    override def hashCode(): Int = Objects.hashCode(alpha3Code) 

      override def equals(other: Any): Boolean = other match { 
     case that: Language => this.alpha3Code == that.alpha3Code 
     case _ => false 
    } 

    override def toString() : String = Objects.toStringHelper(this) 
     .add("name", name)  
     .add("alpha3", alpha3Code) 
     .add("alpha2", alpha2Code) 
     .toString() 
} 
+1

Các "lừa" để sử dụng tùy chọn là chỉ lựa chọn sử dụng và buộc người tiêu dùng phải làm như vậy ;-) Tất nhiên đây không phải là luôn luôn thực tế khi giao dịch với Java (ick!). Chào mừng bạn đến với SO. –

+0

Tôi không nghĩ rằng yêu cầu (alpha2Code! = Null) có thể thất bại vì alpha2Code là một Option – Azzie

Trả lời

4

Tôi nghĩ bạn chỉ nên để lộ Option[String] trong phương thức nhà máy. Ví dụ tôi, với tư cách là người dùng thư viện của bạn, cũng sẽ tự hỏi mình nên sử dụng phương pháp nhà máy nào. Và có lẽ tôi sẽ sử dụng Option.

Scala cung cấp cho chúng tôi đủ công cụ để làm cho cuộc sống của chúng ta dễ dàng hơn. Ví dụ, bạn có thể sử dụng mặc định cho tùy chọn như thế này:

def apply(name: String, alpha3Code: String, alpha2Code: Option[String] = None) = 
new Language(name, alpha3Code, alpha2Code) 

Nếu tôi, một lần nữa là người dùng của thư viện của bạn, muốn vượt qua chỉ chuỗi mà không cần gói nó trong Some mỗi lần, tôi có thể viết chuyển đổi ngầm của riêng tôi như thế này :

implicit def anyToOption[T](t: T): Option[T] = Some(t) 

hoặc thậm chí (nếu cá nhân tôi sử dụng null):

implicit def anyToOption[T](t: T): Option[T] = 
if (t == null) None else Some(t) 

Nhưng tôi tin rằng, nếu bạn thực thi tùy chọn, nó sẽ làm cho API của bạn vững chắc hơn và rõ ràng.

+3

Trên thực tế, có một cách dễ dàng hơn để bọc một đối tượng trong 'Option' sao cho giá trị' null' sẽ trở thành 'None': 'Option (t)' (thay vì 'Some (t)') – Madoc

4

Bạn nên tránh null trừ khi không có lý do chính đáng. Vì vậy, bạn có thể vừa viết điều này:

def apply(name : String, alpha3Code : String, alpha2Code : String) = new Language(name, alpha3Code, Option(alpha2Code)) 
def apply(name : String, alpha3Code : String) = new Language(name, alpha3Code, None) 

Điều kiện tiên quyết là tốt. Bạn có thể viết như sau:

require(Option(alpha3Code) exists (_.nonEmpty)) 
require(Option(name) exists (_.nonEmpty)) 

Không nhất thiết phải cải thiện.

A StringhashCode, vì vậy tôi không hiểu tại sao bạn gọi một phương pháp khác để tạo mã băm thay vì chỉ gọi alpha3Code.hashCode. Tôi nghĩ rằng có một cái gì đó trong API Scala, mặc dù. Không chắc.

equals phải có phương thức canEqual, trừ khi bạn tạo lớp học sealed hoặc final. mô hình phù hợp là khá nhiều cách để làm điều đó, mặc dù bạn có thể viết nó như thế này cho sự hiện diện của một vắt:

case Language(_, `alpha3Code`, _) => true 

Nhưng cách bạn đã viết nó là khá nhiều cách thức mà nó thường được viết.

+0

Các câu trả lời khác dường như nhấn mạnh thực tế là tôi thực sự buộc người tiêu dùng sử dụng phương thức khởi tạo Option. Bạn có đồng ý với tuyên bố đó không? –

+0

Về hashCode, đúng là trong trường hợp này, tôi có thể sử dụng trực tiếp alpha3Code.hashCode, tôi thậm chí không nghĩ về nó vì tôi có thói quen luôn sử dụng cú pháp ổi, cho phép thêm một số khóa: Objects.hashCode (alpha3Code, tên) –

+0

Và điểm tốt liên quan đến phương pháp canEqual. Tôi đã không nhận thức được nó. Đối với những người không biết về canEqual, bạn có thể tham khảo http://books.google.com/books?id=MFjNhTjeQKkC&pg=PA555&lpg=PA555&dq=scala+canEqual+method&source=bl&ots=FKukUGGNvo&sig=0rF3cRgwHVGZ22ggxX5j23nM32w&hl=vi&ei=As1CTaWhMoPGlQfknZ35Dw&sa= X & oi = book_result & ct = kết quả & resnum = 3 & ved = 0CCcQ6AEwAg # v = onepage & q = scala% 20canEqual% 20method & f = false –

-1

Tôi không thích Tùy chọn - chúng thêm mức độ gián đoạn không cần thiết và gây nhầm lẫn trong nhiều trường hợp. Tôi không thích null hơn nữa, vì vậy tôi hiểu rằng việc sử dụng Tùy chọn thường là hợp lý. Tuy nhiên, bạn nên luôn luôn xem nếu có cách tự nhiên hơn để loại bỏ việc sử dụng Option trong một giao diện.

Thông số mặc định hoặc quá tải riêng biệt thường là tùy chọn tốt hơn. Vì vậy, tôi muốn viết lại mã của bạn như thế này:

package com.sirika.openplacesearch.api.language 

import com.google.common.base.Strings 
import com.google.common.base.Objects 

object Language { 
    def apply(name : String, alpha3Code : String, alpha2Code : String) = new Language(name, alpha3Code, alpha2Code) 
    def apply(name : String, alpha3Code : String) = new Language(name, alpha3Code) 
    def unapply(l : Language) = Some(l.name, l.alpha3Code, l.alpha2Code) 
} 


class Language private (val name : String, val alpha3Code : String, val alpha2Code : Option[String]) { 
    def this(name:String,alpha3Code: String ,alpha2Code:String) = this(name,alpha3Code,Option(alpha2Code)) 
    def this(name:String,alpha3Code: String) = this(name,alpha3Code,None) 

    require(!Strings.isNullOrEmpty(alpha3Code)) 
    require(!Strings.isNullOrEmpty(name)) 

    override def hashCode = alpha3Code.hashCode 

    override def equals(other: Any) = other match { 
     case that: Language => this.alpha3Code == that.alpha3Code 
     case _ => false 
    } 

    override def toString = MoreObjects.toStringHelper(this) 
     .add("name", name)  
     .add("alpha3", alpha3Code) 
     .add("alpha2", alpha2Code) 
     .toString() 
} 

Guava docs

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