2012-12-02 37 views
7

Tôi muốn tạo ra trường hợp đối tượng có loại được xác định bởi dữ liệu thời gian chạy:scala động xác định loại

trait Business 
case class Business1() extends Business 
case class Business2() extends Business 

object Business { 
    def fromData(data:Array[Byte]): Business = data(0) match { 
    case 1 => new Business1 
    case 2 => new Business2 
    case _ => throw new RuntimeException("data error") 
    } 
} 

Đoạn mã trên có thể làm công việc của mình nhưng có một vấn đề mà nó được đóng lại. Bất cứ khi nào tôi triển khai lớp con mới Business, tôi sẽ phải sửa đổi mã Business.fromData, ví dụ:

case 3 => new Business3 

Làm thế nào tôi có thể xác định Business.fromData một lần và sau đó có thể thêm Business3, Business4 mà không cần đăng ký với nó?

Sửa

cuối cùng tôi nhận ra rằng đây là một trường hợp sử dụng hoàn hảo của Multimethod, có nghĩa là, cử dựa trên chức năng của một số đối số. Vì vậy, câu hỏi tổng quát hơn nên là "Làm thế nào để thực hiện đa phương thức trong scala"? Tôi tin rằng các mẫu thiết kế chỉ tồn tại vì ngôn ngữ không có khả năng, đó là lý do tại sao tôi không muốn chấp nhận câu trả lời của nhà máy.

+1

tôi sẽ ghi vào địa ngục để gợi ý phản chiếu java? :) –

Trả lời

3

này không giải quyết vấn đề của bạn, nhưng nếu bạn thực hiện Business một "kín" đặc điểm sau đó trình biên dịch sẽ ít bắt bất kỳ trận đấu không đầy đủ cho đến khi bạn đã cập nhật fromData:

sealed trait Business 
case class Business1() extends Business 
case class Business2() extends Business 

biz match { 
    case Business1 => println("Business1") 
} 

... sẽ kết quả trong ...

warning: match is not exhaustive! 
missing combination   Business2 
3

Bạn cũng có thể làm điều này. Mặc dù tôi không chắc chắn nếu đó là tốt hơn thì trường hợp phù hợp. Phụ thuộc vào những gì bạn đang cố gắng làm.

class Business { 
    override def toString = "Business" 
} 

val factories: Map[Int,() => Business] = Map(
    1 -> (() => new Business { 
    override def toString = "Business1" 
    }), 
    2 -> (() => new Business { 
    override def toString = "Business2" 
    }), 
    3 -> (() => new Business { 
    override def toString = "Business3" 
    }) 
) 

object Business { 
    def fromData(data: Array[Byte]): Business = factories(data(0))() 
} 

val b = Business.fromData(Array(1,1,2)) 
println(b) 
+0

'factories' vẫn đóng – xiefei

1

Câu trả lời cổ điển là sử dụng nhà máy có đăng ký, nhà máy trừu tượng a.k.a. Vì vậy, bạn đã tạo ra một bản đồ 'nhà máy' giống như bản trình bày ở đây trong một câu trả lời khác, nhưng bạn cũng sẽ tạo ra một hệ thống phân cấp song song của người tạo đối tượng và đăng ký chúng khi khởi động, như vậy:

trait BusinessCreator { 
    def createBusiness() : Business 
} 
object BusinessCreator1() extends BusinessCreator { 
    override def createBusiness() : Business = new Business1() 

    factories += "1" -> this 
} 
//etc. 

một 'Scalaish' cách hơn để làm điều đó sẽ được bỏ qua hệ thống phân cấp song song và chỉ cần đăng ký một hàm tạo trong các nhà máy phản đối từ một đối tượng đồng hành với mỗi lớp, nhưng ý tưởng là như nhau.

+0

Điều này sẽ không hoạt động kể từ khi cá thể 'BusinessCreator' được tạo ra một cách lười biếng - nó sẽ không được khởi tạo cho đến khi nó được tham chiếu. Vì vậy, bạn sẽ cần phải tham khảo tất cả những người sáng tạo của bạn mà lại là một quá trình khép kín. –

0

Còn cái này thì sao?

val factories = collection.mutable.Map(
    1 -> new Function0[Business] { 
    private[this] lazy val single = new Business { 
     override def toString = "Business1" 
    } 
    override def apply() = single 
    } 
    ,2 -> new Function0[Business] { 
    private[this] lazy val single = new Business { 
     override def toString = "Business2" 
    } 
    override def apply() = single 
    } 
    ,3 -> new Function0[Business] { 
    private[this] lazy val single = new Business { 
     override def toString = "Business3" 
    } 
    override def apply() = single 
    } 
) 
Các vấn đề liên quan