轨道
/
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 无法推断初始化时所用实参的类型,因此必须显式指定类型。 如果你想了解更多,可以阅读:类型推断。 要指定变量的类型,可以使用 : 符号,后面跟上类型。 例如,如果你想创建一个名为 balance、类型为 Int32 的实例变量,可以这样写:@balance : Int32。 或者,如果你想创建一个名为 raining、类型为 Bool 的实例变量,可以这样写:@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。 机器还需要一些帮助,才能确定关闭后杯子里有多少果汁。 机器每分钟消耗 5 个单位的果汁。

定义方法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,全部免费。