swiftでAbstract Classを表現しようとして、protocol extensionで実現できないかと検討したが断念した

## 目的
いくつかのclassが複数の同じ変数を持っていて（例えば、createdAt、updatedAt等）、
また、その変数を利用した同様の(ちょっとだけ違う)メソッドを持っていて、
これらの変数やメソッドを引き上げたいな、というのが目的でした。

### 目的の理由
- 同様の処理がコピペに違い状態で、各classに分散していると、変更を複数箇所に適用しなければならないから
- 変更に弱いというか、記述漏れも発生しそうで怖いから
- 同じ処理を書くのが面倒だから


## 環境
- swift 4.0
- Xcode9.1
- Realm 3.0.2

## 元々の状態（before/after のbefore）
```RealmObjectA.swift
import RealmSwift
// realmに格納される propertyA というIntを保存するclass
class RealmObjectA: Object {
    @objc dynamic var propertyA = 0
    @objc dynamic var createdAt = Int(Date().timeIntervalSince1970)
    @objc dynamic var updatedAt = Int(Date().timeIntervalSince1970)
    @objc dynamic var deleteFlg = false

    // propertyAとdeleteFlgを更新すると同時にupdatedAtを現時刻に更新
    func setAttributes(propertyA:Int, deleteFlg:Bool = false){
        self.propertyA = propertyA
        self.deleteFlg = deleteFlg
        self.updatedAt = Int(Date().timeIntervalSince1970)
    }
}
```
```RealmObjectB.swift
import RealmSwift
// realmに格納される propertyB というStringを保存するclass
class RealmObjectB: Object {
    @objc dynamic var propertyB = ""
    @objc dynamic var createdAt = Int(Date().timeIntervalSince1970)
    @objc dynamic var updatedAt = Int(Date().timeIntervalSince1970)
    @objc dynamic var deleteFlg = false

    // propertyBとdeleteFlgを更新すると同時にupdatedAtを現時刻に更新
    func setAttributes(propertyB:String, deleteFlg:Bool = false){
        self.propertyB = propertyB
        self.deleteFlg = deleteFlg
        self.updatedAt = Int(Date().timeIntervalSince1970)
    }
}
```

このRealmObjectがA〜Jまで10個存在するような状態でした。

これを一度、同様の処理をスーパークラスへ持たせるように変更しました。
## クラスの継承を利用した変更（before/after の after その１）

```RealmStoredObject.swift
import RealmSwift
// realmに格納されるobjectの親クラス
class RealmStoredObject: Object {
    @objc dynamic var createdAt = Int(Date().timeIntervalSince1970)
    @objc dynamic var updatedAt = Int(Date().timeIntervalSince1970)
    @objc dynamic var deleteFlg = false

    func setAttributes(deleteFlg:Bool = false){
        self.deleteFlg = deleteFlg
        self.updatedAt = Int(Date().timeIntervalSince1970)
    }
}
```
```RealmObjectA.swift
// realmに格納される propertyA というIntを保存するclass
class RealmObjectA: realmStoredObject {
    @objc dynamic var propertyA = 0

    func setAttributes(propertyA: Int, deleteFlg:Bool = false){
        super.setAttributes(deleteFlg: deleteFlg)
        self.propertyA = propertyA
    }
}
```
```RealmObjectB.swift
// realmに格納される propertyB というStringを保存するclass
class RealmObjectB: realmStoredObject {
    @objc dynamic var propertyB = ""

    func setAttributes(propertyB: String, deleteFlg:Bool = false){
        super.setAttributes(deleteFlg: deleteFlg)
        self.propertyB = propertyB
    }
}
```

realmObjectAとrealmObjectBがさっぱりしました。
ここにはsetAttributesというメソッドしか記述していませんが、
実際にはAPI連携のために各変数をjsonパラメータ化するメソッドなど
いくつものメソッドがあったので、変数が減ると一気にさっぱりしました。
この時はとても気分が良かったです。

変数引き上げの理由の再掲

```
- 同様の処理がコピペに違い状態で、各classに分散していると、変更を複数箇所に適用しなければならないから
- 変更に弱いというか、記述漏れも発生しそうで怖いから
- 同じ処理を書くのが面倒だから
```

### after その１で解決出来たこと
- 冗長になる処理を一つのclassのみに集約でき、変更があってもそのclassを修正すればよい
- 記述漏れも発生しない（たとえば「realmObjectBでだけupdatedAtを更新してない！」が無い）
- 同じ処理を書く面倒から開放された


### after その１の問題点
1.  親クラスであるrealmStoredObjectをインスタンス化できてしまう
2.  よく分からないけど、swiftっぽくないのでは？と思えた


#### 1. 親クラスであるrealmStoredObjectをインスタンス化できてしまう について

realmStoredObjectはあくまで、realmに格納されるclassの共通部分だけを集約した抽象的なclassであり、インスタンス化させるものではない。本当ならインスタンス化を禁止したく

