2015-02-23 14 views
5

Tôi muốn loại bỏ một thời gian chạy diễn viên chung (asInstanceOf[A]) mà không có chuyển đổi tiềm ẩn.Trả về bản sao của trường hợp lớp từ chức năng chung mà không có thời gian chạy diễn viên

Điều này xảy ra khi tôi có mô hình dữ liệu khá sạch sẽ bao gồm các lớp chữ thường có đặc điểm chung và muốn triển khai một thuật toán chung trên đó. Ví dụ, thuật toán kết quả sẽ lấy một lớp kiểu A là một lớp con của trait T và được cho là trả về một bản sao của lớp bê tông A với một số trường được cập nhật.

Điều này rất dễ thực hiện khi tôi chỉ cần thêm một phép tính trừu tượng copy vào đặc điểm cơ sở và thực hiện điều đó trong tất cả các lớp con. Tuy nhiên điều này có khả năng gây ô nhiễm cho mô hình với các phương thức chỉ được yêu cầu bởi các thuật toán nhất định và đôi khi không thể thực hiện được vì mô hình có thể nằm ngoài tầm kiểm soát của tôi.

Dưới đây là một ví dụ đơn giản để minh họa sự cố và giải pháp bằng cách sử dụng thời gian chạy phôi.

Vui lòng không bị treo lên các chi tiết.

Giả sử có một đặc điểm và một số lớp trường hợp tôi không thể thay đổi:

trait Share { 
    def absolute: Int 
} 

case class CommonShare(
    issuedOn: String, 
    absolute: Int, 
    percentOfCompany: Float) 
    extends Share 

case class PreferredShare(
    issuedOn: String, 
    absolute: Int, 
    percentOfCompany: Float) 
    extends Share 

Và đây là một phương pháp đơn giản để tính toán lại dòng percentOfCompany khi tổng số cổ phần đã thay đổi và cập nhật các trường trong lớp trường hợp

def recalculateShare[A <: Share](share: A, currentTotalShares: Int): A = { 

    def copyOfShareWith(newPercentage: Float) = { 
    share match { 
     case common: CommonShare => common.copy(percentOfCompany = newPercentage) 
     case preferred: PreferredShare => preferred.copy(percentOfCompany = newPercentage) 
    } 
    } 

    copyOfShareWith(share.absolute/currentTotalShares.toFloat).asInstanceOf[A] 
} 

Một số ví dụ lời gọi trên REPL:

scala> recalculateShare(CommonShare("2014-01-01", 100, 0.5f), 400) 
res0: CommonShare = CommonShare(2014-01-01,100,0.25) 

scala> recalculateShare(PreferredShare("2014-01-01", 50, 0.5f), 400) 
res1: PreferredShare = PreferredShare(2014-01-01,50,0.125) 

Vì vậy, nó hoạt động và theo như tôi hiểu cuộc gọi .asInstanceOf[A] sẽ không bao giờ thất bại nhưng là cần thiết để làm cho biên dịch mã. Có cách nào để tránh các diễn viên thời gian chạy một cách an toàn loại mà không có chuyển đổi tiềm ẩn?

+0

Tôi không nghĩ vậy. Bạn sẽ cần một bằng chứng tiềm ẩn của typetag. Nếu không, bạn sẽ gặp phải vấn đề về xóa. –

Trả lời

4

Bạn có một vài lựa chọn mà tôi có thể nghĩ đến, và nó chủ yếu là cân bằng mức độ chung của giải pháp bạn muốn và mức độ bạn có thể dung thứ bao nhiêu.

asInstanceOf

giải pháp của bạn cảm thấy bẩn, nhưng tôi không nghĩ rằng đó là tất cả những gì xấu, và gnarliness được khá tốt kiềm chế.

Typeclass

Một cách tiếp cận tuyệt vời để cung cấp hành vi với các loại dữ liệu trong khi vẫn duy trì được tách mối quan tâm trong mã của bạn là Enrich mẫu Library/typeclass của bạn. Tôi ước gì tôi có một tài liệu tham khảo hoàn hảo cho việc này, nhưng tôi thì không. Tra cứu các thuật ngữ đó hoặc "lớp tiềm ẩn", và bạn sẽ có thể tìm đủ ví dụ để có được sự trôi dạt.

Bạn có thể tạo trait Copyable[A] { def copy(?): A } typeclass (implicit class) và tạo các phiên bản của nó cho từng loại của bạn. Vấn đề ở đây là đó là loại tiết, đặc biệt là nếu bạn muốn phương pháp copy là hoàn toàn chung chung. Tôi để lại danh sách tham số của nó như là một dấu chấm hỏi bởi vì bạn chỉ có thể chỉnh sửa nó thành những gì bạn thực sự cần, hoặc bạn có thể cố gắng làm cho nó hoạt động cho bất kỳ case class, điều này sẽ khá khó khăn, theo như tôi biết.

Optics

Ống kính đã được thực hiện để giải quyết loại này lúng túng. Bạn có thể muốn xem Monocle, đây là một cách tiếp cận chung chung tốt cho vấn đề này. Mặc dù nó vẫn không thực sự giải quyết vấn đề verbosity, nó có thể là cách để đi nếu bạn có vấn đề này định kỳ trong suốt dự án của bạn, và đặc biệt là nếu bạn thấy mình đang cố gắng thay đổi sâu bên trong đồ thị đối tượng của bạn.

+0

Câu trả lời rất hay, cảm ơn bạn! Tôi phải thử Monocle, nhưng tôi giả sử tôi sẽ cần phải vượt qua ống kính vào chức năng chung của tôi cùng với các thông số khác làm cho mã khách hàng của hàm chung của tôi phức tạp hơn. Theo như typeclasses đi, tôi đã thử cách tiếp cận đó nhưng không thực sự có nó để làm việc. Ngoài ra, những nỗ lực của tôi luôn cảnh báo tôi rằng tôi nên bật chuyển đổi tiềm ẩn, đó chính xác là những gì tôi muốn tránh. Liệu tôi có sai? –

+0

Không chắc chắn, tôi chưa thực sự thử Monocle. Nó có thể tự động tạo ra các ống kính, nhưng có vẻ như nó sẽ là lý tưởng nếu chúng có thể được nối với nhau bằng cách nào đó ngầm. Và trong trường hợp của bạn, có vẻ như nó sẽ là lý tưởng nếu có một cách dễ dàng để xác định một ống kính trên giao diện chia sẻ. IMO, Scala đau một chút vì không có sự hỗ trợ trực tiếp tốt hơn cho việc này. – acjay

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