Sto cercando di trovare un'alternativa più pulita (che sia idiomatica per Scala) al tipo di cosa che si vede con il data-binding in WPF/silverlight data-binding - cioè, implementando INotifyPropertyChanged. Per prima cosa, alcuni sottofondi:proprietà idiomatiche ha cambiato la notifica in scala?
Nelle applicazioni .Net WPF o silverlight, si ha il concetto di associazione dati bidirezionale (ovvero, vincolare il valore di qualche elemento dell'interfaccia utente a una proprietà .net del DataContext in un modo in cui le modifiche all'elemento dell'interfaccia utente influiscono sulla proprietà e viceversa. Un modo per abilitare questo è implementare l'interfaccia INotifyPropertyChanged nel DataContext. Sfortunatamente, questo introduce molto codice di codice per qualsiasi proprietà aggiunta a "ModelView" . "tipo Ecco come potrebbe apparire a 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
}
}
}
Per motivi di spazio, supponiamo il tipo INotifyPropertyChanged è un tratto che gestisce un elenco di callback di tipo (AnyRef, String) => Unità e OnPropertyChanged è un metodo che richiama tutte quelle richiamate, passando "questo" come AnyRef e la stringa passata). Questo sarebbe solo un evento in C#.
Si può immediatamente vedere il problema: questo è un sacco di codice boilerplate per solo due proprietà. Ho sempre voluto scrivere qualcosa di simile a questo, invece:
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
}
}
}
So che posso facilmente scrivere in questo modo, se ObservableProperty [T] ha valore/VALUE_ = metodi (questo è il metodo che sto utilizzando ora):
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))
}
}
}
Ma esiste un modo per implementare la prima versione con impliciti o qualche altra caratteristica/linguaggio di Scala per far funzionare le istanze ObservableProperty come se fossero regolari "proprietà" a Scala, senza bisogno di chiamare i metodi di valore? L'unica altra cosa che posso pensare è qualcosa di simile, che è più dettagliato di uno di queste due versioni, ma è ancora meno prolissa rispetto all'originale:
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
}
}
}
Questo documento potrebbe interessarti: http://www.ganguin.net/frp2d.pdf –
Vedere anche http: // stackoverflow.it/questions/1054179/functional-reactive-programming-in-scala Nessun successo finora. Sfortunatamente, questa è stata una decisione di design scarsa in Scala. – thSoft