JavaFX学习笔记(02)(DSL构建)

为了能够更深入的了解JavaFX类库,我将会用Scala实现一个JavaFX的DSL。又开始挖大坑了,希望这次能够填完

这个DSL会是什么样呢?如果你对C++略有涉猎,那么肯定对C++中流插入和流提取不陌生。我们将要实现的这个DSL,就会围绕着<<插入符展开。


首先,我们面临的第一个问题,就是使用Scala的类继承Application类会报错(听说用单例对象继承就可以了,不过当时我没有想到这一点)。于是我写了自己的一个SApplication类作为所有Scala所写的JavaFX应用的基类:

package sjavafx.application

import javafx.application.Application

/**
  * Created by Administrator on 2016/10/3.
  */
abstract class SApplication extends Application {
    def run = Application.launch()
    def run(args: Array[String]) = Application.launch(args: _*)
    def run(args: String*) = Application.launch(args: _*)
    def run(appClass: Class[_ <: Application], args: String*) = Application.launch(appClass, args: _*)
}

这个类只是Application类在Scala中简单的封装,这个类的子类将可以直接成为应用程序类而不需要再去写main()方法。

为了便于在构建DSL过程中进行调试,我从网络上(JavaFXChina)找到了一个简单的例子,然后拿Scala进行了小幅度的改写:

import sjavafx.application.SApplication
import javafx.event.ActionEvent
import javafx.event.EventHandler
import javafx.scene.Scene
import javafx.scene.control.Button
import javafx.scene.layout.StackPane
import javafx.stage.Stage

class Text extends SApplication {
    def start(primaryStage:Stage) {
        val btn = new Button()
        btn.setText("Say 'Hello World'")
        btn.setOnAction(new EventHandler[ActionEvent]() {
            def handle(event:ActionEvent) {
                println("Hello World!")
            }
        })
        val root = new StackPane()
        root.getChildren().add(btn)
        val scene = new Scene(root, 300, 250)

        primaryStage.setTitle("Hello World!")
        primaryStage.setScene(scene)
        primaryStage.show()
    }
}

现在我们就要先围绕着这个测试类来对JavaFX进行包装。

object SApplication {

}

首先我们在定义了一个SApplication类的伴生对象,里面将要存放一些十分重要的隐式转换等核心功能。
现在我们定义一个特质:Insertionable:
JavaFX学习笔记(02)(DSL构建)_第1张图片
这个特质将是整个类库里最核心的部分之一。

object SApplication {
    val VarPool = new mutable.HashMap[String, Insertionable]

    object Name

}

然后我们在这个基础上就可以进行一定的修改了:

object SApplication {
    val VarPool = new mutable.HashMap[String, Insertionable]

    /**
      * 用法:ins :Insertionable << Type << theType
      * 描述:转换Insertionable类型变量为theType所描述类型.
      */
    object Type
    object tSButton/*sjavafx.scene.control.SButton*/

    /**
      * 用法:ins :Insertionable << Name << name:String
      * 描述:设定变量名.
      */
    object Name

}
trait Insertionable {
    def <<(name: Name.type) = new Ins {
        def <<(name: String) = {
            VarPool(name) = ins
            ins
        }
    }

    def <<(ty: Type.type) = new Ins {
        def <<(sbutton: tSButton.type) = ins match{
            case sb :SButton => sb
            case _ => throw new ClassCastException
        }
    }

    class Ins(val ins: Insertionable = this)

}

然后我们加入了新的类SButton,这个类是Button类的子类,并实现了Insertionable接口:

class SButton() extends Button() with Insertionable {
    def this(str: String) = {
        this()
        setText(str)
    }
}

现在我们的测试类已经可以加入<<运算符了!虽然暂时还没有做什么实质性的事情,但是这也证明了我们的尝试是成功的:

import sjavafx.application.SApplication
import javafx.event.ActionEvent
import javafx.event.EventHandler
import javafx.scene.Scene
import javafx.scene.layout.StackPane
import javafx.stage.Stage

import sjavafx.application.SApplication._
import sjavafx.scene.control.SButton


class Text extends SApplication {
    def start(primaryStage:Stage) {
        val btn = new SButton <<
                Name << "btn" <<
                Type << tSButton


        btn.setText("Say 'Hello World'")
        btn.setOnAction(new EventHandler[ActionEvent]() {
            def handle(event:ActionEvent) {
                println("Hello World!")
            }
        })
        val root = new StackPane()
        root.getChildren.add(btn)
        val scene = new Scene(root, 300, 250)

        primaryStage.setTitle("Hello World!")
        primaryStage.setScene(scene)
        primaryStage.show
    }

}

看样子我们的程序还是很成功的:JavaFX学习笔记(02)(DSL构建)_第2张图片

你可能感兴趣的:(Java,JavaFX,Scala,DSL)