2011-06-24 52 views
19

Tôi đã nhìn vào ví dụ về khai thác gỗ tại Scala, và nó thường trông như thế này:Làm cách nào để đăng nhập vào Scala * mà không có * tham chiếu đến trình ghi nhật ký trong * mọi trường hợp *?

import org.slf4j.LoggerFactory 

trait Loggable { 
    private lazy val logger = LoggerFactory.getLogger(getClass) 
    protected def debug(msg: => AnyRef, t: => Throwable = null): Unit = 
    {...} 
} 

này dường như không phụ thuộc vào khung đăng nhập cụ thể. Trong khi điều này thực hiện công việc, nó cũng giới thiệu một val lười biếng không liên quan trong mọi trường hợp muốn ghi nhật ký, cũng có thể là mọi phiên bản của toàn bộ ứng dụng. Điều này có vẻ quá nặng nề với tôi, đặc biệt nếu bạn có nhiều "trường hợp nhỏ" của một số loại cụ thể.

Có cách nào để đặt nhật ký trong đối tượng của lớp bê tông thay vào đó, chỉ bằng cách sử dụng thừa kế? Nếu tôi phải khai báo một cách rõ ràng logger trong đối tượng của lớp, và đề cập rõ ràng nó từ lớp/đặc điểm, thì tôi đã viết gần như nhiều mã như thể tôi đã không sử dụng lại.

bày tỏ trong một đăng nhập phi bối cảnh cụ thể, vấn đề sẽ là:

Làm thế nào để tuyên bố trong một đặc điểm mà các lớp thực hiện phải có một đối tượng singleton loại X, và rằng đối tượng singleton này phải được truy cập thông qua phương pháp def x: X?

Tôi không thể chỉ định nghĩa phương thức trừu tượng, bởi vì chỉ có thể có một triển khai đơn lẻ trong lớp. Tôi muốn việc đăng nhập vào một lớp học siêu hạng đã giúp tôi trở thành một singleton siêu đẳng cấp, và việc đăng nhập vào lớp phụ sẽ giúp tôi trở thành một singleton lớp nhỏ. Hay đơn giản hơn, tôi muốn đăng nhập vào Scala để làm việc giống như việc ghi nhật ký truyền thống trong Java, sử dụng các trình ghi log tĩnh cụ thể cho lớp đang thực hiện việc ghi nhật ký. Kiến thức hiện tại của tôi về Scala nói với tôi rằng điều này đơn giản là không thể nếu không thực hiện nó giống như cách bạn làm trong Java, mà không có nhiều lợi ích nếu sử dụng Scala "tốt hơn".

Trả lời

6

Làm thế nào để tuyên bố trong một đặc điểm rằng lớp thực hiện phải có một đối tượng singleton loại X, và rằng đối tượng singleton này phải truy cập thông qua phương pháp def x: X?

Tuyên bố một đặc điểm phải được thực hiện bởi đối tượng đồng hành của bạn.

trait Meta[Base] { 
    val logger = LoggerFactory.getLogger(getClass) 
} 

Tạo đặc điểm cơ sở cho lớp học của bạn, lớp con phải ghi đè phương pháp meta.

trait Base { 
    def meta: Meta[Base] 
    def logger = meta.logger 
} 

Một lớp Dù với một đối tượng đồng:

object Whatever extends Meta[Base] 

class Whatever extends Base { 
    def meta = Whatever 

    def doSomething = { 
    logger.log("oops") 
    } 
} 

Bằng cách này, bạn chỉ cần có một tham chiếu đến đối tượng meta.

Chúng tôi có thể sử dụng lớp Dù như thế này.

object Sample { 
    def main(args: Array[String]) { 
    val whatever = new Whatever 
    whatever.doSomething 
    } 
} 
+0

Nếu class SomethingElse mở rộng class Dù sao, và ghi đè meta để trỏ tới singleton riêng của nó, thì đột nhiên doSomething sẽ đăng nhập vào singleton đó, và nếu SomethingElse không, thì đầu ra log của SomethingElse sẽ đi tới singleton của Any.Trong nhật ký Java tĩnh "truyền thống", đầu ra nhật ký bị ràng buộc tĩnh, vì vậy khi bạn nhìn thấy một thông báo, bạn biết trong đó lớp sẽ tìm kiếm nó. Không phải như vậy trong giải pháp của bạn. –

+0

Đó có phải là một vấn đề lớn? Khi tôi hiểu điều này, bạn có thể kế thừa hoặc ghi đè, cả hai đều hợp lý. doSomething không đột nhiên đăng nhập vào singleton riêng của nó, nó chỉ làm như vậy khi bạn đã yêu cầu nó một cách rõ ràng bằng cách ghi đè meta. –

0

Tôi không chắc chắn tôi hiểu câu hỏi của bạn hoàn toàn. Vì vậy, tôi xin lỗi lên phía trước nếu đây không phải là câu trả lời bạn đang tìm kiếm.

