お気に入りな技術ブログ「その Swift コード、こう書き換えてみないか」を見ていく中から脱線をしてここしばらくは 名前空間
と enum
まわりの是非についてを眺めていました。それも前回で落ち着いたところですけれど、これまでに 教えてもらったブログ にも触れておいたら視野が広がりそうな予感がするので、今回はそれを見ていく回にしてみますね。よろしくお願いします。
そんな本日は 宮城県仙台市 からの配信になります。
————————————————————————————— 熊谷さんのやさしい Swift 勉強会 #267
00:00 開始 00:40 今回の展望 02:08 列挙子を含まない列挙型 03:31 列挙型を列挙子なしで作る理由 06:14 名前空間 08:11 シングルトン 12:00 単一のエントリーポイント 15:01 ここでシングルトンを使う理由は? 17:44 先ほどの processInfo についての話 18:27 シングルトンの定義方法と特色 23:37 静的メンバーでシングルトンを実現するのはアリ? 25:26 ラッパーは闇雲に使うとなんでもアリになりそう 27:35 便利機能をまとめたユーティリティークラス 29:16 ジェネリクスによる型情報の一元化 32:00 クロージングと次回の展望 —————————————————————————————
Transcription & Summarize : 熊谷さんのやさしい Swift 勉強会 #267
今日は先週取り上げた名前空間に関する話題を引き続き見ていきます。とりあえず、田中陽花さんが執筆したSwiftに関する内容が面白そうなので、これを使って詳しく見ていこうと思います。田中さんの提案によれば、名前空間を上手く活用することで、Swiftでのコーディングがさらに効率的になるようです。
まず、数年前からサブモジュールの議論が行われていますが、まだ完全に決着がついていません。しかし、この間に規制事実が次々と確立されつつあり、自分の中でも「これで良さそうだ」という感覚が生まれています。
次に目を通すブログは、エリカさんのものです。彼女はSwift 3の時代から活発に様々な言語仕様を確立しようとしていた人物です。ブログの中で投げかけられた質問に対して、エリカさんなりの解答が提供されています。彼女の活動は多くのSwift開発者に影響を与えてきました。
エリカさんのブログから重要なポイントを取り上げます。例えば、「少ないオーバーヘッドでインスタンスを誤って生成できない」という点は非常に重要です。具体的には、オーバーヘッドが少ないということは、ソースコードの量が少なくて済むということを意味します。
Swiftにおける名前空間を使うと、インスタンス化が誤って行われるリスクを低減できます。名前空間の本来の目的は、静的メンバーをグループ化したり、シングルトンパターンを実装したりすることです。これにより、冗長なコードを排除し、クリーンなコードベースを維持することができます。
次に取り上げるのは「プロセス」の話題です。プロセスという名前空間が存在し、これを利用することで、コマンドラインのキーや値をインターフェイスとして提供することが可能になっています。
改めて名前空間に関する一連の議論を振り返ると、プロセスやシングルトンの使い方が具体的に理解できるようになりますね。プロセスという名前の番号だけが使われる場合もあります。この辺の具体的な使い方について見ていきますと、レシングルトンと非常によく似たパターンが見えてきます。
以上が、今日取り上げる内容の要点です。次回も引き続き深掘りしていこうと思います。 先ほどの意見ですが、シングルトンについての説明が非常に似ているという点で、間違いありません。シングルトンはインスタンスを複数作らず、プロセス内で唯一つのインスタンスを提供するデザインパターンです。次にコードを書いてみてイメージをつかんだ方が良いかもしれませんね。
class Singleton {
static let shared = Singleton()
private init() {}
}
この例では Singleton
クラスには唯一のインスタンス( shared
)しか存在せず、 init
メソッドは private
にすることで新しいインスタンスの生成を防いでいます。このようにするとプロジェクトで同じインスタンスをどこからでも利用できるようになります。
一方で、スタティックなプロパティやメソッドを持つストラクトを用いる方法もあります。以下のようなコードを想像してみてください。
struct Process {
static let shared = Process()
private init() {}
}
この例でも、 Process
構造体は唯一のインスタンス shared
を提供し、新しいインスタンスの生成を防いでいます。両方とも、利用者から見ると単一のエントリーポイントを持つ点が同様です。プロセスBの方も同様のクラスやストラクトを通じてアクセスする設計が考えられます。
そして、ソフトウェアにおいて唯一のインスタンスを管理するシングルトンの概念は、多くの場面で有用です。ただし、乱用するとコードが難解になりやすいため、適切な場面での使用を心掛けることが重要です。
また、プロセスをシングルトンで管理する際にも同様の原則が適用されます。インスタンス生成を特定の方法で制限し、統一されたアクセス方法を提供することで、プログラムの構造がシンプルで直感的になります。たとえば、テキストから音声を生成する機能についても、内部的にシングルトンのシンセサイザーを持つことで実装が可能です。
struct Synthesizer {
static let shared = Synthesizer()
private init() {}
func synthesize(text: String) -> String {
// 音声生成処理
return "音声データ"
}
}
このように実装することで、 Synthesizer.shared.synthesize(text:)
といった形でどこからでも同じインスタンスを利用することができます。使用者は Synthesizer
インスタンスの存在を意識せずに、音声生成機能だけを利用できる設計になります。
このようなデザインは、シングルトンがもたらす統一されたエントリーポイントの利点を活かしつつ、適切なカプセル化を行うことで、コードの再利用性や可読性を向上させることができます。 まずはこんなのを使いますね。これをイメージしたんですよ。それでも間違っているかもしれませんが、ここはサービスできました。これをイメージして、確かにシングルトンがいいねと思ったんですけど、これはイベントにしているのはイベントにできることを例として言った方が勝ちなんですかね。それならそれでいいんですけど、どうしてもやる必要があるのかなと。これはシングルトンの話で、ケアレスでいいかなという話だから、持ち出さなくていいじゃないですか。コードが使えるシングルトンの部分ですね。そんなところでちょっと引っかかっているんですよ。
コメントも見てなかったんですけど、コメントを見ても特に寄せられてはいないので、あとは何か思いついたりはしないかなという感じで解決しておきました。全然いつでも応用してもらって大丈夫なので、話は先に進めますね。次はラフアイコンに行きましょうか。とりあえずシングルトンとしてもいい感じです。ケアレスはいい感じに使えるよという話です。
では、ラフアイコンの前にもう一度シングルトンに戻りましょう。コードを見たときに、入れ物の方は置いといて、言いたいことはこの上の部分、これだと思うんですよ。下のシングルトンパターン、HTMLからプライベートインとしておきました。下のシングルトンはよくある標準的なものですね。今、標準パターンがないと昔は主流だったシングルトンの話です。
上のほうと主流のものを比べたときに、どちらがシングルトンパターンとして使いたいかというと、そもそもシングルトン自体を使いたくないと思う人もいるでしょう。けれども、シングルトンを使うとしたときに、上と下どっちで書けますかね。上もありかなという気はしますが、いろいろと気になるところもあります。
一つ自分が気にする点としては、インスタンス化ができないからインスタンスをネットで確かめたいなみたいなときは厳しいかなという点ですね。それも含めて同意です。あと、見たときに下のほうが明らかにシングルトンですよね。上はちょっと独立しちゃっているところがあります。グループ化しているといえども少し独立感があります。
そして、上だとスタティックプロパティと同等な動きを見せるので、ライフサイクルが複雑になりますね。アクセスしたときに初めてインスタンス化されるという点で、パフォーマンスが不自然になる場合があるかもしれません。初めてインスタンスを生成したときにコストがかかるからです。そのタイミングが分からないという点が影響を及ぼす可能性があります。
インスタンスを最初に生成した時点で、アクセスしやすくなることも利点の一つです。そして、このシェアードプロパティを使うことで、ライフサイクルが管理しやすくなります。解放のタイミングがアプリが終了するまで同じであるため、この点が非常に管理しやすいです。
そのため、シングルトンを使う場面もありますが、インスタンス化の管理がしやすい普通のイニシャライズ可能な型を使うことの方が多いです。シングルトンを使うより、普通の構造体を使ってインスタンスを管理した方が楽な場合が多いです。
ですので、シングルトンを使う場合でも、上の方式は基本的にはよろしくないです。ただ、可能性を模索したい場合は、上の方式を試すのもありかなとは思いますが、実際のプロダクトにリクエストを出す場合は否定するでしょう。 変数としてはどんな理由で使用するかについて考えてみましょう。否定する方法で話すこともできますが、この点についてはそこまで難しく考える必要はありません。とりあえず記憶の片隅に置いておいてもらえればOKかなと思います。
次に、ケースのない列挙型(case-less enum)を使うこともできます。シングルトンパターンを知っている方は、それがデザインパターンの一つであることも理解しているでしょう。シングルトンは、頻繁に使用するコードを一連化して簡素化するために使うことができます。
Swift では、シングルトンを使って、以前の PlaygroundSupport
や PlaygroundPaging
の代わりに PlaygroundState.runForever
を使えるという話があります。このアイデアについては、特に高速で使えるわけではないかもしれませんが、一種の便利ツールとして紹介されています。例えば、PlaygroundState
の中に runForever
や stop
メソッドなどを追加して使うことができます。
それから、UTTクラス
についても話題になりました。これは、自分だけが使うための便利ツールをまとめておくクラスのことです。ローカル環境でのちょっとした型変更やエクステンションなどを行う際に使うと便利です。アクセスコントロールをうまく使い分けて、影響がないようにするのがポイントです。
ケースのない列挙型
は、メモリレイアウトのサイズ管理などでも活用できます。例えば、構造体に収められたデータがメモリレイアウト上でどう配置されるかを管理するためにも使われます。このようにインスタンスが存在しないことを保証しつつ、名前空間として機能を提供することができます。
ジェネリクスを利用することによって型情報を名前空間に添え、それに基づいた機能を提供することができます。たとえば、関連する型情報に基づいて動作するメソッドを持つことができます。このような使い方がケースのない列挙型のユースケースとして広がっていく可能性があります。
以上で今回の内容を終了し、次回は再びSwiftのコードについてのブログの続きから見ていきたいと思います。お疲れ様でした。ありがとうございました。