《Kotin 极简教程》第14章 使用 Kotlin DSL

第14章 使用 Kotlin DSL

最新上架!!!《 Kotlin极简教程》 陈光剑 (机械工业出版社)
可直接打开京东,淘宝,当当===> 搜索: Kotlin 极简教程
http://www.jianshu.com/p/35b487734339

我们在前面的章节中,已经看到了 Kotlin DSL 的强大功能。例如Gradle 的配置文件 build.gradle (Groovy),以及前面我们涉及到的Gradle Script Kotlin(Kotlin)、Anko(Kotlin)等,都是 DSL。我们可以看出,使用DSL的编程风格,可以让程序更加简单干净、直观简洁。当然,我们也可以创建自己的 DSL。

本章就让我们一起来学习一下 Kotlin中 DSL的相关内容。

我们在上一章中已经看到了在 Android 中使用下面这样的 嵌套DSL 风格的代码来替代 XML 式风格的视图文件

        UI {
            // AnkoContext

            verticalLayout {
                padding = dip(30)
                var title = editText {
                    // editText 视图
                    id = R.id.todo_title
                    hintResource = R.string.title_hint
                }

                var content = editText {
                    id = R.id.todo_content
                    height = 400
                    hintResource = R.string.content_hint
                }
                button {
                    // button 视图
                    id = R.id.todo_add
                    textResource = R.string.add_todo
                    textColor = Color.WHITE
                    setBackgroundColor(Color.DKGRAY)
                    onClick { _ -> createTodoFrom(title, content) }
                }
            }
        }

相比 XML 风格的 DSL(XML 本质上讲也是一种 DSL),明显使用原生的编程语言(例如Kotlin)DSL 风格更加简单干净,也更加自由灵活。

Kotlin DSL 的编程风格是怎样的呢?以及其背后实现的原理是怎样的呢?下面就让我一起来探讨一下。

DSL 是什么

DSL(Domain-Specific Language,领域特定语言)指的是专注于特定问题领域的计算机语言(领域专用语言)。不同于通用的计算机语言(GPL),领域特定语言只用在某些特定的领域。

“领域”可能特指某一产业,比如保险、教育、航空、医疗等;它也可能是指一种方法或者技术,比如JavaEE、.NET、数据库、服务、消息、架构以及领域驱动设计。开发DSL语言的目的在于,我们要以一种更优雅、更简洁的方式面对领域中的一些列挑战。之所以能够这样,是因为这个语言刚好够用于解决领域中唯一存在的一系列挑战,一点儿不多、也一点儿不少,刚刚好。

比如用来显示网页的HTML语言。更加典型的例子是Gradle,它基于Ant 和 Maven,使用基于Groovy的DSL 来声明项目构建配置 build.gradle,而不是传统的XML。

DSL 简单讲就是对一个特定问题 (受限的表达能力) 的方案模型的更高层次的抽象表达(领域语言),使其更加简单易懂 (容易理解的语义以及清晰的语义模型)。

DSL 只是问题解决方案模型的外部封装,这个模型可能是一个 API 库,也可能是一个完整的框架等等。DSL 提供了思考特定领域问题的模型语言,这使得我们可以更加简单高效地来解决问题。DSL 聚焦一个特定的领域,简单易懂,功能极简但完备。DSL 让我们理解和使用模型更加简易。

DSL 有内部 DSL 跟外部 DSL 之分。例如 Gradle、Anko 等都是我们使用通用编程语言(Java 和 Kotlin)创建的内部DSL。

内部DSL

