學習軌道
/
Crystal
Crystal
/
練習
/
Johannes 的果汁機
Johannes 的果汁機

Johannes 的果汁機

學習練習

簡介

類別是物件導向程式設計中的核心概念。 在 Crystal 中,萬物皆為物件,每個物件都有型別,並且能回應某些方法。 類別是建立物件的藍圖,為狀態(例如變數)提供初始值,並提供行為的實作(例如方法)。 物件是類別的實例,持有所有實例變數與狀態。

類別定義

使用class關鍵字定義類別,後面接著以UpperCamelCase撰寫的類別名稱。

class Account
end

建立實例

若要建立類別的實例,請使用new方法呼叫類別名稱。

class Account
end

account = Account.new

在上面的範例中,變數account是類別Account的實例。

建立實例時,一個名為initialize的特殊方法會扮演建構子的角色。 建立實例時會呼叫它。 建構子讓你設定實例的初始狀態。 initialize方法可以接受引數,並在建立實例時傳入。

class Account
  def initialize(number)
    5 + number # => 9
  end
end

Account.new(4)
Note

initialize方法無法手動回傳值,而是由new回傳新建立的實例。

class Account
  def initialize(number)
    5 + number
  end
end

Account.new(4)
# => #<Account:0x7f5dc33dcea0>

實例方法

實例方法是可供類別實例使用的方法,並且可以在該實例上呼叫。 實例方法使用def關鍵字定義,後面接著方法名稱。 它們定義在類別定義內。

class Account
  def balance
    100
  end
end

account = Account.new
account.balance # => 100

實例變數

實例變數是實例中所有方法都能使用的變數。 若要存取實例變數,你需要定義一個實例方法。 它們可以在initialize方法中初始化。 它們使用@前綴定義。

class Account
  def initialize
    @balance = 100
  end

  def balance
    @balance
  end
end

account = Account.new
account.balance
# => 100

實例變數也可以定義在類別定義內。

class Account
  @age = 0
end

帶初始化的實例變數

實例變數可以使用傳入initialize方法的引數來初始化。 由於 Crystal 無法推斷初始化時所用引數的型別,因此必須明確指定。 如果你想進一步了解,可以閱讀:型別推斷。 若要指定變數的型別,可以使用:符號,後面接著型別。 例如,如果你想建立一個型別為Int32、名為balance的實例變數,可以這麼做:@balance : Int32。 或者,如果你想建立一個型別為Bool、名為raining的實例變數,可以這麼做:@raining : Bool。

有幾種實作方式,其中一種是在initialize方法中將引數宣告為該型別,再將實例變數指定給該引數,如下面的範例:

class Account
  def initialize(balance : Int32)
    @balance = balance
  end

  def balance
    @balance
  end
end

account = Account.new(1)
account_2 = Account.new(5)

account.balance
# => 1

account_2.balance
# => 5

另一種方式是在類別定義中,以下列形式宣告實例變數的型別:@<variable> : <Type>,如下面的範例:

class Weather

  @raining : Bool

  def initialize(raining)
    @raining = raining
  end

  def raining
    @raining
  end
end

weather = Weather.new(true)
weather.raining
# => true

修改實例變數

方法可以修改實例變數。 這些方法可以在類別的實例上呼叫。 當方法修改實例變數時,該變更只會在呼叫它的那個類別實例中生效。

class Account
  def initialize(balance : Int32)
    @balance = balance
  end

  def withdraw(amount)
    @balance -= amount
  end

  def balance
    @balance
  end
end

account = Account.new(10)
account_2 = Account.new(10)
account.balance
# => 10

account_2.balance
# => 10

account.withdraw(5)
account.balance
# => 5

account_2.balance
# => 10

類別方法

類別方法是為類別定義、而非為其實例定義的方法。 它們提供了一種建立不依賴實例狀態之方法的方式。 它們使用def關鍵字定義,後面接著self.<method name>。 self是對呼叫 self 時所在命名空間的參照。 在這個例子中,它指的是類別Account。 這等同於撰寫Account.,但self.是較建議的寫法。

