简单翻译了一篇编程技巧,虽然内容上是关于 Lua 的,但实际上大部分技巧都是通用的(适用于其他语言).
这个原则((YAGNI原则))和你计划在将来添加的程序特性有关,该原则是 “You aren’t gonna need it(你不会需要它的)” 的缩写.你不应该在需求明确之前添加新的程序功能或者程序特性,任何新的程序特性其实都会对程序的扩展性产生限制,所以一个不必要的程序特性很可能会影响到之后新增的一些必要的程序特性,并且当程序特性需求不明确时,我们也很难定义该程序特性实际的开发内容和测试方法.
相比较复杂代码,简单代码往往更优雅,更不易产生Bug, 同时也更方便构建扩展,程序员之间也经常会提起这两个原则(“Keep it simple, stupid(保持简单)”(KISS原则) 和 “Do the simplest thing that could possibly work(能用即可)”(DTSTTCPW原则))
每次当你修改现有代码的时候,总是尽可能的去重构(优化)一下(BoyScout原则)(译注:BoyScout原则原话:“永远把露营地弄得比你发现时还要干净”),让你的代码保持干净简洁,以使其易于理解、修改和扩展,同时确保代码表述了所有(必要)内容并且只表述了一次.
程序员为了编写更少的代码行数,往往会在一行代码里塞入很多语句,当然,这么做确实让代码变短了,但同时也让代码变得更难以使用了:一行包含许多语句(操作)的代码,往往难以阅读、维护和优化,并且更短的代码也并不一定有更快的执行速度,在下面的示例代码中,短代码(example 1)虽然行数较少,但是却存在两处多余的操作(相比较 example 2),有兴趣的朋友可以找一找.
-- example 1
local d = math.sqrt((x - x2)^2 + (y - y2)^2)
local nx, ny = (x - x2)/d, (y - y2)/d
-- example 2
local dx, dy = x - x2, y - y2
local dsq = dx^2 + dy^2
local d = math.sqrt(dsq)
local nx, ny = dx/d, dy/d
和许多解释型语言一样, Lua 并不支持函数声明,所以变更函数名字或者函数所需参数个数的行为,在 Lua 中是个危险且费时的操作:每当你修改某个特定函数的时候,你必须手动对你代码中所有调用该函数的地方进行同步修改,这个过程极易产生 Bug,这也是为什么我们要尽量避免重复的函数调用.当我们与第三方 API 或者 Lua 模块交互时,合并函数调用会变得更为重要,因为更少的函数调用会让我们在 移植新平台 或者 升级 API 时更加简单.
-- example 1
if up then
player:move(0, 10)
elseif down then
player:move(0, -10)
end
if right then
player:move(10, 0)
elseif left then
player:move(-10, 0)
end
-- example 2
local dx, dy = 0, 0
if up then
dy = 10
elseif down then
dy = -10
else
if right then
dx = 10
elseif left then
dx = -10
end
if dx ~= 0 or dy ~= 0 then
player:move(dx, dy)
end
一点小小的数学知识很可能能替代一大段的逻辑代码.在程序开发中,我们往往倾向于使用数学公式而不是大段的 “if” 判断:我们可以很方便的调整仅包含数学公式的函数,但是对于包含很多逻辑判断的代码,其中不仅很容易产生 Bug,而且也难以扩展.
-- if then solution
local x = tx*tile_width
if ty%2 == 1 then
x = x + tile_width/2
end
local y = ty*tile_height/2
-- math solution
local x_offset = ty%2*(tile_width/2)
local x = tx*tile_width + x_offset
local y = ty*(tile_height/2)
以我的经验来看,这条技巧(避免中间(程序)对象)可能是编写 Lua 代码时最重要的一条优化技巧.不断的创建 table 对象不仅需要大量的创建时间,而且会给垃圾收集器带来非常大的压力,致使程序出现长时间的 GC 消耗,进而造成程序的帧率不稳.
在 Lua 中,函数直接支持多值返回,这让我们基本不用创建中间(table)对象来处理多返回值的问题.
-- example 1
player.get_position = function(player)
return { x = player.x, y = player.y }
end
-- example 2
player.get_position = function(player)
return player.x, player.y
end