内部DSL是指与项目中使用的通用目的编程语言(Java、C#或Ruby)紧密相关的一类DSL。它基于通用编程语言实现。

例如,Rails框架被称为基于Ruby的DSL,用于管理Ruby开发的Web应用程序。Rails之所以被称为DSL,原因之一在于Rails应用了一些Ruby语言的特性,使得基于Rails编程看上去与基于通用目的的Ruby语言编程并不相同。

根据Martin Fowler和Eric Evans的观点,框架或者程序库的API是否满足内部DSL的关键特征之一就是它是否有一个流畅(fluent)的接口。这样,你就能够用短小的对象表达式去组织一个原本很长的表达式,使它读起来更加自然。

外部DSL

外部DSL跟通用编程语言(GPL)类似,但是外部DSL更加专注于特定领域。

创建外部DSL和创建一种通用的编程语言的过程是相似的,它可以是编译型或者解释型的。它具有形式化的文法,只允许使用良好定义的关键字和表达式类型。经过编译的DSL通常不会直接产生可执行的程序(但是它确实可以)。

大多数情况下,外部DSL可以转换为一种与核心应用程序的操作环境相兼容的资源,也可以转换为用于构建核心应用的通用目的编程语言。例如,Hibernate中使用的对象-关系映射文件,就是由外部DSL转换为资源的实例。

提示:关于 DSL 的详细介绍可以参考:《领域特定语言》(Martin Fowler)这本书。

Kotlin 的 DSL 特性支持

许多现代语言为创建内部 DSL 提供了一些先进的方法, Kotlin 也不例外。

在Kotlin 中创建 DSL , 一般主要使用下面两个特性:

  • 扩展函数、扩展属性
  • 带接收者的 Lambda 表达式(高阶函数)

例如上面的示例的 UI {...} 的代码,我们举例简单说明如下

函数名 函数签名 备注说明
UI fun Fragment.UI(init: AnkoContext.() -> Unit):AnkoContext android.support.v4.app.Fragment的扩展函数; 入参 init 是一个带接收者的函数字面值, 我们直接传入的是一个 Lambda 表达式
verticalLayout inline fun ViewManager.verticalLayout(init: _LinearLayout.() -> Unit): LinearLayout android.view.ViewManager的扩展函数

使用kotlinx.html DSL 写前端代码

为了加深对 Kotlin DSL 实用性上的理解,我们本节再介绍一个 Kotlin 中关于
HTML 的 DSL: kotlinx.html 。

kotlinx.html 是可在 Web 应用程序中用于构建 HTML 的 DSL。 它可以作为传统模板系统(例如JSP、FreeMarker等)的替代品。

kotlinx. html 分别提供了kotlinx-html-jvm 和 kotlinx-html-js库的DSL , 用于在 JVM 和浏览器 (或其他 javascript 引擎) 中直接使用 Kotlin 代码来构建 html, 直接解放了原有的 HTML 标签式的前端代码。这样,我们 也可以使用 Kotlin来先传统意义上的 HTML 页面了。 Kotlin Web 编程将会更加简单纯净。

提示: 更多关于kotlinx.html的相关内容可以参考它的 Github 地址 :https://github.com/Kotlin/kotlinx.html

要使用 kotlinx.html 首先添加依赖

dependencies {
    def kotlinx_html_version = "0.6.3"
    compile "org.jetbrains.kotlinx:kotlinx-html-jvm:${kotlinx_html_version}"
    compile "org.jetbrains.kotlinx:kotlinx-html-js:${kotlinx_html_version}"
    ...
}

kotlinx.html 最新版本发布在 https://jcenter.bintray.com/ 仓库上,所以我们添加一下仓库的配置

repositories {
    maven { url 'https://jitpack.io' }
    mavenCentral()
    jcenter() // https://jcenter.bintray.com/ 仓库
    maven { url "https://repo.spring.io/snapshot" }
    maven { url "https://repo.spring.io/milestone" }
}

我们来写一个极简百度首页示例。这个页面的前端 HTML 代码如下:

<!DOCTYPE html>
<html lang=zh-CN>
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name=viewport content="width=device-width,initial-scale=1">
    <title>百度一下</title>
    <link href="https://cdn.bootcss.com/bootstrap/4.0.0-alpha.6/css/bootstrap.min.css" rel="stylesheet">
    <script src="https://cdn.bootcss.com/jquery/3.2.1/jquery.min.js"></script>
    <link href="dsl.css" rel="stylesheet">
    <script src="dsl.js"></script>
</head>
<body>
<div class="container">
    <div class="ipad center">

    </div>

    <form class="form">
        <input id="wd" class="form-control ipad">
        <button id="baiduBtn" type="submit" class="btn btn-primary form-control ipad">百度一下</button>
    </form>
</div>
</body>
</html>

其中,dsl.css文件内容如下

.ipad {
    margin: 10px
}

.center {
    text-align: center;
}

dsl.js 文件内容如下

$(function () {
    $('#baiduBtn').on('click', function () {
        var wd = $('#wd').val()
        window.open("https://www.baidu.com/s?wd=" + wd)
    })
})

上面我们是通常使用的 HTML+JS+CSS 的方式来写前端页面的方法。现在我们把 HTML 部分的代码用Kotlin 的 DSL kotlinx.html 来重新实现一遍。

我们首先新建 Kotlin + Spring Boot 工程,然后直接来写 Kotlin 视图类HelloDSLView,代码如下:

package com.easy.kotlin.chapter14_kotlin_dsl.view

import kotlinx.html.*
import kotlinx.html.stream.createHTML
import org.springframework.stereotype.Service

/**
 * Created by jack on 2017/7/22.
 */
@Service
class HelloDSLView {
    fun html(): String {
        return createHTML().html {
            head {
                meta {
                    charset = "utf-8"
                    httpEquiv = "X-UA-Compatible"
                    content = "IE=edge"
                }
                title("百度一下")
                link {
                    href = "https://cdn.bootcss.com/bootstrap/4.0.0-alpha.6/css/bootstrap.min.css"
                    rel = "stylesheet"
                }
                script {
                    src = "https://cdn.bootcss.com/jquery/3.2.1/jquery.min.js"
                }
                link {
                    href = "dsl.css"
                    rel = "stylesheet"
                }
                script {
                    src = "dsl.js"
                }
            }

            body {
                div(classes = "container") {
                    div(classes = "ipad center") {
                        img {
                            src = "https://www.baidu.com/img/bd_logo1.png"
                            width = "270"
                            height = "129"
                        }
                    }

                    form(classes = "form") {
                        input(InputType.text, classes = "form-control ipad") {
                            id = "wd"
                        }
                        button(classes = "btn btn-primary form-control ipad") {
                            id = "baiduBtn"
                            type = ButtonType.submit
                            text("百度一下")

                        }
                    }

                }
            }
        }
    }
}

相比之下,我们使用 DSL 的风格要比原生 HTML 要简洁优雅。关键是,我们的这个 HTML 是用 Kotlin 写的,这也就意味着,我们的 HTML 代码不再是简单的静态的前端代码了。我们完全可以直接使用后端的接口返回数据来给 HTML 元素赋值,我们也完全具备了(当然是完全超越了)诸如 JSP、Freemarker 这样的视图模板引擎的各种判断、循环等的语法功能,因为我们直接使用的是一门强大的编程语言 Kotlin 来写的 HTML 代码 。

然后,我们就可以直接在控制器层的代码里直接调用我们的 Kotlin 视图代码了:

@Controller
class HelloDSLController {
    @Autowired
    var helloDSLView: HelloDSLView? = null

    @GetMapping("hello")
    fun helloDSL(model: Model): ModelAndView {
        model.addAttribute("hello", helloDSLView?.html())
        return ModelAndView("hello")
    }
}

为了简单起见,我们借用一下 Freemarker 来做视图解析引擎,但是它只负责原封不动地来传输我们的 Kotlin 视图代码。hello.ftl 代码如下:

${hello}

我们的源码目录如下

── src
    ├── main
    │   ├── java
    │   ├── kotlin
    │   │   └── com
    │   │       └── easy
    │   │           └── kotlin
    │   │               └── chapter14_kotlin_dsl
    │   │                   ├── Chapter14KotlinDslApplication.kt
    │   │                   ├── controller
    │   │                   │   └── HelloDSLController.kt
    │   │                   └── view
    │   │                       └── HelloDSLView.kt
    │   └── resources
    │       ├── application.properties
    │       ├── banner.txt
    │       ├── static
    │       │   ├── dsl.css
    │       │   ├── dsl.js
    │       │   └── hello.html
    │       └── templates
    │           └── hello.ftl
    └── test
        ├── java
        ├── kotlin
        │   └── com
        │       └── easy
        │           └── kotlin
        │               └── chapter14_kotlin_dsl
        │                   └── Chapter14KotlinDslApplicationTests.kt
        └── resources

然后,启动运行 SpringBoot 应用,浏览器访问 http://127.0.0.1:8888/hello , 我们可以看到如下输出界面:

这就是 DSL 的精妙之处。我们后面可以尝试使用 kotlinx.html 来写Kotlin 语言的前端代码了。在做 Web 开发的时候,我们通常是使用 HTML + 模板引擎(Velocity、JSP、Freemarker 等)来集成前后端的代码,这让我们有时候感到很尴尬,要学习模板引擎的语法,还得应对 前端HTML代码中凌乱的模板引擎标签、变量等片段代码。

使用 Kotlin DSL 来写 HTML 代码的情况将完全不一样了,我们将重拾前后端集成编码的乐趣(不再是模板引擎套前端 HTML,各种奇怪的 #、<#>、${} 模板语言标签),我们直接把 更加优雅简单的 DSL 风格的HTML 代码搬到了后端,同时HTML中的元素将直接跟后端的数据无缝交互,而完成这些的只是 Kotlin(当然,相应领域的 DSL 基本语义模型还是要学习一下)。

提示:本节项目源码: https://github.com/EasyKotlin/chapter14_kotlin_dsl

实现一个极简的http DSL

我们现在已经基本知道 Kotlin 中 DSL 的样子了。但是这些 DSL 都是怎样实现的呢?本节我们就通过实现一个极简的http DSL来学习创建 DSL 背后的基本原理。

在这里我们对 OkHttp 做一下简单的封装,实现一个类似 jquery 中的 Ajax 的 http 请求的DSL。

OkHttp 是一个成熟且强大的网络库,在Android源码中已经使用OkHttp替代原先的HttpURLConnection。很多著名的框架例如Picasso、Retrofit也使用OkHttp作为底层框架。

提示: 更多关于OkHttp 的使用可参考: http://square.github.io/okhttp/

创建 Kotlin Gradle 项目

我们首先使用 IDEA 创建 Kotlin Gradle 项目

然后,在 build.gradle 里面配置依赖

    compile 'com.github.ReactiveX:RxKotlin:2.1.0'
    compile group: 'com.squareup.okhttp3', name: 'okhttp', version: '3.8.1'
    compile group: 'com.alibaba', name: 'fastjson', version: '1.2.35'

其中,RxKotlin是ReactiveX 框架对 Kotlin 语言的支持库。我们这里主要用RxKotlin来进行请求回调的异步处理。

我们使用的是 'com.github.ReactiveX:RxKotlin:2.1.0' , 这个库是在 https://jitpack.io 上,所以我们在repositories配置里添加 jitpack 仓库

repositories {
    maven { url 'https://jitpack.io' }
    ...
}

RxKotlin

ReactiveX是Reactive Extensions的缩写,一般简写为Rx,最初是LINQ的一个扩展,由微软的架构师Erik Meijer领导的团队开发,在2012年11月开源。

Rx扩展了观察者模式用于支持数据和事件序列。Rx是一个编程模型,目标是提供一致的编程接口,帮助开发者更方便的处理异步I/O(非阻塞)数据流。

Rx库支持.NET、JavaScript和C++ 。Rx近几年越来越流行,现在已经支持几乎全部的流行编程语言了。一个语言列表如下所示:

Rx 支持的编程语言 项目主页
Java RxJavahttps://github.com/ReactiveX/RxJava
JavaScript  RxJShttps://github.com/ReactiveX/rxjs
C#  Rx.NEThttps://github.com/Reactive-Extensions/Rx.NET
C#(Unity)  UniRxhttps://github.com/neuecc/UniRx
Scala  RxScalahttps://github.com/ReactiveX/RxScala
Clojure  RxClojurehttps://github.com/ReactiveX/RxClojure
C++  RxCpphttps://github.com/Reactive-Extensions/RxCpp
Lua  RxLuahttps://github.com/bjornbytes/RxLua
Ruby  Rx.rbhttps://github.com/Reactive-Extensions/Rx.rb
Python: RxPYhttps://github.com/ReactiveX/RxPY
Go  RxGohttps://github.com/ReactiveX/RxGo
Groovy  RxGroovyhttps://github.com/ReactiveX/RxGroovy
JRuby  RxJRubyhttps://github.com/ReactiveX/RxJRuby
Kotlin  RxKotlinhttps://github.com/ReactiveX/RxKotlin
Swift  RxSwifthttps://github.com/kzaher/RxSwift
PHP  RxPHPhttps://github.com/ReactiveX/RxPHP
Elixir  reaxivehttps://github.com/alfert/reaxive
Dart  RxDarthttps://github.com/ReactiveX/rxdart

Rx的大部分语言库由ReactiveX这个组织负责维护。Rx 比较流行的库有RxJava/RxJS/Rx.NET等,当然未来RxKotlin也必将更加流行。

提示: Rx 的社区网站是: http://reactivex.io/ 。 Github 地址:https://github.com/ReactiveX/

Http请求对象封装类

首先我们设计Http请求对象封装类如下

class HttpRequestWrapper {

    var url: String? = null

    var method: String? = null

    var body: RequestBody? = null

    var timeout: Long = 10

    internal var success: (String) -> Unit = {}
    internal var fail: (Throwable) -> Unit = {}

    fun success(onSuccess: (String) -> Unit) {
        success = onSuccess
    }

    fun error(onError: (Throwable) -> Unit) {
        fail = onError
    }
}

HttpRequestWrapper的成员变量和函数说明如下表

成员 说明
url 请求 url
method 请求方法,例如 Get、Post 等,不区分大小写
body 请求头,为了简单起见我们直接使用 OkHttp的RequestBody类型
timeout 超时时间ms,我们设置了默认值是10s
success 请求成功的函数变量
fail 请求失败的函数变量
fun success(onSuccess: (String) -> Unit) 请求成功回调函数
fun error(onError: (Throwable) -> Unit) 请求失败回调函数

http 执行引擎

我们直接调用 OkHttp 的 Http 请求 API

private fun call(wrap: HttpRequestWrapper): Response {

    var req: Request? = null
    when (wrap.method?.toLowerCase()) {
        "get" -> req = Request.Builder().url(wrap.url).build()
        "post" -> req = Request.Builder().url(wrap.url).post(wrap.body).build()
        "put" -> req = Request.Builder().url(wrap.url).put(wrap.body).build()
        "delete" -> req = Request.Builder().url(wrap.url).delete(wrap.body).build()
    }

    val http = OkHttpClient.Builder().connectTimeout(wrap.timeout, TimeUnit.MILLISECONDS).build()
    val resp = http.newCall(req).execute()
    return resp
}

它返回请求的响应对象Response。

我们在OkHttpClient.Builder().connectTimeout(wrap.timeout, TimeUnit.MILLISECONDS).build()中设置超时时间的单位是 TimeUnit.MILLISECONDS

我们通过wrap.method?.toLowerCase()处理请求方法的大小写的兼容。

使用 RxKotlin 完成请求响应的异步处理

我们首先新建一个数据发射源:一个可观察对象(Observable),作为发射数据用

    val sender = Observable.create<Response>({
        e ->
        e.onNext(call(wrap))
    })

其中,e 的类型是 io.reactivex.Emitter (发射器),它的接口定义是

public interface Emitter<T> {
    void onNext(@NonNull T value);
    void onError(@NonNull Throwable error);
    void onComplete();
}

其方法功能简单说明如下:

方法 功能
onNext 发射一个正常值数据(value)
onError 发射一个Throwable异常
onComplete 发射一个完成的信号

这里,我们通过调用onNext方法,把 OkHttp 请求之后的响应对象Response 作为正常值发射出去。

然后我们再创建一个数据接收源:一个观察者(Observer)

    val receiver: Observer<Response> = object : Observer<Response> {
        override fun onNext(resp: Response) {
            wrap.success(resp.body()!!.string())
        }

        override fun onError(e: Throwable) {
            wrap.fail(e)
        }

        override fun onSubscribe(d: Disposable) {
        }

        override fun onComplete() {
        }

    }

receiver 的 onNext 函数接收 sender 发射过来的数据 Response, 然后我们在函数体内,调用这个响应对象,给 wrap.success 回调函数进行相关的赋值操作。同样的,onError 函数中也执行相应的赋值操作。

最后,通过 subscribe 订阅函数来绑定 sender 与 receiver 的关联:

sender.subscribe(receiver)

作为接收数据的 receiver (也就是 观察者 (Observer) ),对发送数据的 sender (也就是可被观察对象( Observable)) 所发射的数据或数据序列作出响应。

这种模式可以极大地简化并发操作,因为它创建了一个处于待命状态的观察者,在未来某个时刻响应 sender 的通知,而不需要阻塞等待 sender 发射数据。这个很像协程中的通道编程模型。

DSL主函数 ajax

我们的ajax DSL主函数设计如下:

fun ajax(init: HttpRequestWrapper.() -> Unit) {
    val wrap = HttpRequestWrapper()
    wrap.init()
    doCall(wrap)
}

其中,参数init: HttpRequestWrapper.() -> Unit 是一个带接收者的函数字面量,它的类型是init = Function1<com.kotlin.easy.HttpRequestWrapper, kotlin.Unit>。 HttpRequestWrapper是扩展函数init()的接收者,点号 . 是扩展函数修饰符。

我们在函数体内直接调用了这个函数字面量 wrap.init() 。这样的写法可能比较难以理解,这个函数字面量 init 的调用实际上是 init.invoke(wrap) ,就是把传入 ajax 的函数参数直接传递给 wrap 。为了更简单的理解这个 init 函数的工作原理,我们通过把上面的 ajax 函数的代码反编译成对应的 Java 代码如下:

   public static final void ajax(@NotNull Function1 init) {
      Intrinsics.checkParameterIsNotNull(init, "init");
      HttpRequestWrapper wrap = new HttpRequestWrapper();
      init.invoke(wrap);
      doCall(wrap);
   }

也就是说,ajax 函数的一个更容易理解的写法是

fun ajax(init: HttpRequestWrapper.() -> Unit) {
    val wrap = HttpRequestWrapper()
    init.invoke(wrap)
    doCall(wrap)
}

我们在实际应用的时候,可以直接把 init 写成Lambda 表达式的形式,因为接收者类型HttpRequestWrapper 可以从上下文推断出来。

我们这样调用 ajax 函数:

ajax {
    url = testUrl
    method = "get"
    success {
        string ->
        println(string)
        Assert.assertTrue(string.contains("百度一下"))
    }
    error {
        e ->
        println(e.message)
    }
}

下面是几个测试代码示例:

package com.kotlin.easy

import com.alibaba.fastjson.JSONObject
import okhttp3.MediaType
import okhttp3.RequestBody
import org.junit.Assert
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4

/**
 * Created by jack on 2017/7/23.
 */

@RunWith(JUnit4::class)
class KAjaxTest {

    @Test fun testHttpOnSuccess() {
        val testUrl = "https://www.baidu.com"
        ajax {
            url = testUrl
            method = "get"
            success {
                string ->
                println(string)
                Assert.assertTrue(string.contains("百度一下"))
            }
            error {
                e ->
                println(e.message)
            }
        }

    }

    @Test fun testHttpOnError() {
        val testUrl = "https://www2.baidu.com"

        ajax {
            url = testUrl
            method = "get"
            success {
                string ->
                println(string)
            }
            error {
                e ->
                println(e.message)
                Assert.assertTrue("connect timed out" == e.message)
            }
        }
    }

    @Test fun testHttpPost() {
        var json = JSONObject()
        json.put("name", "Kotlin DSL Http")
        json.put("owner", "Kotlin")
        val postBody = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), json.toString())
        ajax {
            url = "saveArticle"
            method = "post"
            body = postBody
            success {
                string ->
                println(string)
            }
            error {
                e ->
                println(e.message)
            }
        }
    }

    @Test fun testLambda() {
        val testUrl = "https://www.baidu.com"

        val init: HttpRequestWrapper.() -> Unit = {
            this.url = testUrl
            this.method = "get"
            this.success {
                string ->
                println(string)
                Assert.assertTrue(string.contains("百度一下"))
            }
            this.error {
                e ->
                println(e.message)
            }
        }
        ajax(init)
    }

到这里,我们已经完成了一个极简的 Kotlin Ajax DSL。

本节工程源码: https://github.com/EasyKotlin/chatper14_kotlin_dsl_http

本章小结

相比于Java,Kotlin对函数式编程的支持更加友好。Kotlin 的扩展函数和高阶函数(Lambda 表达式),为定义Kotlin DSL提供了核心的特性支持。

使用DSL的代码风格,可以让我们的程序更加直观易懂、简洁优雅。如果使用Kotlin来开发项目的话,我们完全可以去尝试一下。

时间: 2024-12-03 09:04:03

《Kotin 极简教程》第14章 使用 Kotlin DSL的相关文章

分布式唯一ID极简教程

原创 2017-11-21 帝都羊 架构师小秘圈 一,题记 所有的业务系统,都有生成ID的需求,如订单id,商品id,文章ID等.这个ID会是数据库中的唯一主键,在它上面会建立聚集索引! ID生成的核心需求有两点: 全局唯一 趋势有序   二,为什么要全局唯一?   著名的例子就是身份证号码,身份证号码确实是对人唯一的,然而一个人是可以办理多个身份证的,例如你身份证丢了,又重新补办了一张,号码不变. 问题来了,因为系统是按照身份证号码做唯一主键的.此时,如果身份证是被盗的情况下,你是没有办法在系

Photoshop制作极简的砂石写意层次海报教程

给各位photoshop软件的使用者们来详细的解析分享一下制作极简的砂石写意层次海报的教程. 教程分享: 1.首先新建一个900 * 900像素的画布.   2.使用油漆桶工具将画布的背景色填充为#e8e8e8.   3.使用形状 > 椭圆工具,创建468 * 468像素的圆形.   4.选中画布中刚才建立的圆形,先Ctrl + C 复制一个形状,再选中这个形状,Ctrl + T 进行变形.   5.以中心点将圆形的长宽都缩小到90%.     6.使用布尔运算工具,选择"减去顶层形状&q

90分钟实现一门编程语言(极简解释器教程)_C#教程

本文介绍了如何使用 C# 实现一个简化 Scheme--iScheme 及其解释器. 如果你对下面的内容感兴趣: 实现基本的词法分析,语法分析并生成抽象语法树. 实现嵌套作用域和函数调用. 解释器的基本原理. 以及一些 C# 编程技巧. 那么请继续阅读. 如果你对以下内容感兴趣: 高级的词法/语法分析技术. 类型推导/分析. 目标代码优化. 本文则过于初级,你可以跳过本文,但欢迎指出本文的错误 :-) 代码样例 public static int Add(int a, int b) { retu

Illustrator三个简单粗暴极简设计制作教程

给各位Illustrator软件的使用者们来详细的解析分享一下三个简单粗暴极简设计制作教程. 教程分享:       好了,以上的信息就是小编给各位Illustrator的这一款软件的使用者们带来的详细的三个简单粗暴极简设计制作教程解析分享的全部内容了,各位看到这里的使用者们,小编相信大家现在是非常的清楚了方法了吧,那么各位就快去按照小编上面的教程自己去尝试下吧,希望能给大家带去有用的帮助.

极简的网页视觉设计教程:网页变得轻薄简洁而美观

导言: 如何让杂乱又咄咄逼人的网页变得轻薄简洁而美观,又需保留完整功能.同时很好的区分出重点模块?这往往是让设计师困扰纠结之源:如何更好的将多余的元素.色彩.形状和纹理剔除,保留重点并梳理清晰的视觉流动方向? 如果一个页面有太多的元素,用户将迷惑于去看哪里或误解每个元素的优先级.优秀的设计师需要用设计语言将焦点正好引导到正确的内容上,正如提到的,极简的网页视觉能够将最重要的内容带到最前面并为用户避免分散注意力,比如,在一个黑白设计上的任何色斑,都会引起用户的注意力,色彩本身也将成为焦点,这便成为

小米手机极简模式开启教程

1.我们在桌面点击[设置]按钮进入,如下图所示: 2,在设置下面就有一个[极简模式]我们点击它打开进入: 3,在极简模式界面我们看到默认是关闭的, 现在我们来打开它,点击开启,如下图所示: 4,然后系统会友好的给个提示了,我们点击[确定]即可,如下图所示: 5,但有朋友发现极简模式开启之后没有反应了,这个就是重点所在了我们必须要重启手机才可以真正的进入到极简模式的哦: 好了重启手机之后你会发现已经成功进入到了极简模式了,就是这个界面了简单实用,字体又大适合老人使用的吧.

网页设计理论:漂亮而不空洞的极简网站设计

作为设计师,我们都知道,一个极简的设计可以实现漂亮的效果. 然而,很多设计师在实现上有些麻烦:要么是没有时间让使用如此少的元素制作的页面看起来漂亮,要么就是最终的结果只是看起来"不完美". 网上有很多关于极简主义设计的文章,而本文的目的是帮你实现一个漂亮而不空洞的极简网站设计 最重要的是,我们将展示一个小的极简网站设计画廊,这样你就可以分析为什么一些设计可以而其它的则不可以. 什么是极简主义设计 极简主义设计已经被描述为最基本的设计,剔除了多余的元素.色彩.形状和纹理. 它的目的是使内

极简的Python入门指引_python

初试牛刀 假设你希望学习Python这门语言,却苦于找不到一个简短而全面的入门教程.那么本教程将花费十分钟的时间带你走入Python的大门.本文的内容介于教程(Toturial)和速查手册(CheatSheet)之间,因此只会包含一些基本概念.很显然,如果你希望真正学好一门语言,你还是需要亲自动手实践的.在此,我会假定你已经有了一定的编程基础,因此我会跳过大部分非Python语言的相关内容.本文将高亮显示重要的关键字,以便你可以很容易看到它们.另外需要注意的是,由于本教程篇幅有限,有很多内容我会

大数据的设计师帮你快速搞定一个极简风格网站

  如何快速做出一个极简风格网站?国外某个网站统计分析了112个明显带有极简风格的网站后,发布了这篇超级干货.全程都是以数据来说话,从占比最大的设计要素开始说起,有案例有分析,把极简风格的设计彻底发挥到了术的地步. 最小化设计的目标是移除界面当中不必要的元素或内容,减少干扰,使界面最大程度的支持用户的任务流程. 要将界面以恰当的方式简化到只保留必要元素的程度,设计师需要对一系列与最小化策略高度相关的设计模式有所了解.界面设计,就像人类的语言一样,最终是由人们的使用方式所定义的.如今,虽然"最小化