class Account
  def self.balance
    100
  end
end

Account.balance
# => 100

說明

你的朋友 Johannes 很喜歡自己動手做(DIY)硬體。 他們剛做出了一台果汁機,Johannes 非常興奮。 Johannes 的目標是每天早上都能做出新鮮的果汁。

Johannes 果汁機有很多厲害的功能。 像是內建的測量系統,可以告訴你杯子裡有多少果汁。 它也有計時器,能告訴你機器已經運轉了多久。

Johannes 不太擅長寫程式,但他們想寫一個程式來控制這台機器。 因此他們請你來幫忙。

1. 除錯燈

這台機器有一個除錯燈,原理很簡單:只要機器有電,除錯燈就會亮。 只有在除錯燈亮著的時候,程式才會執行。

這個方法應該是一個名為JuiceMaker的類別方法。

定義類別方法JuiceMaker.debug_light_on?,讓它回傳true。

JuiceMaker.debug_light_on?
#=> true

2. 初始化機器

當機器通電時,應該會建立一個JuiceMaker類別的實體。 這台機器應該要有一個初始化狀態。 初始化狀態應該包含以下資訊:

  1. 機器是否在運轉。當機器開啟時,這應該一律是false。
  2. 杯子裡的果汁量,這會是一個測量杯中果汁量的感測器。

定義初始化狀態,它會把杯子裡的果汁量當作參數,並建構這個初始化狀態。 初始化狀態應該包含一個設為false的實體變數@running,以及一個根據類別初始化時所給的引數而定的實體變數@fluid。

JuiceMaker.new(5)
#=> #<JuiceMaker:0x10f0b8 @running=false, @fluid=5>

3. 開啟機器

這台機器可以開啟和關閉。 你需要定義一個方法來開啟機器。 當機器開啟時,運轉狀態應該設為true。

定義方法JuiceMaker#start,用來開啟機器。

juice_maker = JuiceMaker.new(5)
juice_maker.start

juice_maker
#=> #<JuiceMaker:0x10f0b8 @running=true, @fluid=5>

4. 機器狀態

這台機器需要能告訴你機器的狀態。 如果機器正在運轉,它應該回傳true;如果沒有運轉,則回傳false。

定義方法JuiceMaker#running?,讓它回傳機器的狀態。

juice_maker = JuiceMaker.new(5)
juice_maker.start

juice_maker.running?
#=> true

5. 加入果汁

這台機器可以把果汁加入杯子。 機器能夠知道加了多少果汁到杯子裡。 加入果汁之後,機器需要協助才能知道杯子裡有多少果汁。

定義方法JuiceMaker#add_fluid,它會把加入的果汁量當作參數,並更新杯子裡的果汁量。

juice_maker = JuiceMaker.new(5)
juice_maker.add_fluid(5)

juice_maker
#=> #<JuiceMaker:0x10f0b8 @running=false, @fluid=10>

6. 關閉機器

這台機器可以開啟和關閉。 你需要定義一個方法來關閉機器。 這個方法只會在機器運轉時被呼叫。 當機器關閉時,運轉狀態應該設為false。 關閉之後,機器也需要協助才能判斷杯子裡有多少果汁。 這台機器每分鐘會用掉五個單位的果汁。

定義方法JuiceMaker#stop,它會把機器已運轉的分鐘數當作參數,並回傳機器關閉後杯子裡的果汁量。

juice_maker = JuiceMaker.new(5)

juice_maker.start
juice_maker
#=> <JuiceMaker:0x10f0b8 @running=true, @fluid=5>

juice_maker.stop(1)
#=> #<JuiceMaker:0x10f0b8 @running=false, @fluid=0>
透過 GitHub 編輯 連結會在新視窗或分頁中開啟
Crystal Exercism

準備好開始 Johannes 的果汁機 了嗎?

註冊 Exercism,透過 26 個概念142 個練習 和真人引導來學習並精通 Crystal,全部免費。