2010-05-26 19 views
11

Tôi đang cố gắng tìm một giải pháp thay thế sạch hơn (đó là thành ngữ đối với Scala) với loại điều bạn thấy với ràng buộc dữ liệu trong ràng buộc dữ liệu WPF/silverlight - tức là, triển khai INotifyPropertyChanged. Đầu tiên, một số nền:tài sản thành ngữ đã thay đổi thông báo trong scala?

Trong ứng dụng .Net WPF hoặc Silverlight, bạn có khái niệm liên kết dữ liệu hai chiều (có nghĩa là, ràng buộc giá trị của một phần tử của giao diện người dùng với thuộc tính .net của DataContext trong một cách để thay đổi thành phần UI ảnh hưởng đến thuộc tính và ngược lại Một cách để kích hoạt tính năng này là thực hiện giao diện INotifyPropertyChanged trong DataContext của bạn. . "loại đây là cách nó có thể tìm trong Scala:

trait IDrawable extends INotifyPropertyChanged 
{  
     protected var drawOrder : Int = 0 
     def DrawOrder : Int = drawOrder 
     def DrawOrder_=(value : Int) { 
      if(drawOrder != value) { 
        drawOrder = value 
        OnPropertyChanged("DrawOrder") 
      } 
     } 

     protected var visible : Boolean = true 
     def Visible : Boolean = visible 
     def Visible_=(value: Boolean) = { 
      if(visible != value) { 
        visible = value 
        OnPropertyChanged("Visible") 
      } 
     } 
     def Mutate() : Unit = { 
      if(Visible) { 
       DrawOrder += 1 // Should trigger the PropertyChanged "Event" of INotifyPropertyChanged trait 
      } 
     } 
} 

Vì lợi ích của vũ trụ, chúng ta hãy giả định loại INotifyPropertyChanged là một đặc điểm quản lý một danh sách các callbacks loại (AnyRef, string) => Đơn vị, và OnPropertyChanged là một phương thức gọi tất cả các callbacks đó, truyền "this" như AnyRef và String được truyền vào). Đây chỉ là một sự kiện trong C#.

Bạn có thể thấy ngay vấn đề: đó là một tấn mã soạn sẵn cho hai thuộc tính. Tôi đã luôn muốn viết một cái gì đó như thế này thay vì:

