質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.35%
iOS

iOSとは、Apple製のスマートフォンであるiPhoneやタブレット端末のiPadに搭載しているオペレーションシステム(OS)です。その他にもiPod touch・Apple TVにも搭載されています。

Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

iPhone

iPhoneとは、アップル社が開発・販売しているスマートフォンです。 同社のデジタルオーディオプレーヤーiPodの機能、電話機能、インターネットやメールなどのWeb通信機能の3つをドッキングした機器です。

Q&A

1回答

1175閲覧

swiftにおけるclassメソッドとインスタンスメソッドの使い分け

mumei1

総合スコア2

iOS

iOSとは、Apple製のスマートフォンであるiPhoneやタブレット端末のiPadに搭載しているオペレーションシステム(OS)です。その他にもiPod touch・Apple TVにも搭載されています。

Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

iPhone

iPhoneとは、アップル社が開発・販売しているスマートフォンです。 同社のデジタルオーディオプレーヤーiPodの機能、電話機能、インターネットやメールなどのWeb通信機能の3つをドッキングした機器です。

0グッド

0クリップ

投稿2020/06/14 08:52

編集2020/06/14 09:01

前提・実現したいこと

クラスメソッドとインスタンスメソッドの使い分けを理解していないのですが、
使い分けの方法としてインスタンス先のメンバ変数を参照するか否かで使い分けるのがいいのでしょうか?
もし、上記の認識が正だとするとインスタンス先のメンバ変数を参照しないクラスであれば
そのクラス内の関数は全てクラスメソッドとして定義するのがベストなのでしょうか?
また、それぞれのメリットデメリットがあるなら教えてください。
よろしくお願いいたします。

該当のソースコード

// クラスメソッド class ClassMethod { class func classMethod() { // 処理内容 } }
// インスタンスメソッド class InstanceMethod { let test = "" func instanceMethod() { // 処理内容 } }

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

hoshi-takanori

2020/06/14 22:47

インスタンスのプロパティを参照しないのであれば、単なる関数にしてもいいかもしれませんね。
mumei1

2020/06/16 15:25

単なる関数とはなんでしょうか?
hoshi-takanori

2020/06/16 16:57

舌足らずですみません。クラスに所属しない関数の意味で書きました。処理の内容によりますが…。
guest

回答1

0

もしかすると「インスタンス先のメンバ変数を参照するか否か」みたいな実装的なものよりは、境界が曖昧になるのですけれど「その機能がインスタンスに着目したものか、型に関するものか」みたいな意味的なドメイン・文脈で判断するのがよかったりするかもしれません。

あくまでも個人的な感覚なので、ひとつの参考として捉えてもらえると幸いです。

例えば・・・

例えが少し無理やりになるのですけれど、ボードゲームのプレイヤーを実装していたとして、ユーザーがマニュアル操作するプレイヤー Human と、コンピューターが一様なランダムで手を打つ Random とがあったとします。

クラスの構造

どちらのプレイヤーであるかは実行時のインスタンスによって切り替えられるとすると、次のようにプロトコル Player を作ってそれを両者に準拠させるのが良いと思います。

swift

1protocol Player { 2} 3 4class Human : Player { 5} 6 7class Random : Player { 8}

このとき、プレイヤーが "Manual" 操作なのか "Auto" 操作なのかを示す mode プロパティーと、次の一手を決める move メソッドを機能として備えることを考えます。

kind プロパティー

まず、操作モードを示す mode プロパティーについて考えると、その種類はインスタンスではなく肩によって決まってくるので、インスタンス毎にプロパティーで保持しておく必要はありません。

そうすると、クラスプロパティーを使って表現することもできることになるのですけれど、そうしたときの様子に少し無理が出てくるように感じます。この例は際どいところで主観も大きいですが。

swift

