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

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

新規登録して質問してみよう
ただいま回答率
85.47%
Swift Playground

Swift Playgroundは、Swiftをインタラクティブに習得できるiPad向けのアプリケーション。コーディングの知識は一切必要なく、Swift Playgrounds上でプログラミングしたコードによりドローン・ロボットを自在に動かすことが可能です。

継承

継承(インヘリタンス)はオブジェクト指向プログラミングに存在するシステムです。継承はオブジェクトが各自定義する必要をなくし、継承元のオブジェクトで定義されている内容を引き継ぎます。

Swift

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

コードレビュー

コードレビューは、ソフトウェア開発の一工程で、 ソースコードの検査を行い、開発工程で見過ごされた誤りを検出する事で、 ソフトウェア品質を高めるためのものです。

Q&A

解決済

2回答

1990閲覧

Swift overrideの多用を解消する

uhsi

総合スコア57

Swift Playground

Swift Playgroundは、Swiftをインタラクティブに習得できるiPad向けのアプリケーション。コーディングの知識は一切必要なく、Swift Playgrounds上でプログラミングしたコードによりドローン・ロボットを自在に動かすことが可能です。

継承

継承(インヘリタンス)はオブジェクト指向プログラミングに存在するシステムです。継承はオブジェクトが各自定義する必要をなくし、継承元のオブジェクトで定義されている内容を引き継ぎます。

Swift

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

コードレビュー

コードレビューは、ソフトウェア開発の一工程で、 ソースコードの検査を行い、開発工程で見過ごされた誤りを検出する事で、 ソフトウェア品質を高めるためのものです。

0グッド

0クリップ

投稿2020/06/22 20:58

編集2020/06/23 12:04

実現したいこと

ある処理に対してどのクラスのインスタンスを渡すかによって異なる動作をするようにしたい
例:ボードゲームのオンライン対戦とコンピューター対戦を同じコードで行う

試したこと

Base.classを作りA.class,B.classはBaseを継承し、変更が必要なメソッドをoverrideする

問題点

overrideが増える、Base.classに空のメソッドを作る必要がある、overrideを必須にすることができない

実装したいこと

・ベースとなるクラスまたはプロトコルなどが共通のメソッドを持つことができ、そのメソッドからベースクラスが持つ変数を参照することができる
・サブクラスで実装しなければならないメソッドを指定できる

現在のコード

Swift