Xác định object là bạn đã đặt logger vào, sau đó tạo đồng hành trait.

object Loggable { 
    private val logger = "I'm a logger" 
} 

trait Loggable { 
    import Loggable._ 
    def debug(msg: String) { 
     println(logger + ": " + msg) 
    } 
} 

Vì vậy, bây giờ bạn có thể sử dụng nó như thế này:

scala> abstract class Abstraction 
scala> class Implementation extends Abstraction with Loggable 
scala> val test = new Implementation 
scala> test.debug("error message") 
I'm a logger: error message 

Điều này trả lời câu hỏi của bạn?

+0

Với giải pháp này, hoặc mỗi lớp bản ghi bằng cách sử dụng logger giống nhau, hoặc mỗi lớp có để xác định lại các phương thức debug() ... để sử dụng các logger khác nhau. –

0

Tôi nghĩ bạn không thể tự động nhận đối tượng đơn lẻ tương ứng của một lớp hoặc yêu cầu tồn tại một singleton như vậy.

Một lý do là bạn không thể biết loại singleton trước khi nó được xác định. Không chắc chắn, nếu điều này giúp hoặc nếu nó là giải pháp tốt nhất cho vấn đề của bạn, nhưng nếu bạn muốn yêu cầu một số đối tượng meta phải được xác định với một đặc điểm cụ thể, bạn có thể định nghĩa một cái gì đó như:

trait HasSingleton[Traits] { 
    def meta: Traits 
} 

trait Log { 
    def classname: String 
    def log { println(classname) } 
} 

trait Debug { 
    def debug { print("Debug") } 
} 

class A extends HasSingleton[Log] { 
    def meta = A // Needs to be defined with a Singleton (or any object which inherits from Log} 
    def f { 
    meta.log 
    } 
} 
object A extends Log { 
    def classname = "A" 
} 

class B extends HasSingleton[Log with Debug] { // we want to use Log and Debug here 
    def meta = B 
    def g { 
    meta.log 
    meta.debug 
    } 
} 
object B extends Log with Debug { 
    def classname = "B" 
} 

(new A).f 
// A 
(new B).g 
// B 
// Debug 
+0

Giải pháp của bạn gặp phải vấn đề tương tự như vấn đề của Stefan De Boey. –

+0

Hmm, nếu bạn muốn có nó bị ràng buộc tĩnh, bạn có lẽ nên viết nó tĩnh. – Debilski

11

Tối ưu hóa sớm là thư mục gốc của mọi điều ác

Hãy rõ ràng đầu tiên về một điều: nếu đặc điểm của bạn trông giống như sau:

trait Logger { lazy val log = Logger.getLogger } 

sau đó, những gì bạn có không thực hiện như sau:

  • Bạn có KHÔNG tạo một trường hợp logger mỗi thể hiện của loại hình
  • Bạn đã không cho mình một bộ nhớ cũng không phải là vấn đề hiệu suất (trừ khi bạn có)

gì bạn done như sau:

  • Bạn có thêm tham chiếu trong mỗi insta nce kiểu của bạn
  • Khi bạn truy cập vào logger cho lần đầu tiên, bạn có lẽ làm một số tra cứu bản đồ

Lưu ý rằng, ngay cả khi bạn đã tạo ra một logger riêng biệt cho mỗi thể hiện của loại hình (mà tôi thường xuyên làm, ngay cả khi chương trình của tôi chứa hàng trăm nghìn điều này, để tôi có quyền kiểm soát chặt chẽ đối với đăng nhập của mình), bạn gần như chắc chắn vẫn còn sẽ không có hiệu suất cũng như vấn đề về bộ nhớ!


Một "giải pháp" là (tất nhiên), để làm cho các đối tượng đồng hành thực hiện các giao diện logger:

object MyType extends Logger 

class MyType { 
    import MyType._ 
    log.info("Yay") 
} 
+0

Tôi đã không nói rằng mỗi trường hợp có được đối tượng logger riêng của nó, bởi vì không có khung khai thác gỗ mà tôi từng nghe nói về điều đó. Vì chúng thường được khởi tạo tĩnh, chúng chấp nhận chi phí cao hơn một chút khi sử dụng một số loại bản đồ bên trong nhà máy, đó là lý do tại sao không có ai "logger mới". Tôi đã nói tất cả về * tham chiếu *. Câu "val lười biếng không liên quan trong mọi trường hợp" chỉ đơn giản có nghĩa là "tham chiếu cuối cùng lười biếng không liên quan trong mọi trường hợp". –

+1

Tôi không nói rằng bạn đã nói điều đó. Nhưng mối quan tâm của bạn cho chất thải tôi cảm thấy đã đảm bảo làm rõ như những gì chi phí này được. Đó là: tối thiểu. Hầu như không tồn tại trên thực tế. Đối với một triệu đối tượng, chúng tôi đang nói 8-16Mb không gian thêm. –

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