文档章节

不用框架也能编码

Napierfyu
 Napierfyu
发布于 2017/07/12 15:57
字数 1504
阅读 10
收藏 0

 

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

 

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

 

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

 

方 法

 

很显然,这里摆出的观点并不新颖而且很多人都认为这是开发软件的正确途径。因此,如果你熟悉依赖反转的概念或者使用很多小程序库(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
}

结 束

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

 

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

© 著作权归作者所有

共有 人打赏支持
Napierfyu
粉丝 0
博文 3
码字总数 4321
作品 0
海淀
私信 提问
Yii框架里关于编码的问题,请知道的前辈不吝赐教

是这样的。 我的前端HTML,声明编码是UTF-8,网页运行UTF_8良好 然后我不用YII框架,进行PHP试编写的时候:网页运行UTF_8良好 然后我现在再用YII框架,莫名其妙的变成了GB-2312..... 有没有前...

崩子皆蔓
2013/12/15
315
2
YMP v1.0 发布,轻量级 Web 开发框架

轻量级的Web应用开发框架YMP正式发布v1.0版本! 在经历了近一年时间实际公司项目环境下测试和迭代,优化和修复了若干问题,提高YMP框架代码的成熟度和稳定性。变更详情点击:http://git.oschi...

有理想的鱼
2014/10/25
4.3K
15
为什么编码规范里要求每行代码不超过80个字符

也许在Python编码风格指导(PEP8)中最有争议的一部分要数每行代码不超过80个字符的限制。没错,实际上是79个字符,但我使用80个字符,这个大概数,它是给程序员的一个参考值。 古老的VT100终端...

oschina
2013/04/09
11.2K
40
RDIFramework.NET平台代码生成器V3.2版本全新发布-更新于2017-02-27(提供下载-免费使用)

  回顾V3.1版本更新内容如下:   1、增加对Oracle表创建语句的查看。   2、新增对MySql的代码生成支持。   3、全面重构对多线程的支持,改变以前会无故退出的现象。   本次在V3.1版...

80368704
2018/06/29
0
0
开源中国春节前最后一波招聘,我们送开源内裤的

开源中国继续招聘 Java 工程师、Ruby 工程师和编辑人才: Java 工程师要求: 对学历没要求,而且不要求你会 Spring、Struts 和 Hibernate 等这些框架。热爱编码,Java 基本功扎实。 Ruby 工程...

oschina
2015/01/09
12.4K
183

没有更多内容

加载失败,请刷新页面

加载更多

tac 与cat

tac从后往前看文件,结合grep使用

writeademo
今天
2
0
表单中readonly和dsabled的区别

这两种写法都会使显示出来的文本框不能输入文字, 但disabled会使文本框变灰,而且通过通过表单提交时,获取不到文本框中的value值(如果有的话), 而readonly只是使文本框不能输入,外观没...

少年已不再年少
今天
2
0
SpringBoot上传图片操作

首先有个上传文件的工具类 /** * 文件上传 * @param file * @param filePath * @param fileName * @throws Exception */public static void uploadFile(byte[] file, String ...

_liucui_
今天
6
0
DrawerLayout

public class MainActivity extends BaseActivity implements NavigationView.OnNavigationItemSelectedListener,OnFragmentInteractionListener{ public NavigationView navView; ......

安卓工程师王恒
今天
2
0
python精简笔记

python精简笔记-字符串基本用法 字符串常见用法: * encode() # 编码成bytes类型 * find() # 查找子串 * index() # 获取下标 * replace() # 替换子串 * len(string) # 返回字符串长度,...

平头哥-Enjoystudy
今天
2
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部