物件導向程式是目前主流的程式設計範式,其思維為在函式上加入狀態,藉由狀態改變而改變程式內的資料。在本文中,我們從 Nim 語言的觀點來看如何撰寫物件導向程式。
建立物件
在以下例子中,我們建立一個 Point
類別,該類別帶有 x
和 y
兩個屬性,之後就可以用 Point
類別建立 p
物件:
type
Point* = ref object
x*: float
y*: float
when isMainModule:
let p = Point(x: 3, y: 4)
assert(p.x == 3)
assert(p.y == 4)
在 Nim 語言中,某個類別沒有繼承其他類別時,皆會繼承 object
類別。在 Point*
、x*
和 y*
中,尾端帶有星號 *
,這牽涉到模組的封裝;我們會於後文詳談模組,目前只要知道若要對外公開的項目就要加星號 *
即可。
帶有方法的物件
在上一個例子中,我們將物件的屬性直接對外公開,這不是良好的習慣;若以後我們要限制屬性的範圍,會破壞原有的外部程式碼。一般來說,我們會將屬性私有化,用公開方法來存取,如下例:
type
Point* = ref object
px: float
py: float
proc x*(p: Point): float =
p.px
proc `x=`(p: Point, x: float) =
p.px = x
proc y*(p: Point): float =
p.py
proc `y=`(p: Point, y: float) =
p.py = y
proc newPoint*(x: float, y: float): Point =
new(result)
result.x = x
result.y = y
when isMainModule:
var p = newPoint(3, 4)
assert(p.x == 3)
assert(p.y == 4)
這種存取屬性的方法,稱為 getter 和 setter。在我們這個例子中,x*
和 y*
等 getters 是公開的,但 x=
和 y=
等 setters 是私有的,在物件建立後,我們就無法更改 px
和 py
這兩個屬性;藉由控管公開方法,屬性變成唯讀的。
我們可以將上例修改,屬性就會變成可修改的:
type
Point* = ref object
px: float
py: float
proc x*(p: Point): float =
p.px
proc `x=`*(p: Point, x: float) =
p.px = x
proc y*(p: Point): float =
p.py
proc `y=`*(p: Point, y: float) =
p.py = y
proc newPoint*(x: float, y: float): Point =
new(result)
result.x = x
result.y = y
when isMainModule:
var p = newPoint(0, 0)
assert(p.x == 0)
assert(p.y == 0)
p.x = 3
p.y = 4
assert(p.x == 3)
assert(p.y == 4)
一旦將屬性設為可修改的,日後再將其改為唯讀的,就有可能破壞外部程式碼;所以,儘量不要一下子就將權限開放過大。
UFCS (Uniform Function Call Syntax)
UFCS 算是 Nim 的語法糖,在這個規則下,method(obj, args)
等同於 obj.method(args)
。如下例:
type
Point = ref object
px: float
py: float
# Declare Point as above.
when isMainModule:
var p = newPoint(3, 4)
assert(x(p) == 3) # Same as p.x
assert(y(p) == 4) # Same as p.y