不用框架也能编码
不用框架也能编码
Napierfyu 发表于5个月前
不用框架也能编码
  • 发表于 5个月前
  • 阅读 2
  • 收藏 0
  • 点赞 0
  • 评论 0

腾讯云 技术升级10大核心产品年终让利>>>   

摘要: 没有框架你编写代码还能又快又高效吗?

 

本文来自“优才网”,关注更多程序员技能提升资料,请观众“优才网”公众号。

 

没有框架你编写代码还能又快又高效吗?像这样的试验证明了:你能。但是你应该这么做吗?

 

我已经在一个令人愉快的项目上工作一段时间了,这个项目很小,小到让我感到可以为所欲为,氮素呢?它又很大,大到能够看到我的想法所产生的效用。通过这个项目我实施的一个试验是:我能不能不用任何主要的框架去做它呢?这会花去更多的时间吗?它会相当困难吗?

 

方 法

 

很显然,这里摆出的观点并不新颖而且很多人都认为这是开发软件的正确途径。因此,如果你熟悉依赖反转的概念或者使用很多小程序库(libraries)来代替一个大框架,那么这个部分就对你不会有太大的启发了。

 

正如我已经在上一段落中暗示的,我决心着重依靠依赖反转原理,不管我在这个过程中铺设了多少额外的具有“基础设施”作用的代码,那都是为了适应我的所需,而不是为了别的什么。

 

为了更明确一些,对于任何一种正常来说可能被一种框架提供的代码,我将以一个描述我的实际的、目前的需求为目的的接口(interface)开始。一旦接口设计完成了,我将在我的应用的基础设施层面来使它生效。就本质上来说,我进入了一个圈圈,我发明某种被称为梦想API的东西来解决问题,然后呢我再做可以接入API的应用。

 

package stupid.jokes;
interface Dreamable {
  Lots<Money> makeHappy(Customer customer);
}

 

因为我在这个项目上是有很确定的截止日期的,所以我把这些接口看作是防止时间表问题出现的备用计划。如果发生任何失误,我可能会尝试引入重量级的防身武器(比如Spring)以及用它使接口生效。

 

只要时间表或者我低水平的编程技巧没有什么问题,我决心继续用(相对来说)小的程序库(libraries)以及自己编写一些重要的代码块。

 

举例:REST 端点

 

我猜用于暴露REST端点的代码是一个特别好的功能的案例,这个功能一般会被诸如Spring、 Ratpack这类的框架所提供。我也觉得它会是这个post的好案例,作为定义端点的很酷的方法,当你选择一个框架的时候,这是一件重要的事情。

 

在这个事例中,我的“梦想API”十分简单。它要求基础设施层来执行两个接口以及定义两个额外的类型别名(我们在Kotlin世界中)。

 

interface Router {
    fun get(path: String, action: Handler)
    fun post(path: String, action: Handler)
    fun put(path: String, action: Handler)
    fun delete(path: String, action: Handler)
}
interface Request {
    fun body(): String
    fun <T : Any> body(clazz: KClass<T>): T
    fun pathParam(name: String): String
    fun queryParam(name: String): String
}
typealias Response = (Any) -> Unit
typealias Handler = (Request, Response) -> Unit

 

很明显,我没有立即写下全部这些。我一个特征接一个特征,一个方法接一个方法地去迭代它。如果我记得正确,我的第一个端点只是请求“post”支持和一个请求体。

 

在应用启动过程中,Router安装启用的一个实例是传递一个方法,这个方法对应用中的所有路由的初始化负责。

 

fun init(router: Router) {
    router.get("/groups/:id/members") { req, res ->
        val groupId = req.pathParam("id")
        val members = findMembers(groupId)
        res.invoke(members)
    }
    router.post("/groups/:id/members") { req, _ ->
        val groupId = req.pathParam("id")
        val studentToAdd = req.body(StudentToAdd::class)
        addMember(groupId, studentToAdd)
    }
    router.get("/groups/:id/lessons") { req, res ->
        val groupId = req.pathParam("id")
        val lessons = findLessons(groupId)
        res.invoke(lessons)
    }
    router.post("/groups/:id/lessons") { req, _ ->
        val groupId = req.pathParam("id")
        val lessonToAdd = req.body(LessonToAdd::class)
        addLesson(groupId, lessonToAdd)
    }
    // etc.
}

 

这里,你的观点可能有所不同,但是我真的认为这是一片令人愉快的API。最重要的事情是它可以做我需要的一切来服务于这个应用的特殊使用案例。

 

现在,因为一些好的理由,我并不真的想要展示这里所执行的全部,但是我愿意让你知道这其实比很多人想象的要简单得多。

 

例如,使用Servlet API来处理路径参数的一个简单机制是分裂字符串的事件和遍历URI。

 

fun parse(req: HttpServletRequest): Request {
    val uriParts = req.requestURI.split("/")
    val pathParts = path.split("/")
    val pathParams = mutableMapOf<String, String>()
    for (i in pathParts.indices) {
        if (pathParts[i].startsWith(":")) {
            val key = pathParts[i].replace(":", "")
            pathParams.put(key, uriParts[i])
        }
    }
    return ParsedRequest(req, pathParams)
}

 

另一个来自你的应用与揭露REST端点相关的例子是启动一个嵌入式web服务器。一些人也会因为像Spring Boot这样的框架有这个功能而开心,即使你亲自启动这个服务器看起来真的是小菜一碟。

 

fun main(args: Array<String>) {
    val server = Server(8080)
    server.handler = HandlerList(routerServletHandler())
    server.start()
    server.join()
}
private fun routerServletHandler(): ServletHandler {
    val servletHandler = ServletHandler()
    servletHandler.addServletWithMapping(RouterServlet::class.java, "/*")
    return servletHandler
}

结 束

很高兴我的小试验很成功而且应用没有用任何主要的框架就按时交付了。我也很高兴用这种方式写应用并没有多煎熬。相反,我一直乐在其中,而且我没有因为没使用一些流行的框架而速度慢下来。

 

除了乐趣,我不确定在这个试验中是否还有什么重要的课题。首先,我一直问自己,为什么我们这些开发者要盲从那些夸大其词的框架,尤其是当这些框架并不能多有用时。另外,有没有什么方法能够彻底改造我们开始启动的每个项目的“轮胎”呢,尤其是当我们拥有众多的微服务的时候。

共有 人打赏支持
粉丝 0
博文 3
码字总数 4321
×
Napierfyu
如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
* 金额(元)
¥1 ¥5 ¥10 ¥20 其他金额
打赏人
留言
* 支付类型
微信扫码支付
打赏金额:
已支付成功
打赏金额: