Help us understand the problem. What is going on with this article?

【Swift】デフォルト引数を活かして堅牢なアプリケーションを作る

More than 3 years have passed since last update.

はじめに

この記事ではデフォルト引数を使って「完全コンストラクタ」を実現するという事を書いていきます。

完全コンストラクタとは

完全コンストラクタとは設計パターンの一つで、初期化時に全てのプロパティーの値をセットするという考え方です。
オブジェクト指向の話で良く出てくる話題で、シンプルな考え方なので覚えやすいし使いどころの多い考え方です。

完全コンストラクタの例

User1が完全コンストラクタを利用している例です。
初期化時に全てのプロパティーがセットされている事が分かります。

// User1が完全コンストラクタに準拠している
class User1 {
    let name: String
    init(name: String) {
        self.name = name
    }
}
let user1 = User1(name: "名前")

// User2は完全コンストラクタに準拠していない
class User2 {
    var name: String = ""
}
let user2 = User2()
user2.name = "名前"

完全コンストラクタのメリット

完全コンストラクタの大きなメリットと思われる点は下の通りです。
安全性の向上と開発者への分かりやすさが大きなメリットになります。

1. 予期しない値の変更による不具合を避けれる

インスタンスの初期化位置と離れた所での値変更、メソッドの引数でインスタンスを渡したらメソッド内でプロパティー書き換えなどが該当します。
この辺りは見つけるのが困難なので可能なら避けたい所です。

2. 値のセットのタイミングによって起こる不具合を避けられる

メソッドがプロパティーの値を使う場合、プロパティーのセット前にメソッドを呼び出すと予期しない動きをする事があります。
完全コンストラクタを使えば値のセットが終わっている事が保証されるのでこういった不具合を回避できます。

私の場合はfontをセットする前にlabel.sizeToFite()を呼び出してしまう事が多いので、初期化時にfontもセットしたいと常々思います。

class User {
    var firstName = ""
    var lastName = ""
    var fullName: String {
        return firstName + lastName
    }
}
let user = User()
print(user.fullName) // 空文字になる
user.firstName = "First"
user.lastName = "Last"

3. クラスを使う人が分かりやすい

初期化の時にプロパティーで渡すので、クラスを使う人がどのプロパティーが必須かが分かりやすいというメリットもあります。
安全性向上とは別の切り口のメリットです。

完全コンストラクタが実現しずらくなる状況

このように良いことが多い完全コンストラクタですが、プロパティーが増えてくると維持するのが大変になります。
初期化時に値を渡すので、プロパティーを1つ増やした時にはこのクラスを使っている箇所全てに影響が出ます。
都度コンストラクタを増やせば回避できるのですが、プロパティーを増やす度にコンストラクタを増やし続けるのも非常に大変です。

そういった時に役立つのがSwiftのデフォルト引数です。

*そもそもプロパティーが増えてきたらクラスの分割を考えるべきだったりするのですが、チームの方針などで分割しにくい事も有り得るので今回はその辺りは置いといて考えます。

Swiftのデフォルト引数

Swiftではメソッドがデフォルト引数を持つことができます。

func method(value1: Int = 0, value2: Int = 1, value3: Int = 2) {}
method(value2: 10)

それを使えばクラスの初期化は下のように書けます。
これならプロパティーが増えても既存のコードへの修正は最小限で済みますししプロパティーが増えても使う側は必要なものだけセットすればいいので非常に簡単です。

class User {
    var firstName: String
    var lastName: String
    var age: Int
    var prefecture: String
    var fullName: String {
        return firstName + lastName
    }
    init(firstName: String, lastName: String, age: Int = 20, prefecture: String = "東京") {
        self.firstName = firstName
        self.lastName = lastName
        self.age = age
        self.prefecture = prefecture
    }
}
// ageだけ省略
let user = User(firstName: "firstName", lastName: "lastName", prefecture: "神奈川")

まとめとこの記事を書いたきっかけ

メソッドのラベルがあるおかげで堅牢なアプリケーションが作れるという話でした。

最後にこの記事を書こうと思った発端も少し話してみます。
記事を書くきっかけになったのは、メソッドのラベルでした。

Swiftのラベルとは下の例のlabel部分です。

func method(label value: Int) {
}
method(label: 1)

昔Objective-cを使った時にラベルが非常に冗長に感じていてあまり良い仕様とは思っていませんでした。
Swiftができた時も既存のライブラリとの互換性の為にラベルを実装された事で残念に感じていました。
しかし最近になってラベルがあるおかげで好きな位置のデフォルト引数を指定でき、それのおかげでメソッドでコンストラクタを最小限にできる事を知って非常に感動しました。

この記事がきっかけでSwiftのメソッドのラベルって結構面白いと思って頂けたら幸いです。

参考URL

https://codeiq.jp/magazine/2013/10/496/
http://fukuchiharuki.me/wiki/index.php?%E8%A8%AD%E8%A8%88%E3%83%91%E3%82%BF%E3%83%BC%E3%83%B3%2F%E5%AE%8C%E5%85%A8%E3%82%B3%E3%83%B3%E3%82%B9%E3%83%88%E3%83%A9%E3%82%AF%E3%82%BF

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした