从零开始的8-bit迷宫探险【Level 6】Swift 基础语法 (四)

今日目标

  • 认识类别 (class) 及继承
  • 认识协定 (protocol)
  • 认识结构 (struct)

类别 (class)

类别是物件导向很重要的一个概念,在开发游戏时也常用到它。
可以把它想像成一张角色设计图,里头可能定义了角色会具备的属性、能力。有了这张设计图後,我们可以制作角色出来,也就是将角色实体化,变成一个真实的角色物件 (object)。

  • 接着来看宣告类别的方式:
    • 定义了一个角色类别 Role,具有头发颜色 hairColor 跟眼睛颜色 eyeColor 两种属性
    • 它有一个建构子 init,可带入参数,并且在初始化时赋予 hairColoreyeColor
    • 定义了两种方法,让角色可以跑 run、跳 jump
class Role {
    var hairColor: String
    var eyeColor: String
    
    init(hairColor: String, eyeColor: String) {
        self.hairColor = hairColor
        self.eyeColor = eyeColor
    }
    
    func run() {
        print("run")
    }
    func jump() {
        print("jump")
    }
}
  • 接着可以创建一个实体:
    • 透过 Role(hairColor: "orange", eyeColor: "blue") 可以创建一个实体,并且带入这个角色特有的参数。例如我们创建一个人类的角色实体 human
    • 透过 human.hairColor 取得实体的属性
    • 透过 human.run() 可以执行方法
let human = Role(hairColor: "black", eyeColor: "brown")
print("hair color: \(human.hairColor)")
print("eye color: \(human.eyeColor)")
human.run()
human.jump()
印出结果:
hair color: black
eye color: brown
run
jump

继承

提到类别就不能少了继承的概念,当一个 A 类别继承了 B类别,则 B类别称为父类别,A类别称为子类别。子类别可以继承父类别的属性及方法,可以减少重复的程序码。
在 swift 里,使用继承的方式可以用 :,将要继承的类别放在冒号後面,并且最多只能继承一个类别
例如,宣告一个新的勇者类别 Brave,让它继承 Role 类别

  • 可以在方法前加上 override,覆写原本的 run方法
  • 可以新宣告飞 fly 方法
class Brave: Role {
    override func run() {
        print("run faster")
    }
    func fly() {
        print("fly")
    }
}
  • 创建一个实体:
    • 勇者类别 Brave 依然可以使用父类别 Role 中的属性及方法,并且多了自己专有的方法,除了跑、跳之外,还可以飞
let brave = Brave(hairColor: "orange", eyeColor: "blue")
print("hair color: \(brave.hairColor)")
print("eye color: \(brave.eyeColor)")
brave.run()
brave.jump()
brave.fly()
印出结果:
hair color: orange
eye color: blue
run faster
jump
fly

协定 (protocol)

协定 (protocol) 的概念,其实和 interface 的概念非常相像,是一个尚未实作的、抽象的概念
它可以跟类别一样定义一些属性及方法,但是并不会实作,也就是方法内没有内容。

  • 宣告协定的方式:
    • 新增一个超能力 SuperPower 的协定,让勇者来使用
    • 使用协定的人必须含有 useSuperPower() 方法的实作
protocol SuperPower {
    func useSuperPower()
}
  • Brave 类别遵循 SuperPower 协定:
    • 我们在继承的 Role 类别後方,使用逗号 ,,再加上 SuperPower 协定。
    • 协定必需写在类别的後方,可以同时遵循很多种协定。
    • 实作 useSuperPower() 方法的内容,让勇者具有瞬间移动的超能力
class Brave: Role, SuperPower {
    override func run() {
        print("run faster")
    }
    func fly() {
        print("fly")
    }
    func useSuperPower() {
        print("move instantly!!")
    }
}
let brave = Brave(hairColor: "orange", eyeColor: "blue")
brave.useSuperPower()
印出结果:
move instantly!!
  • 协定的好处
    当有不同的东西都需要某一种方法,并且方法的内容也不一样时,我认为满适合使用协定的概念来实作。
    例如,我们再创建一个怪物的类别 Monster,同样继承 Role 类别。我们希望勇者跟怪物同样都可以使用超能力,并且他们的超能力不同,因此我们将 Monster 也遵循 SuperPower 协定