1protocol Player { 2 3 static var mode: String { get } 4} 5 6class Human : Player { 7 8 class var mode: String { 9 10 return "Manual" 11 } 12} 13 14class Random : Player { 15 16 class var mode: String { 17 18 return "Auto" 19 } 20}

こうしたときに、どちらのプレイヤーでもインスタンス化して扱えるように Player 型の変数を用意したときに、その操作モードを取得するコードを書いてみます。

swift

1let player: Player = Manual() 2 3print(type(of: player).mode)

この print 文に着目してコードを読んでみると、流れとしては「プレイヤーの型情報を取得して、型情報の操作モードを参照する」みたいな流れになるように感じます。それはそれで間違いはないと思うのですけれど、ここは単純に「プレイヤーの操作モードを参照する」で目的を明確に達成できるので、そういう意味でインスタンスに着目して、たとえ他のプロパティーに依存していなくてもインスタンスプロパティーで実装すると、明瞭なコードになるような気がします。

swift

1protocol Player { 2 3 var mode: String { get } 4} 5 6class Human : Player { 7 8 var mode: String { 9 10 return "Manual" 11 } 12} 13 14class Random : Player { 15 16 var mode: String { 17 18 return "Auto" 19 } 20}

swift

1let player: Player = Manual() 2 3print(player.mode)
move メソッド

次の一手を返す move メソッドは、もう少し明瞭な違いが見えてきます。

今回の例で想定している move メソッドは、人が操作する Human クラスであれば、判断をユーザーに丸投げするので、内部プロパティーに依存しないメソッドになります。そして、一様にランダムで手を決める Random クラスは、それが純粋にランダムに頼るのであれば内部プロパティーで調整する必要がないため、プロパティーに依存しない作りにすることができます。

swift

1protocol Player { 2 3 static func move() -> Location { get } 4} 5 6class Human : Player { 7 8 class func move() -> Location { 9 10 return ・・・ 11 } 12} 13 14class Random : Player { 15 16 class func move() -> Location { 17 18 return ・・・ 19 } 20}

ただ、このようにすると先ほどの例と似ているのですけれど、実際に使おうとしたときに「型に対して次の手を求める」みたいな、少し違和感のあるコードになってしまう気がします。

swift

1let player: Player = Manual() 2let location = type(of: player).move()

また、今回の例は単純なので簡単に予測できるとは思うのですけれど、将来、パラメーターで難易度調整ができたりするプレイヤークラスが登場したりしたときに、インスタンスメソッドでないと実現できなくなったりします。

そういった観点で「インスタンスとして扱うプレイヤー型としての機能であるなら、インスタンスメソッドとして実装する」のが、コードが無理なく書けて、かつ柔軟性の高いコードになるように感じます。

標準ライブラリーでの使われ方

自分の価値観で述べるだけだと参考にならないかもしれないので、標準ライブラリーでクラスメソッド(静的メソッド)が使われる場面も幾つか紹介しておきますね。

1) 自身のインスタンスを生成する

標準ライブラリーで比較的よく目にするのが、自分自身の型を生成するためのメソッドを導入するのにクラスメソッドやクラスプロパティーが使われる場面があります。

たとえば、UIKit の機能になりますけれど、UIColor に備えられている標準的な色のインスタンスはクラスプロパティーで実装されています。

swift

1class UIColor { 2 3 class var yellow: UIColor { 4 5 return ・・・ 6 } 7}

これは、何らかの色を表現するインスタンスの機能ではなく、UIColor という型で表現できる範囲での黄色という意味合いと捉えると理解しやすくなる気がします。コードで書くと次のように「UIColor の黄色」と読めて明瞭です。

swift

1let color = UIColor.yellow

ちなみにかなり無理やりな余談になるのですけれど、これを無理してインスタンスプロパティーにするとものすごい破綻の仕方をしてくれます。

swift

1class UIColor { 2 3 var yellow: UIColor { 4 5 return ・・・ 6 } 7}

こうしたときには、ダミーの色を用意しないといけなくなり、しかもそれが極めてコードを難読にします。