trait IDrawable 
{ 
     val Visible = new ObservableProperty[Boolean]('Visible, true) 
     val DrawOrder = new ObservableProperty[Int]('DrawOrder, 0) 
     def Mutate() : Unit = { 
      if(Visible) { 
       DrawOrder += 1 // Should trigger the PropertyChanged "Event" of ObservableProperty class 
      } 
     } 
} 

tôi biết rằng tôi có thể dễ dàng viết nó như thế này, nếu ObservableProperty [T] có giá trị gia tăng/Value_ = phương pháp (đây là phương pháp tôi đang sử dụng bây giờ):

trait IDrawable { 
     // on a side note, is there some way to get a Symbol representing the Visible field 
     // on the following line, instead of hard-coding it in the ObservableProperty 
     // constructor? 
     val Visible = new ObservableProperty[Boolean]('Visible, true) 
     val DrawOrder = new ObservableProperty[Int]('DrawOrder, 0) 
     def Mutate() : Unit = { 
      if(Visible.Value) { 
       DrawOrder.Value += 1 
      } 
     } 
} 

// given this implementation of ObservableProperty[T] in my library 
// note: IEvent, Event, and EventArgs are classes in my library for 
// handling lists of callbacks - they work similarly to events in C# 
class PropertyChangedEventArgs(val PropertyName: Symbol) extends EventArgs("") 
class ObservableProperty[T](val PropertyName: Symbol, private var value: T) { 
    protected val propertyChanged = new Event[PropertyChangedEventArgs] 
    def PropertyChanged: IEvent[PropertyChangedEventArgs] = propertyChanged 
    def Value = value; 
    def Value_=(value: T) { 
     if(this.value != value) { 
      this.value = value 
      propertyChanged(this, new PropertyChangedEventArgs(PropertyName)) 
     } 
    } 
} 

Nhưng có cách nào để triển khai phiên bản đầu tiên sử dụng implicits hoặc một số tính năng khác/thành ngữ của Scala để làm cho trường hợp ObservableProperty chức năng như thể chúng là thường xuyên "tài sản" trong scala, mà không cần phải gọi các phương pháp giá trị? Chỉ khác điều tôi có thể nghĩ đến là một cái gì đó như thế này, đó là nhiều tiết hơn một trong hai phiên bản trên, nhưng vẫn còn ít tiết so với bản gốc:

trait IDrawable {  
    private val visible = new ObservableProperty[Boolean]('Visible, false) 
    def Visible = visible.Value 
    def Visible_=(value: Boolean): Unit = { visible.Value = value } 

    private val drawOrder = new ObservableProperty[Int]('DrawOrder, 0) 
    def DrawOrder = drawOrder.Value 
    def DrawOrder_=(value: Int): Unit = { drawOrder.Value = value } 

    def Mutate() : Unit = { 
    if(Visible) { 
     DrawOrder += 1 
    } 
    } 
} 
+3

Bài báo này có thể bạn quan tâm: http://www.ganguin.net/frp2d.pdf –

+0

Xem thêm http: // stackoverflow.com/questions/1054179/functional-reactive-programming-in-scala Không thành công cho đến nay. Thật không may, đây là một quyết định thiết kế tồi tệ ở Scala. – thSoft

Trả lời

2

tôi không thể khẳng định rằng đây là một khung thay đổi sở hữu kinh điển trong Scala, nhưng tôi đã sử dụng một lớp học như thế này trước đây:

abstract class Notifier[T,U](t0: T) { 
    import java.util.concurrent.atomic.AtomicReference 
    import scala.actors.OutputChannel 
    type OCUT = OutputChannel[(U,AtomicReference[T])] 
    val data = new AtomicReference[T](t0) 
    def id: U 
    protected var callbacks = Nil:List[T => Unit] 
    protected var listeners = Nil:List[OCUT] 
    def apply() = data.get 
    def update(t: T) { 
    val told = data.getAndSet(t) 
    if (t != told) { 
     callbacks.foreach(_(t)) 
     listeners.foreach(_ ! (id,data)) 
    } 
    } 
    def attend(f: T=>Unit) { callbacks ::= f } 
    def attend(oc: OCUT) { listeners ::= oc } 
    def ignore(f: T=>Unit) { callbacks = callbacks.filter(_ != f) } 
    def ignore(oc: OCUT) { listeners = listeners.filter(_ != oc) } 
} 

Động lực để tạo lớp này là tôi muốn có một cách thread-safe linh hoạt để phản ứng với những thay đổi, mà điều này cung cấp (vì nó cung cấp cả gọi lại và có thể đẩy tin nhắn đến các diễn viên). Có vẻ như với tôi - trừ khi tôi hiểu lầm chính xác những gì bạn muốn bởi vì tôi đã không có dịp để tìm hiểu các công cụ WPF/Silverlight - điều này có thể thực hiện mọi thứ bạn muốn và hơn thế nữa.

Ví dụ,

class IDrawable extends SomethingWithOnPropertyChanged { 
    val drawOrder = new Notifier[Int,Symbol](0) { def id = 'DrawOrder } 
    val visible = new Notifier[Boolean,Symbol](false) { def id = 'Visible } 
    drawOrder.attend((i:Int) => OnPropertyChanged(drawOrder.id)) 
    def mutate { 
    if (visible()) drawOrder() += 1 
    } 
} 

nên tương đương với những gì bạn muốn. (Một lần nữa, tôi không chắc chắn bạn muốn điều này như thế nào; bạn có thể tạo một tập hợp biểu tượng -> ánh xạ thông báo mà bạn sẽ tìm kiếm bằng phương pháp áp dụng để mục tiêu có thời gian làm việc dễ dàng hơn khi nó được biểu tượng DrawOrder.)

Sự khác biệt đáng kể duy nhất từ ​​cách sử dụng của bạn là Trình thông báo sử dụng các phương pháp áp dụng/cập nhật để lưu bản mẫu; bạn không phải viết def x và def x_ = methods mỗi lần, nhưng bạn phải sử dụng() để truy cập.

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