class Monster: Role, SuperPower {
    func useSuperPower() {
        print("fireball!!")
    }
}
let monster = Monster(hairColor: "red", eyeColor: "red")
monster.useSuperPower()
印出结果:
fireball!!
  • 现在,我的勇者和怪物两种角色,都可以使用超能力方法 useSuperPower() 了,并且具有不同种类的超能力。而最初创建的人类角色,没有遵循超能力协定 SuperPower,因此也确实不能使用超能力。
  • 我们不需要将 useSuperPower() 方法宣告在 Role 类别中,也不需要在一开始就定义他的方法内容,只需要宣告在抽象的协定中,并且只在使用到的地方实作方法即可。
  • 加上了协定後,Xcode 会贴心地提醒必须实作的方法,否则会报错,可以让开发者更清楚知道需要实作哪些方法。

结构 (struct)

结构 (struct) 和类别 (class) 类似,可以宣告属性及方法,但是结构不能继承,不过它是可以使用协定的。

  • 宣告一个结构
    • 定义属性及方法
    • 建构子 init 可以省略
    • 遵循 SuperPower 协定,并且实作 useSuperPower() 方法
struct Brave: SuperPower {
    var hairColor: String
    var eyeColor: String

    func fly() {
        print("fly")
    }
    func useSuperPower() {
        print("move")
    }
}
  • 创建一个实体:
let brave = Brave(hairColor: "orange", eyeColor: "blue")
print("hair color: \(brave.hairColor)")
print("eye color: \(brave.eyeColor)")
brave.fly()
brave.useSuperPower()
印出结果:
hair color: orange
eye color: blue
fly
move

结构 vs 类别

从上述的范例,结构看起来感觉跟类别很像对吧,但是他们还是有差异的。

  • 结构储存的内容是实体的值 (value type)
  • 类别储存的内容是实体存放的位址 (reference type)

简单来讲,当我对一个结构的实体做修改,我可以确实只修改到它。
而当我对一个类别的实体做修改,有可能会有其他实体也一起被修改!原因是当修改类别的实体时,实际上是对它指向的位址做修改,当有其他实体也是指向同个位址时,就会一起被修改了。

  • 以这个范例为例:
    当对 structBrave2 做修改时,structBrave 还是维持原本的值 (blue)
    当对 classBrave2 做修改时,classBrave 的值也变成新的值了 (green)
struct StructBrave {
    var hairColor: String
    var eyeColor: String
}
class ClassBrave {
    var hairColor: String
    var eyeColor: String
    
    init(hairColor: String, eyeColor: String) {
        self.hairColor = hairColor
        self.eyeColor = eyeColor
    }
}
let structBrave = StructBrave(hairColor: "orange", eyeColor: "blue")
let classBrave = ClassBrave(hairColor: "orange", eyeColor: "blue")
var structBrave2 = structBrave
var classBrave2 = classBrave
structBrave2.eyeColor = "green"
classBrave2.eyeColor = "green"
print(structBrave.eyeColor)
print(classBrave.eyeColor)
印出结果:
blue
green
  • 由於结构是 value type,因此如果使用 let 宣告实体,不能修改他的属性
let structBrave = StructBrave(hairColor: "orange", eyeColor: "blue")
let classBrave = ClassBrave(hairColor: "orange", eyeColor: "blue")
structBrave.eyeColor = "green" // 报错,不能修改常数
classBrave.eyeColor = "green" // 成功编译,因 var eyeColor: String 为变数

以上是今日的介绍~
明天将进入 swift 基础语法的最後一篇介绍,继续加油吧!/images/emoticon/emoticon61.gif


<<:  Day 1 : 前言-为什麽要撰写这系列文章

>>:  无法上网?请询问你的 ISP:何谓网路服务供应商?

加点GCP – cloud function

Golang 加点GCP – cloud function 如果想要开始做点其他事情,就必须脱离本机...

用React刻自己的投资Dashboard Day5 - 多张图表渲染(Rendering lists)

tags: 2021铁人赛 React 上一篇只画了一张图表,投资怎麽可能只需要看一张图呢?这边就再...

Day 04:「当个方块设计师」- 制作自己的方块,改变大小并加上背景色彩及边框

Day 3 的作业有没有写呢各位? 如果昨天有练习的话,应该觉得不会太难吧! 我们今天会用一样的方...

服务组织控制(Service Organization Control :SOC)

-服务组织控制(SOC) 服务组织控制(Service Organization Control ...

Day2-Start to go

简介 Go 是由 Google 开发的程序语言,於2007发起,在2009正式推,2012年发布第一...