Nim 語言程式教學:類別 (Class) 和物件 (Object)

PUBLISHED ON MAR 25, 2018 — PROGRAMMING
FacebookTwitter LinkedIn LINE Skype EverNote GMail Email Email

    物件導向程式是目前主流的程式設計範式,其思維為在函式上加入狀態,藉由狀態改變而改變程式內的資料。在本文中,我們從 Nim 語言的觀點來看如何撰寫物件導向程式。

    建立物件

    在以下例子中,我們建立一個 Point 類別,該類別帶有 xy 兩個屬性,之後就可以用 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 是私有的,在物件建立後,我們就無法更改 pxpy 這兩個屬性;藉由控管公開方法,屬性變成唯讀的。

    我們可以將上例修改,屬性就會變成可修改的:

    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