1class Base { 2 3 var isFirstTurn:Bool! 4 5 func setListener(result:(Int) -> Void){} 6 7 func setBordData(){ 8 // do something 9 } 10 11} 12 13class OnlineGame:Base { 14 override func setListener(result:(Int) -> Void){ 15 //... received signal 16 result(signal) 17 } 18} 19 20class COMGame:Base { 21 override func setListener(result:(Int) -> Void){ 22 //... received signal 23 result(comMove) 24 } 25 26 func comMove() -> Int { 27 return //... think next move 28 } 29} 30 31class GameView :UIViewController { 32 let helper:Base! 33 34 // in a function 35 print("isFirstTurn:(helper.isFirstTurn)") 36 37 helper.setListener({ 38 result in 39 // do something 40 }) 41 // in a function 42}

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

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

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

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

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

guest

回答2

0

プロトコル指向にしてみたらどうですか


説明は得意じゃないので適当ですが、こんなんどうでしょうか?
パタンは色々あるので、もっといい説明がごろごろ転がってるので調べてみて

swift

1 2protocol Beep { 3 4 var value: Int { get set } 5 6 func beep() 7} 8 9extension Beep { 10 func beep() { 11 print("Beeep") 12 } 13} 14 15class Base { 16 17 var value: Int = 5 18 19} 20 21class Hoge: Base, Beep { 22 23} 24 25class Fuga: Base, Beep { 26 27 func beep() { 28 print("fugaBeeep") 29 } 30} 31 32var beep1: Beep = Hoge() 33 34var beep: Beep = Hoge() 35 36beep.beep() // => Beeep 37 38print(beep.value) // => 5 39 40beep = Fuga() 41 42beep.beep() // => fugaBeeep 43 44print(beep.value) // => 5 45 46

protocolにプロパティを宣言して、プロトコルに従ってクラスに実装するだけですけど。
ご自身で調べられてはいかがですか?

投稿2020/06/22 21:25

編集2020/06/23 14:16
退会済みユーザー

退会済みユーザー

総合スコア0

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

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

uhsi

2020/06/23 07:21

プロトコルにすると同じコードに異なるインスタンスを渡すことができないと思うのですが
uhsi

2020/06/23 07:57

また、共通の処理を書くことができなくなります
uhsi

2020/06/23 08:29

ありがとうございます。extentionを使うのですね。助かりました。
uhsi

2020/06/23 12:05

補足しました。変数の扱い方を教えてください。
guest

0

ベストアンサー

今回のような場面ではプロトコルを使って表現するのが一般的かもしれません。プロトコルの構造も複雑なので、今回の場面で使えるかもしれない基本的なところを紹介してみます。

プロトコルでインターフェイスを規程

たとえば Player というプロトコルを作成して、そこに必要な機能を規定します。

swift

1protocol Player { 2 3 func nextMove() -> Location 4}

これを、たとえば OnlinePlayerComputerPlayer に適用すると、それらの型ではプロトコルで規定した機能の実装を必須にすることができます。

swift

1class OnlinePlayer : Player { 2 3 func nextMove() -> Location { 4 5 return ・・・ 6 } 7} 8 9class ComputerPlayer : Player { 10 11 12 func nextMove() -> Location { 13 14 return ・・・ 15 } 16}

プロトコル型でインスタンスを表現

このとき、共通するプロトコル Player を基底クラスのように Player 型として使うことで、どちらのインスタンスでも取り扱えるようになります。

swift

1let player: Player = ComputerPlayer()

共通機能(既定の実装)を持たせたい場合

もし OnlinePlayerComputerPlayer とで共通して使いたい機能があるようでしたら、Player プロトコルに「プロトコル拡張」を使って機能を実装しておくことも可能です。

swift

1extension Player { 2 3 func nextMove() -> Location { 4 5 return ・・・ 6 } 7}

特定の条件だけに共通機能を持たせたいようなときは、たとえば、手動操作のプレイヤーに限って何かをしたいときは ManualControl プロトコルを作成して、自動操縦なプレイヤーにだけ適用することも可能です。

swift

1protocol ManualControl { 2 3} 4 5class ComputerPlayer : Player, ManualControl { 6 7}

このようにすると、プロトコル拡張を行うときに条件を指定して、より柔軟な既定の実装を行えます。

swift

1extension Player : where Self : ManualControl { 2 3 func nextMove() -> Location { 4 5 return ・・・ 6 } 7}

このように、性質に合わせたプロトコルを並列に加えていきながら、条件を絞ってプロトコル拡張を行うことで、まるで Player を既定クラスのように使いながら、クラス継承を重ねるような系統分けも行えます。

プロパティーについて

プロパティーは、プロトコルに実装することは extension を使ってもできないので、プロトコルに存在だけ宣言することで必須にして、型に都度実装する必要があります。

swift

1protocol Player { 2 3 var cards: [Card] { get set } 4 var level: Int { get } 5} 6 7class OnlinePlayer : Player { 8 9 var cards: [Card] 10 var level: Int 11} 12 13class ComputerPlayer : Player { 14 15 var cards: [Card] 16 let level: Int = 1 17}

たとえばこのような記述が必要になるので、慣れないうちは手間が増えて複雑に感じられるかもしれないですけれど、プロパティー定義の記述自体はシンプルですし、プロトコルで実装が義務付けられているのも手伝って、型を定義するときに実装し忘れることもない(忘れるとコンパイラーに指摘される)ため、思いのほか負担なく実装することができます。

プロパティーの実装を型に定義しないといけないですけれど、存在自体は Player プロトコルに規定されているので、たとえば Player プロトコルを拡張して既定の実装を作るときには「プロパティーがあること前提で」コードを記述できるので、まるで Player にプロパティーが実装されているかのようにコードをかけます。

swift

1extension Palyer { 2 3 func drawCard() -> Card? { 4 5 return cards.randomElement() 6 } 7}

もし、何も実装しなければ特定の値を取得できるようにしたいときには、既定の実装で計算型プロパティー (Computed Property) を実装してあげることは可能です。

swift

1extension Player { 2 3 var level: Int { 4 5 get { 6 7 return 1 8 } 9 } 10}

ただし、値を読み書きするための記憶領域をプロパティーに持たせる (Stored Property) ことは、プロトコルではできないので、そういったものは必ず、型に直接実装する必要があります。

質問者の「実装したいこと」に対する補足

オブジェクト指向と違って、特にプロパティーの実装位置が『親が持つか、子が持つか』的な大きく違う点があるので感覚的に違和感を感じるかもしれませんけれど、これまでに紹介した事柄を使って、質問者 ushi さんの挙げたコードを書き換えてみると、次のようになりそうです。

swift

1protocol Base { 2 3 // 変数の実態は持てないが、存在する前提で参照できる。 4 var isFirstTurn: Bool! { get } 5 6 // 適用先で実装しなければならないメソッドを指定できる。 7 func setListener(result: (Int) -> Void) 8} 9 10extension Base { 11 12 // プロトコル拡張で共通のメソッドを実装できる。 13 func setBoardData() { 14 15 // この中で、存在するはずのプロパティーを参照できる。 16 if isFirstTurn { 17 18 } 19 20 // do something 21 } 22} 23 24class OnlineGame: Base { 25 26 // プロトコルが要求するプロパティーの実装が必要。 27 var isFirstTurn: Bool! = false 28 29 // プロトコルが要求するメソッドの実装が必要。 30 func setListener(result:(Int) -> Void) { 31 //... received signal 32 result(signal) 33 } 34} 35 36class COMGame: Base { 37 38 // プロトコルが要求するプロパティーの実装が必要。 39 var isFirstTurn: Bool! = false 40 41 // プロトコルが要求するメソッドの実装が必要。 42 func setListener(result: (Int) -> Void){ 43 44 //... received signal 45 result(signal) 46 } 47 48 func comMove() -> Int { 49 50 //... think next move 51 return next 52 } 53}

プロパティーの初期化を Base ではなく各型が担わないといけないところもプロトコルを使った場合の特徴的なところです。この辺りが吉と出るか凶と出るかは状況によると思いますけれど、場合によってはプロトコルで、イニシャライザー init(isFirstTurn:) を記載して実装必須にしておいて、型の設計時ではなくインスタンスを作るときに外側から適切な isFirstTurn の値を設定する道筋を作ってみるのも悪くない選択肢のひとつに思います。

投稿2020/06/23 08:30

編集2020/06/23 14:02
TomohiroKumagai

総合スコア441

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

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

uhsi

2020/06/23 08:51

詳しいご説明をありがとうございます。以前まではBase.classを使っていたので変数を容易に扱うことができたのですが、protocolで宣言した変数をA.classなどから使用することができません。変数についてもご説明をお願いできないでしょうか。
TomohiroKumagai

2020/06/23 09:14

変数を使うのですね。プロトコルの場合「プロパティーの実装を必須」とすることまではできても、値を保存する変数を extension 等で実装することはできないので、変数は型に必ず実装する必要があります。オブジェクト指向と比べれば手間になってしまうのですけど、プロトコルに必要なプロパティーを記載(宣言)だけしておいて、型を定義する際にそれを実装する方法でやってみると、どうでしょうか。
TomohiroKumagai

2020/06/23 09:19

プロパティーをプロトコルに宣言しておけば、プロトコル拡張でプログラムを組むときには「あるものとして」記載できるので、コーディング上の支障はないと思われます。プロトコルを使って具体的に型を実装する際に、プロパティーを実装しないといけないことが最初のうちは二度手間のように感じられるかもしれないですけれど、慣れてくるとむしろ妥当に思えてくる⋯かもしれません。
退会済みユーザー

退会済みユーザー

2020/06/23 09:28 編集

プロトコルに変数を宣言し、基底クラスに変数を実装して継承するのはどうでしょうか?
uhsi

2020/06/23 11:21

申し訳ないのですが、理解できないのでそれぞれ説明していただいた方法の例を追加で記載していただけますでしょうか。
uhsi

2020/06/23 12:06

補足しました。
TomohiroKumagai

2020/06/23 14:03

回答内に「プロパティーについて」以降を補足しました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.47%

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

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

質問する

関連した質問