原文:Object Oriented Programming in Swift
经过将事物分化成能够承继和组合的目标,学习面向目标的编程在 Swift 中是如何工作的。
面向目标编程(Object oriented programming)是一种基本的编程范式,假如你想要认真学习 Swift,就必须掌握这种范式。这是因为面向目标编程是你即将运用的大多数结构的核心。将一个问题分化成彼此发送音讯的目标,起初或许看起来很奇怪,但这是一种简化杂乱体系的老练办法,能够追溯到20世纪50年代。
目标简直能够用来模仿任何东西——地图上的坐标,屏幕上的触摸,乃至是银行账户中的利率波动。当你刚刚开端的时分,在你将其扩展到更笼统的概念之前,练习对实践世界中的物理事物进行建模是十分有用的。
在本教程中,你将运用面向目标编程来创立你自己的乐队。在这一过程中,你还会学到许多重要的概念,包括:
- 封装/Encapsulation
- 承继/Inheritance
- 重写与重载/Overriding & Overloading
- 类型与实例/Types & Instances
- 组合/Composition
- 多态性/Polymorphism
- 拜访操控/Access Control
涉及的内容有许多,所以让咱们开端吧! :]
开端
发动 Xcode 并转到 File.… 输入 Instruments 作为称号,挑选 iOS 作为开发渠道,然后点击 Next。挑选保存 Playground 的方位,然后点击创立。删去编辑器中的一切,以便从头开端。
以面向目标的办法设计事物,通常从一个通用的概念开端,延伸到更详细的类型。你想创立乐器,所以从一个乐器类型开端,然后基于它界说详细的(不是字面意思!)乐器,如钢琴和吉他,是十分合理的。把整个工作幻想成一棵乐器的宗族树,一切的东西都是这样从笼统到详细,从上到下活动的。
一个子类型和它的父类型之间的联系是一种 is-a 联系。例如,“吉他是一种乐器”。现在你对你所处理的目标有了直观的了解,是时分开端实施了。
特点
在 Playground 的顶部增加以下代码块:
// 1
class Instruments {
// 2
let brand: String
// 3
init(brand: String) {
// 4
self.brand = brand
}
}
这儿做了相当多的工作,所以让咱们把它剖析一下:
- 你用
class
关键字创立Instrument
基类(base class)。这是乐器类层次结构的根类。它界说了一个蓝图,构成了任何种类的乐器的根底。因为它是一个类型,Instrument
这个姓名被大写了。它不一定要大写,但这是 Swift 中的常规。 - 你声明了乐器的存储特点(stored properties),这是一切乐器都有的一个特点。在这儿,咱们用
brand
表明乐器的品牌,你把它用字符串表明。 - 你用
init
关键字为该类创立一个初始化器(initializer)。其意图是经过初始化一切存储特点来构造新的乐器实例。 - 你将作为参数传入的内容赋值到乐器的存储特点
brand
上。因为特点和参数有相同的姓名,你用self
关键字来区分它们。
你现已完成了一个包括 brand
特点的乐器 clsss
,但你还没有给它任何行为。是时分以办法的形式增加一些行为到这个组合中了。
办法
你能够对一个乐器进行调音和演奏,而不管它的详细类型。在 Instrument
类中的初始化器之后增加以下代码:
func tune() -> String {
fatalError("Implement this method for (brand)")
}
tune()
办法是一个占位符函数,假如你调用它,在运行时就会崩溃。有这样的办法的类被说成是笼统(abstract)类,因为它们不是用来直接运用的。相反,你必须界说一个重写该办法的子类来做一些合理的工作,而不是只调用 fatalError()
。稍后会有更多关于重写的内容。
在 class
中界说的函数(Function)被称为办法(method),因为它们能够拜访特点,例如 Instrument
中的 brand
特点。在一个 class
中安排特点和相关操作是操控杂乱性的一个强壮东西。它乃至有一个美丽的姓名:封装(encapsulation)。类的类型被说成是对数据(如存储特点)和行为(如办法)的封装。
接下来,在你的 Instrument
类之前增加以下代码:
class Music {
let notes: [String]
init(notes: [String]) {
self.notes = notes
}
func prepared() -> String {
return notes.joined(separator: " ")
}
}
这是一个 Music
类,它封装了一个音符数组,并答应你用 prepare()
办法将其拼接成一个字符串。
在 tune()
办法之后给 Instrument
类增加以下办法:
func play(_ music: Music) -> String {
return music.prepared()
}
play(_:)
办法回来一个要播映的 String
。你或许想知道为什么你要吃力地创立一个特殊的 Music
类型,而不是直接传递一个音符的字符串数组。这有几个好处。创立 Music
有助于建立一个词汇表,使编译器能够检查你的工作,并为将来的扩展创造一个当地。
接下来,在 Instrument
类中紧接着 play(_:)
增加以下办法:
func perform(_ music: Music) {
print(tune())
print(play(music))
}
perform(_:)
办法首要对乐器进行调音,然后一气呵成地演奏所给的音乐。你将两种办法组合在一起,构成完美的交响乐。(双关语十分有意义! :] )
就 Instrument
类的完成而言,便是这样了。现在是时分增加一些详细的乐器了。
承继
在 Playground 的底部,紧接着 Instrument
类的完成,增加以下类声明:
// 1
class Piano: Instruments {
// 是否有踏板
let hasPedals: Bool
// 2
static let whitekeys = 52
static let blackKeys = 36
// 3
init(brand: String, hasPedals: Bool = false) {
self.hasPedals = hasPedals
// 4
super.init(brand: brand)
}
// 5
override func tune() -> String {
return "Piano standard tuning for (brand)."
}
override func play(_ music: Music) -> String {
// 6
let preparedNotes = super.play(music)
return "Piano playing (preparedNotes)"
}
}
下面是工作的经过,一步步来:
- 你创立了
Piano
类作为Instrument
的一个子类。一切存储的特点和办法都被Piano
子类主动承继并可运用。 - 一切的钢琴都有完全相同数量的白键和黑键,不管其品牌如何。它们相应的特点的关联值不会动态改变,所以你把特点标记为静态类型(
static
),以明确这一点。 - 初始化器为它的
hasPedals
参数供给了一个默认值,假如你想的话,能够把它关掉。 - 在设置子类存储特点
hasPedals
之后,你运用super
关键字来调用父类初始化器。父类初始化器负责初始化承继的特点——在本例中是brand
特点。 - 你用
override
关键字覆写了从父类承继的tune()
办法的完成。这儿供给了一个tune()
的完成,它不调用fatalError()
,而是为Piano
做一些特定的工作。 - 你覆写了从父类承继的
play(_:)
办法。在这个办法中,你这次运用了super
关键字来调用Instrument
的办法,以便取得音乐的预备音符,然后在钢琴上演奏。
因为 Piano
派生自 Instrument
,你的代码的用户现已对它有了许多了解。它有一个品牌,能够调音、演奏,乃至能够扮演。
Swift 类运用一种叫做**两步初始化(two-phase-initialization)**的初始化过程,以确保在你运用它们之前一切的特点都被初始化。假如你想了解更多关于初始化的常识,请检查咱们的 Swift 初始化系列教程。
钢琴的调音和演奏是相应的,但你能够用不同的办法演奏。因而,现在是时分把踏板加进去了。
办法覆写
在 Piano
类中,在重载的 play(_:)
办法之后增加以下办法:
func play(_ music: Music, usingPedals: Bool) -> String {
let preparedNotes = super.play(music)
if hasPedals && usingPedals {
return "Play piano notes (preparedNotes) with pedals."
} else {
return "Play piano notes (preparedNotes) without pedals."
}
}
假如 usePedals
为真,并且钢琴实践上有踏板能够运用,这就重载了 play(_:)
办法来运用踏板。它没有运用 override
关键字,因为它有一个不同的参数列表(parameter list)。Swift 运用参数列表(又称签名)来决议运用哪一个。不过你需求当心重载办法,因为它们有或许形成混乱。例如,perform(_:)
办法总是会调用 play(_:)
办法,而不会调用你专门的play(_:usingPedals:)
办法。
替换 play(_:)
办法,在 Piano
中,用一个新的完成版本来调用你的新踏板:
override func play(_ music: Music) -> String {
return play(music, usingPedals: hasPedals)
}
Piano
类的完成就到此为止。是时分创立一个真实的钢琴实例了,给它调音,并在上面演奏一些十分酷的音乐。]
实例
在 Piano
类声明之后,在 Playground 的终究增加以下代码块:
// 1
let piano = Piano(brand: "Yamaha", hasPedals: true)
piano.tune()
// 2
let music = Music(notes: ["C", "G", "F"])
piano.play(music, usingPedals: false)
// 3
piano.play(music)
// 4
Piano.whitekeys
Piano.blackKeys
这便是这儿所发生的工作,一步一步来:
- 你创立一个钢琴
piano
作为Piano
类的一个实例,并调用tune()
办法对它进行调音。请注意,虽然类型(类)总是大写的,但实例总是小写的。相同,这也是常规。 - 你声明一个
Music
类的实例music
,用你的特殊重载办法在钢琴上演奏它,让你不运用踏板就能演奏歌曲。 - 你调用
Piano
类的play(_:)
的版本,假如能够的话,它总是运用踏板。 - 键数是钢琴类中的
static
类型的常量值,所以你不需求一个特定的实例来调用它们–你只需运用类名前缀。
现在你现已尝到了钢琴音乐的滋味,你能够在其中参加一些吉他独奏。
中间层笼统基类
在 Playground 的终究增加 Guitar
类的完成:
class Guitar: Instruments {
let stringGauge: String
init(brand: String, stringGauge: String = "medium") {
self.stringGauge = stringGauge
super.init(brand: brand)
}
}
这就创立了一个新的 Guitar
类,在 Instrument
基类中增加了字符串表的概念,作为一个文本String
。和 Instrument
相同,Guitar
被认为是一个笼统类型,它的 tune()
和play(_:)
办法需求在子类中被重写。这便是为什么它有时被称为中间层笼统基类(intermediate abstract base class)。
你会注意到,没有什么能够阻挠你创立一个笼统类的实例。这是真的,也是 Swift 的一个约束(缺点)。有些言语答应你特别阐明一个类是笼统的,你不能创立它的实例。
Guitar
类就到此为止——你现在能够增加一些十分酷的吉他了! 让咱们开端吧! :]
Concrete Guitars/吉他详细类型
你要创立的第一种类型的吉他是原声吉他。将 AcousticGuitar
类增加到 Playground 的末尾,紧随 Guitar
类之后:
class AcousticGuitar: Guitar {
static let numberOfStrings = 6
static let fretCount = 20
override func tune() -> String {
return "Tune (brand) acoustic with E A D G B E"
}
override func play(_ music: Music) -> String {
let preparedNotes = super.play(music)
return "Play folk tune on frets (preparedNotes)."
}
}
一切的原声吉他都有 6 根弦和 20 个琴格,所以你把相应的特点建模为 static,因为它们与一切原声吉他有关。并且它们是常量,因为它们的值永远不会随时刻改变。这个类自己不增加任何新的存储特点,所以你不需求创立一个初始化器,因为它主动承继了其父类吉他的初始化器。是时分用一个应战来测试一下这个吉他了!
应战:界说一把罗兰牌原声吉他。调音,并演奏。
let acousticGuitar = AcousticGuitar(brand: "Roland", stringGauge: "light")
acousticGuitar.tune()
acousticGuitar.play(music)
是时分制造一些噪音,播映一些响亮的音乐了。你将需求一个扩音器! :]
private
原声吉他很好,但扩音器的吉他更酷。在 Playground 的底部增加放大器类,让派对开端:
// 1
class Amplifier {
// 2
private var _voumme: Int
// 3
private(set) var isOn: Bool
init() {
isOn = false
_voumme = 0
}
// 4
func plugIn() {
isOn = true
}
func unplug() {
isOn = false
}
// 5
var volume: Int {
// 6
get {
return isOn ? _voumme : 0
}
// 7
set {
_voumme = min(max(newValue, 0), 10)
}
}
}
这儿有相当多的工作要做,所以让咱们把它分化一下:
- 你界说了
Amplifier
类。这也是一个根类,就像Instrument
相同。 - 存储特点
_volume
被标记为private
,因而它只能在Amplifier
类中被拜访,并被躲藏起来,不被外部用户发现。称号最初的下划线强调了它是一个私有的完成细节。再说一次,这只是一个常规。但遵从常规是好的。:] - 存储特点
isOn
能够被外部用户读取,但不能被写入。这是用private(set)
来完成的 -
plugIn()
和unplug()
影响isOn
的状况。 - 名为
volume
的计算特点(computed property)包装了私有存储特点_volume
。 - 假如没有电源刺进,getter 办法会将音量降为0。
- 在 setter 办法里边,音量将总是被约束在0到10之间的某个值。不能把放大器设置为 11。
拜访操控关键字 private
关于躲藏杂乱性和维护你的类不受无效修正的影响十分有用。它的花名是 “维护不变量”。不变量指的是一个操作应该一直保存的真理。
组合
现在你有了一个方便的放大器组件,是时分在电吉他中运用它了。将 ElectricGuitar
类的完成增加到 Playground 的末尾,紧接着 Amplifier
类的声明:
// 1
class ElectricGuitar: Guitar {
// 2
let amplifier: Amplifier
// 3
init(brand: String, stringGauge: String = "light", amplifier: Amplifier) {
self.amplifier = amplifier
super.init(brand: brand, stringGauge: stringGauge)
}
// 4
override func tune() -> String {
amplifier.plugIn()
amplifier.volume = 5
return "Tune (brand) electric with E A D G B E"
}
// 5
override func play(_ music: Music) -> String {
let preparedNotes = super.play(music)
return "Play solo (preparedNotes) at volume (amplifier.volume)."
}
}
一步一步来吧:
-
ElectricGuitar
是一个详细类型,它派生自笼统的、中间层基类Guitar
。 - 一个电吉他包括一个放大器。这是一种 has-a 的联系,而不是像承继那样的 is-a 联系。
- 一个自界说的初始化器,初始化一切存储的特点,然后调用超类。
- 一个合理的
tune()
办法。 - 一个合理的
play()
办法。
以类似的办法,将 BassGuitar
类声明增加到 Playground 的底部,紧接着 ElectricGuitar
类的完成:
class BassGuitar: Guitar {
let amplifier: Amplifier
init(brand: String, stringGauge: String = "heavy", amplifier: Amplifier) {
self.amplifier = amplifier
super.init(brand: brand, stringGauge: stringGauge)
}
override func tune() -> String {
amplifier.plugIn()
return "Tune (brand) electric with E A D G"
}
override func play(_ music: Music) -> String {
let preparedNotes = super.play(music)
return "Play bass line (preparedNotes) at volume (amplifier.volume)."
}
}
这就创造了一个也利用了一个(有一个)放大器的低声吉他。阶级遏止的举动。是时分进行另一次应战了!
应战:
你或许听说过,类是遵从引证语义的。这意味着持有一个类实例的变量实践上持有该实例的引证。假如你有两个具有相同引证的变量,改变一个变量的数据将改变另一个变量的数据,这实践上是同一件事。经过实例化一个放大器并在 Gibson 电吉他和 Fender 低声吉他之间同享它,来展示引证语义的效果。
let amplifier = Amplifier()
let electricGuitar = ElectricGuitar(brand: "Gibson", stringGauge: "medium", amplifier: amplifier)
electricGuitar.tune()
let bassGuitar = BassGuitar(brand: "Fender", stringGauge: "heavy", amplifier: amplifier)
bassGuitar.tune()
bassGuitar.amplifier.volume
electricGuitar.amplifier.volume
bassGuitar.amplifier.unplug()
bassGuitar.amplifier.volume
electricGuitar.amplifier.volume
bassGuitar.amplifier.plugIn()
bassGuitar.amplifier.volume
electricGuitar.amplifier.volume
多态
面向目标编程的一大优势是能够经过相同的接口运用不同的目标,而每个目标都有自己独特的行为办法。这便是多态性,意味着 “多种形式”。在 Playground 的结尾增加 Band
类的完成:
class Band {
let instruments: [Instruments] // 声明为 Instruments 乐器基类,实践存储详细子类
init(instruments: [Instruments]) {
self.instruments = instruments
}
func perform(_ music: Music) {
for instrument in instruments {
instrument.perform(music)
}
}
}
Band
类有一个乐器数组的存储特点,你能够在初始化器中设置。乐队在舞台上进行现场扮演,经过 for in
循环遍历乐器数组,为数组中的每件乐器调用 perform(_:)
办法。
现在,持续准备你的第一次摇滚音乐会。在 Playground 的底部,紧接着 Band
类的完成,增加以下代码块:
let instruments = [piano, acousticGuitar, electricGuitar, bassGuitar]
let band = Band(instruments: instruments)
band.perform(music)
你首要从你从前创立的 Instrument
类实例中界说一个 instruments
数组。然后你声明 band
目标,并用乐队初始化器配置其 instruments
特点。终究你运用 band
实例的 perform(_:)
办法使乐队进行现场音乐扮演(打印调音和演奏的结果)。
注意,虽然 instruments
数组的类型是 [Instrument]
,但每个乐器都根据其**类的类型(class type)**进行相应的扮演。这便是多态性在实践中的效果:你现在能够在现场表演中像个专家相同扮演了! :]
注意:假如你想了解更多关于类的信息,请检查咱们的 Swift 枚举、结构和类的教程。
拜访操控
你现已看到了 private
的效果,它是一种躲藏杂乱性和维护你的类不被无意中进入无效状况(即破坏不变性)的办法。Swift 更进一步,供给了四个等级的拜访操控,包括:
- **
private
:**仅在类的内部可见。 - **
fileprivate
:**可从同一文件中的任何当地看到。 - **
internal
:**在同一模块或应用程序中的任何当地都能够看到。 - **
public
:**在模块外的任何当地都能够看到。
还有其他与拜访操控有关的关键字:
- **
open
:**可在模块外任何当地运用。不只能够在模块外的任何当地运用,并且还能够从外部进行子类化或重写。 - **
final
:**不能被重写或子类化。
假如你没有指定一个类、特点或办法的拜访操控权限,它默认为 internal
。因为你通常只要一个单一的模块,这让你在开端时疏忽了拜访操控的问题。只要当你的应用程序变得更大、更杂乱,你需求考虑躲藏一些杂乱性时,你才真实需求开端忧虑这个问题。
制作一个结构
假设你想制作你自己的音乐和乐器结构。你能够经过向你的 Playground 的编译源增加界说来模仿这一点。首要,从 Playground 中删去 Music
和 Instrument
的界说。这将导致许多过错,你现在要修正这些过错。
经过进入 View/Navigators/Show Project Navigator,确保项目导航器在 Xcode 中是可见的。然后右击 Sources 文件夹,从菜单中挑选 New File。重命名文件 MusicKit.swift
并删去里边的一切内容。将其内容替换为:
// 1
final public class Music {
// 2
public let notes: [String]
public init(notes: [String]) {
self.notes = notes
}
public func prepared() -> String {
return notes.joined(separator: " ")
}
}
// 3
open class Instrument {
public let brand: String
public init(brand: String) {
self.brand = brand
}
// 4
open func tune() -> String {
fatalError("Implement this method for (brand)")
}
open func play(_ music: Music) -> String {
return music.prepared()
}
// 5
final public func perform(_ music: Music) {
print(tune())
print(play(music))
}
}
保存文件并切换回你的 Playground 的主页。这将持续像以前相同工作。这儿有一些关于你在这儿所做的工作的阐明。
-
final public
意味着它将被一切外部人员看到,但你不能对它进行子类化。 - 假如你想从外部看到它,每个存储特点、初始化器、办法都必须被标记为
public
。 -
Instrument
类被标记为public
,因为答应子类化。 - 办法也能够被标记为
open
,以答应重写。 - 办法能够被标记为
final
,所以没有人能够掩盖它们。这或许是一个有用的确保。
何去何从?
你能够下载本教程的终究 Playground,其中包括了本教程的示例代码。
你能够在咱们的《swift Apprentice》一书中阅览更多关于面向目标编程的内容,或者经过咱们的《设计形式教程》一书来进一步应战自己。
我希望你喜爱这个教程,假如你有任何问题或意见,请参加下面的论坛评论!
附录
MusicKit.swift
// 音乐
final public class Music {
public let notes: [String]
public init(notes: [String]) {
self.notes = notes
}
public func prepared() -> String {
return notes.joined(separator: " ")
}
}
// 乐器基类
open class Instrument {
public let brand: String
public init(brand: String) {
self.brand = brand
}
// Concrete classes must override this method.
open func tune() -> String {
fatalError("Implement this method for (brand)")
}
// Derived classes must call the superclass
// their own specialization.
open func play(_ music: Music) -> String {
return music.prepared()
}
final public func perform(_ music: Music) {
print(tune())
print(play(music))
}
}
Instruments
// 钢琴
class Piano: Instrument {
let hasPedals: Bool
static let whiteKeys = 52
static let blackKeys = 36
init(brand: String, hasPedals: Bool = false) {
self.hasPedals = hasPedals
super.init(brand: brand)
}
override func tune() -> String {
return "Piano standard tuning for (brand)."
}
override func play(_ music: Music) -> String {
return play(music, usingPedals: hasPedals)
}
func play(_ music: Music, usingPedals: Bool) -> String {
let preparedNotes = super.play(music)
if hasPedals && usingPedals {
return "Play piano notes (preparedNotes) with pedals."
}
else {
return "Play piano notes (preparedNotes) without pedals."
}
}
}
let piano = Piano(brand: "Yamaha", hasPedals: true)
piano.tune()
let music = Music(notes: ["C", "G", "F"])
piano.play(music, usingPedals: false)
piano.play(music)
Piano.whiteKeys
Piano.blackKeys
// 吉他
class Guitar: Instrument {
let stringGauge: String
init(brand: String, stringGauge: String = "medium") {
self.stringGauge = stringGauge
super.init(brand: brand)
}
}
// 原声吉他
class AcousticGuitar: Guitar {
static let numberOfStrings = 6
static let fretCount = 20
override func tune() -> String {
return "Tune (brand) acoustic with E A D G B E"
}
override func play(_ music: Music) -> String {
let preparedNotes = super.play(music)
return "Play folk tune on frets (preparedNotes)."
}
}
let acousticGuitar = AcousticGuitar(brand: "Roland", stringGauge: "light")
acousticGuitar.tune()
acousticGuitar.play(music)
// 放大器
class Amplifier {
private var _volume: Int
private(set) var isOn: Bool
init() {
isOn = false
_volume = 0
}
func plugIn() {
isOn = true
}
func unplug() {
isOn = false
}
var volume: Int {
get {
return isOn ? _volume : 0
}
set {
_volume = min(max(newValue, 0), 10)
}
}
}
// 电子吉他
class ElectricGuitar: Guitar {
let amplifier: Amplifier
static let numberOfStrings = 6
static let fretCount = 24
init(brand: String, stringGauge: String = "light", amplifier: Amplifier) {
self.amplifier = amplifier
super.init(brand: brand, stringGauge: stringGauge)
}
override func tune() -> String {
amplifier.plugIn()
amplifier.volume = 5
return "Tune (brand) electric with E A D G B E"
}
override func play(_ music: Music) -> String {
let preparedNotes = super.play(music)
return "Play solo (preparedNotes) at volume (amplifier.volume)."
}
}
// 贝斯吉他
class BassGuitar: Guitar {
let amplifier: Amplifier
static let numberOfStrings = 4
static let fretCount = 24
init(brand: String, stringGauge: String = "heavy", amplifier: Amplifier) {
self.amplifier = amplifier
super.init(brand: brand, stringGauge: stringGauge)
}
override func tune() -> String {
amplifier.plugIn()
return "Tune (brand) bass with E A D G"
}
override func play(_ music: Music) -> String {
let preparedNotes = super.play(music)
return "Play bass line (preparedNotes) at volume (amplifier.volume)."
}
}
// !!!: 类遵从引证语义
let amplifier = Amplifier()
let electricGuitar = ElectricGuitar(brand: "Gibson", stringGauge: "medium", amplifier: amplifier)
electricGuitar.tune()
let bassGuitar = BassGuitar(brand: "Fender", stringGauge: "heavy", amplifier: amplifier)
bassGuitar.tune()
// Notice that because of class reference semantics, the amplifier is a shared
// resource between these two guitars.
bassGuitar.amplifier.volume
electricGuitar.amplifier.volume
bassGuitar.amplifier.unplug()
bassGuitar.amplifier.volume
electricGuitar.amplifier.volume
bassGuitar.amplifier.plugIn()
bassGuitar.amplifier.volume
electricGuitar.amplifier.volume
// 乐队
final class Band {
let instruments: [Instrument]
init(instruments: [Instrument]) {
self.instruments = instruments
}
func perform(_ music: Music) {
for instrument in instruments {
instrument.perform(music)
}
}
}
// 多态类型
let instruments = [piano, acousticGuitar, electricGuitar, bassGuitar]
let band = Band(instruments: instruments)
band.perform(music)