```RealmStoredObject.swift
abstract class RealmStoredObject: Object {
}
```
のようにしたい所なのですが、swiftにabstract classの概念はなく、抽象クラスは実現できません。

そのため、

```
let realmStoredObject = RealmStoredObject()
```
のように宣言するとインスタンス化できてしまい、最悪この本来存在しないobjectをrealmに格納してしまう、なんてことにも成り得るかもしれません。


#### 2.  よく分からないけど、swiftっぽくないのでは？と思えた について

**そもそも何故swiftにはabstract classが用意されていないのでしょうか。**
未熟な自分にはその理由を察することはできませんでしたが、
用意していないにも理由があり、
「swiftの哲学が抽象クラスを作らせたくない」ということなのでしょう。

なので、別の方法を模索してみることにしました。


## protocol & protocol extensionでの実現妄想（before/after の after その２）

- 「classよりできるだけstruct」（今回はrealm objectを継承するため無理ですが）
- 「継承よりもprotocol」

swiftといえば、そんなイメージがなんとなくあります。
そのためスーパークラスによる抽象化ではなく、
振る舞いを定義できるprotocolと、
その振る舞いにデフォルトの動きを定義できるprotocol extensionで
実現できないかを検討してみることにしました。

変数引き上げの理由の再々掲

```
- 同様の処理がコピペに違い状態で、各classに分散していると、変更を複数箇所に適用しなければならないから
- 変更に弱いというか、記述漏れも発生しそうで怖いから
- 同じ処理を書くのが面倒だから
```

それでは、protocolでswiftyに書いてみようと思います。

```RealmStoredProtocol.swift
import SwiftyJSON

protocol RealmStoredProtocol {
    var createdAt    :Int  { get set }
    var updatedAt    :Int  { get set }
    var deleteFlg    :Bool { get set }
    
    func setAttributes(measuredOn: Date, deleteFlg:Bool)
}

extension RealmStoredProtocol {
    mutating func setAttributes(deleteFlg:Bool = false){
        self.deleteFlg = deleteFlg
        self.createdAt = Int(Date().timeIntervalSince1970)
        self.updatedAt = Int(Date().timeIntervalSince1970)
    }
}
```
protocol と protocol extensionはこんな感じになりそうですね。

ではRealmObjectAはどうなるでしょうか。

```RealmObjectA.swift
class RealmObjectA: Object, RealmStoredProtocol {
    @objc dynamic var propertyA = 0
    @objc dynamic var createdAt = Int(Date().timeIntervalSince1970)
    @objc dynamic var updatedAt = Int(Date().timeIntervalSince1970)
    @objc dynamic var deleteFlg = false

    func setAttributes(measuredOn: Date, propertyA :Int, deleteFlg:Bool = false) {
        // ↓extension のデフォルト実装が呼べない…
        setAttributes(measuredOn: measuredOn, deleteFlg: deleteFlg)
        self.propertyA = propertyA
    }

    
```

### すでに発生した問題点
#### 1. 変数は引き上げられないので、全部定義しなきゃだめ

protocolでデフォルトを定義したコンピューテッドプロパティ（値を保持せず、既に存在する値を引っ張ってくるプロパティ）は
準拠する側（今回のRealmObjectA）で定義する必要はありません。

```SomeProtocol.swift
protocol SomeProtocol {
  var defaultNum :Int { get }
}

extension SomeProtocol {
  // SomeProtocolに準拠するclassでは定義しなくてもdefaultNumが使える。
  var defaultNum :Int {
    return 1
  }
}

```

ただし、今回はrealm objectであることもあり、ストアドプロパティ（値を保持するプロパティ）であるため、この方法は使えません。
とはいえ、protocolに準拠するためには、それらの変数を定義しないとコンパイルエラーになってくれるので、
定義漏れの心配は無いため、とりあえず良しとしましょう。


#### 2. **protocol extensionで実装したメソッドはoverrideできない**

これが困っていて、今回のsetAttributesや、その他定義しているメソッドでは
共通しない変数によって追加の動作が存在しています。
（正確にはsetAttributesの場合は引数が変わるため、overrideではなくオーバーロードして、protocol extension側のsetAttributesを呼び、処理を追加、みたいなことをしたいのですが…）


## 今回は素直に継承でよいのでは…？

結局良い方法にたどり着けず、

- ストアドプロパティの実装を共有したい
- サブクラス側でデフォルト実装に処理を足したい

自分の中では、この場合は継承の方が適しているのかな、という結論に終わりました。
「いやいや、何を言っとるんやお前は。こうすればええやん」というご指摘があれば、随時大募集中です。


少々歯切れの悪い状態で申し訳ないですが、ひとまず記事はここまでです。
まだまだ師走も始まったばかりですが、
みなさま素敵なクリスマスシーズンをお過ごしください。