【Swift】クラスの使い方。オブジェクト指向プログラミングとインスタンスについて(Swift 2.1、XCode 7.2)
オブジェクト指向プログラミングとは
Swiftはオブジェクト指向言語である。オブジェクトとは、役割ごとに分けたデータと操作の集まりのことをいう。
例えば、ハンバーガーショップでイメージしてみると、店員は「社員番号、名前、担当作業」などのデータを持ち、「注文を受ける」などの操作を持ったオブジェクト。店員は複数人いるので、社員番号、名前が異なる店員オブジェクトが沢山いる。
レジは「レジ内現金、注文受付商品、在庫情報」などのデータを持ち、「注文をキッチンに送信」などの操作を持ったオブジェクト。
オブジェクトは他のオブジェクトからの要求に対して、自分が持っているデータを使ったり、さらに他のオブジェクトから情報を貰ったりして要求に答える。例えば、店員オブジェクトはバリューセットの注文を受けると、レジオブジェクトに対して自分の社員番号、商品情報を引数に「注文をキッチンに送信」を要求する。
要求を受けたレジオブジェクトは品切れ情報を確認、受付担当者を保存して、キッチンオブジェクトに対して注文情報を引数に調理を要求する。要求を受けたキッチンオブジェクトはバリューセットオブジェクトを作り、レジオブジェクトに戻す。
このように、オブジェクトたちが互いに要求を投げ合いながら、プログラムを構成する手法をオブジェクト指向プログラミングという。
クラスとは
クラスとは、このオブジェクト指向プログラミングを実現するために、プロパティ(データ)とメソッド(操作)を集めて1つにまとめたコードのことをいう。
ただし、クラスはインスタンスというものを生成して始めてオブジェクトの役割を担うことができる。言わば、クラスは設計図、インスタンスは設計図をもとに作られた実体のようなものである。1つのクラスから複数のインスタンスを作ることができ、インスタンスごとに社員番号、名前が違う店員オブジェクトが複数いたり、ハンバーガーの種類やポテトのサイズが違うハンバーガーオブジェクトが複数いたりする。
クラスの作り方
クラスは以下のように定義する。
1 2 3 4 |
class クラス名 { プロパティ メソッド } |
上記例の社員クラスを実装すると以下のコードのようになる。しかし、以下のコードはコンパイルエラーになる。なぜなら、社員番号、名前、担当に値が設定されていないためである。Swift2.0においては、クラスのプロパティは必ず初期値を設定しなければならない。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
/* ** 社員クラス */ class Employee { let id:Int //社員番号 let name:String //名前 let tantou:String //担当 //注文を受ける func reciveOrder() -> Bool{ //とりあえず処理は省略 return true } } //エラー //error: class 'Employee' has no initializers |
そこで、以下のコードのようにプロパティの宣言と同時に値を設定すればエラーは発生しなくなる。社員番号、名前、担当はインスタンスごとに値を変える必要があるので、未設定を意味するような値を設定しておく。
1 2 3 4 5 6 7 8 9 10 11 12 |
class Employee { var id:Int = 0 //社員番号 var name:String = "無し" //名前 var tantou:String = "無し" //担当 //注文を受ける func reciveOrder() -> Bool{ //とりあえず処理は省略 return true } } |
インスタンスの作り方
次にクラスからインスタンスを生成する。「var 変数 = クラス名()」のように記述することでインスタンスが変数に代入される。そして「変数.プロパティ名 = 値」で社員番号、佐藤、ホールスタッフの値を設定する。これでインスタンスごとに異なるプロパティ値を持たせることができた。
1 2 3 4 5 6 7 8 9 10 11 |
var employee1 = Employee() employee1.id = 1054 employee1.name = "佐藤" employee1.tantou = "ホールスタッフ" var employee2 = Employee() employee2.id = 3672 employee2.name = "広田" employee2.tantou = "キッチンスタッフ" |
しかし、この方法はあまり好ましくない。理由は、プロパティ値を初期化するために何行も記述するのが煩わしいというのもあるが、初期化後に変更しない値を変数(var)で宣言しなければならないため、プロパティ値が思わぬところで変更されないよう今後のコーディングで注意する必要が出てくるためだ。
そこで、利用するのがイニシャライザというメソッドである。イニシャライザとは、インスタンスを生成するときに呼び出されるinitメソッドのことである。イニシャライザの中でプロパティ値を設定すれば、プロパティを宣言すると同時に値を代入しなくても良くなる。
例えば、以下のコードのように、init(社員番号, 名前, 担当)というメソッドを定義しておけば、社員クラスのインスタンスを生成するときに必ず社員番号, 名前, 担当を引数に与えることになる。
これにより、インスタンス生成のコードがスッキリするのと同時に、プロパティを定数(let)で宣言して安全性を高めることができるということだ。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
class Employee { let id:Int //社員番号 let name:String //名前 let tantou:String //担当 //イニシャライザ init(id:Int, name:String, tantou:String){ //引数の値をインスタンスの変数に代入する self.id = id self.name = name self.tantou = tantou } //注文を受ける func reciveOrder() -> Bool{ //とりあえず処理は省略 return true } } //社員クラスのインスタンスを生成 var employee1 = Employee(id:1054, name:"佐藤", tantou:"ホールスタッフ") var employee2 = Employee(id:3672, name:"広田", tantou:"キッチンスタッフ") |
イニシャライザを複数定義する
イニシャライザは引数を変えて複数定義できる。
例えば、先ほどの引数が3つのイニシャライザの下に、引数が2つのイニシャライザを定義したのが以下のコードである。担当を設定する必要が無い場合に2つの引数でインスタンス生成することができる。なお、イニシャライザの中から別のイニシャライザを呼ぶ場合はinitの頭にconvenienceを付ける必要がある。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
class Employee { let id:Int //社員番号 let name:String //名前 let tantou:String //担当 //イニシャライザ init(id:Int, name:String, tantou:String){ //引数の値をインスタンスの変数に代入する self.id = id self.name = name self.tantou = tantou } //イニシャライザ convenience init(id:Int, name:String){ self.init(id:id, name:name, tantou:"共通"); } //注文を受ける func reciveOrder() -> Bool{ //とりあえず処理は省略 return true } } //社員クラスのインスタンスを生成 var employee1 = Employee(id:1054, name:"佐藤", tantou:"ホールスタッフ") var employee2 = Employee(id:3672, name:"広田") |
ちなみに、以下のコードのように引数が与えられなかったときの初期値を記述しておけば、引数2つ、3つどちらでもインスタンス生成できるので、イニシャライザを複数用意する必要は無くなる。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
/* ** 社員クラス */ class Employee { let id:Int //社員番号 let name:String //名前 let tantou:String //担当 //イニシャライザ init(id:Int, name:String, tantou:String = "共通"){ //引数の値をインスタンスの変数に代入する self.id = id self.name = name self.tantou = tantou } //注文を受ける func reciveOrder() -> Bool{ //とりあえず処理は省略 return true } } //社員クラスのインスタンスを生成 var employee1 = Employee(id:1054, name:"佐藤", tantou:"ホールスタッフ") var employee2 = Employee(id:3672, name:"広田") |
レジクラスを作ると以下のコードのようになる。オブジェクトから別のオブジェクトのメソッドを呼び出すイメージをつかむのが目的なので、プロパティの使われ方やメソッド内の処理については省略している。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
/* ** レジクラス */ class Regi { let regiId:Int //レジ番号 var money:Int //レジ内現金 var zaiko:[String:Int] = [:] //在庫情報 var order:[String:Int] = [:] //注文情報 //イニシャライザ init(regiId:Int, money:Int){ //引数の値をインスタンスの変数に代入する self.regiId = regiId self.money = money } //注文をキッチンに送信 func sendOrder(orderMenu:String, number:Int, id:Int) -> Bool{ //とりあえず処理は省略 return true } } |
社員オブジェクトからレジオブジェクトのメソッドを呼び出すので、社員オブジェクトにレジオブジェクトの参照を持たせるように修正する。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
/* ** 社員クラス */ class Employee { let id:Int //社員番号 let name:String //名前 let tantou:String //担当 let regi:Regi //レジ //イニシャライザ init(id:Int, name:String, regi:Regi, tantou:String = "共通"){ //引数の値をインスタンスの変数に代入する self.id = id self.name = name self.tantou = tantou self.regi = regi } //注文を受ける func reciveOrder(orderMenu:String, number:Int) -> Bool{ //レジオブジェクトのメソッドを呼び出す。 regi.sendOrder(orderMenu, number:number, id:id) return true } } |
社員のインスタンスを生成するときにレジのインスタンスを引数に与える。これで、社員インスタンスからレジインスタンスのメソッドを呼び出せるようになった。
1 2 3 4 5 6 |
//レジのインスタンスを生成 var regi1 = Regi(regiId:1,money:100000) //社員のインスタンスを生成 var employee1 = Employee(id:1054, name:"佐藤", regi:regi1, tantou:"ホールスタッフ") |