swift

1let color = UIColor(red: 1, green: 0, blue: 0, alpha: 0).yellow

これと逆に考えて良いのかはわかりませんけれど、仮に逆に考えて良いとしたら、意味的にインスタンスが持つべきメソッドを、プロパティーに依存しないからといってクラスメソッドを使って実装してしまうと、もしかするとこの例みたいに大きな破綻を生んでいたりするかもわかりません。

仮説なので真偽は定かではないですけれど、そんな感じで「意味」に焦点を当ててどちらに実装するかを考えるのは、それなりの価値はある行為のような気がします。

また、この「自身の型のインスタンスを生成するのに、クラスプロパティーを使う」方法は、その型のインスタンスを取ることがわかっている場面で、効果をはっきしてくれたりします。

今回の例では、yellow をクラスプロパティーで定義することで「UIColoryellow」という意味を持ってくれるので、たとえば viewbackgroundColor プロパティーが UIColor 型なのであれば、それで表現できる色を取ることが明確なので、次のように型名を省略して色を指定できるという書き方も可能になります。

swift

1view.backgroundColor = .yellow

こういうところからも「意味」に着目していることの一つの現れなのかなって感じたりします。

2) 型が表現する文脈での機能
文字列にみる静的メソッド

先ほどの「意味」に着目した話と同じ用例になると思うのですけれど、演算子の定義にも静的メソッド(≒クラスメソッド)が使われています。

swift

1struct String { 2 3 static func + (lhs: String, rhs: String) -> String { 4 5 return ・・・ 6 } 7}

これは、もちろんプロパティーに依存しないからという理由でも説明はつきますけれど、たとえば「数値としての足し算(加算)」「文字列としての足し算(連結)」みたいに、それが対象とする型(ドメイン)によって動きが違う、同じ名前でもドメインごとに違うもの、みたいな感覚で捉えても、しっくりくるように思います。

浮動小数点数にみる静的プロパティー

他にも例えば、倍制度浮動小数点数での円周率を表現するのにも静的プロパティーが使われています。

swift

1struct Double { 2 3 static var pi: Double { 4 5 return ・・・・ 6 } 7}

こちらは先ほどの UIColor の例と同じで「倍制度浮動小数点数で表現できる値の円周率」という意味合いと捉えられます。特徴なども UIColor と同じなので、例えば、2.0 に対して .pi を掛け合わせれば、それだけでその文脈に適した精度の円周率が使われるという利点があります。

swift

1let answer = 2.0 * .pi

はっきりとした回答を持っていないため、抽象的でわかりにくい回答になってしまっているとは思います。確証がないため自論になっている可能性も否定はできないですけれど「インスタンス先のメンバ変数を参照しないのであれば、クラスメソッドを使えば良い」とは言い切れなそうな感じが伝わってもらえたら本望です。

クラスメソッド(静的メソッド)ならではの特徴があったり、どちらかにすることでコードの意味合い(読め方)が変わってきて読みやすくなったり読みにくくなったりする、そんなあたりも「それぞれのメリット・デメリット」のうちの一例として紹介させてもらったつもりです。

こんな回答が何かのヒントになってくれると嬉しいのですが、いかがでしょうか。

投稿2020/06/16 17:24

TomohiroKumagai

総合スコア441

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

TomohiroKumagai

2020/06/16 17:34

コメントにあった「単なる関数(フリーな関数)」というのも良い例で、これについては Swift API デザインガイドライン (https://swift.org/documentation/api-design-guidelines/) の "Conventions" セクションに明確な推奨例が記されていたりします。 その理由のひとつに「明確な所属先がないとき」みたいな例があるので、所属がないからプロパティーに依存していないとも取れますし、今回の回答で綴らせてもらったみたいに「どこに所属するか」に主軸を置いて配備先を選んでみるのも良い、という解釈に繋げても良いかもしれません。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問