読者です 読者をやめる 読者になる 読者になる

騒音のない世界 BLOG

コンピュータの話と、音楽の話

新曲「終わりのない物語」出しました

クリーントーンのギターが淡々とつぶやくようにシンプルなメロディを弾きながら広い世界を旅していくような感じで、素朴で暖かい曲になったと思います。

ぜひ聴いてください。

過去の曲もぜひ。

1~2ヶ月に1曲ぐらいのペースで上げてます。 更新情報など気になったら各種SNSのフォローお願いします。

ポリモーフィズムをもっと理解する

ポリモーフィズムオブジェクト指向の3本柱として「継承」「カプセル化」と共に語られることが多いですが、サブクラス化してオーバーライドして…というのはポリモーフィズムの一面にすぎません。この記事ではポリモーフィズムとは何なのかを説明すると共に3種類のポリモーフィズムを取り上げ、「ポリモーフィズム」という言葉を様々な角度から見ていきたいと思います。対象読者としては全くの初学者というよりも何となくポリモーフィズムは知ってて使ったりもするけどちゃんと説明できないとか、そういう人を考えています。

ポリモーフィズムを一言で説明する

ポリモーフィズムとは、一言で説明するとどういう概念なのでしょうか。日本語訳としては、オブジェクト指向の文脈だと「多態性」、関数型の文脈だと「多相性」が多い印象ですが、どちらもピンとくる言葉ではありません。そこでまずは言葉を分解して読み解いてみたいと思います。

ポリモーフィズム(Polymorphism)という言葉はポリ(Poly)とモーフィズム(Morphism)にわけられます。ポリは「複数の」、モーフィズムは「形状」と置き換えられそうです。つまりポリモーフィズムとは「複数の形状」と読み取れます。

まだ抽象的で掴みどころがないですね。実際ポリモーフィズムという言葉自体は結構抽象的で、後ほどご紹介しますが生物学などでも用いられる幅広い言葉のようです。そこで、もう少しプログラミング的な意味に注目していこうかと思います。Wikipediaの冒頭の説明がとてもわかりやすいです。

polymorphism is the provision of a single interface to entities of different types. Polymorphism (computer science) - Wikipedia

短くすると「Single interface for different types」とでも言うのでしょうか。つまりプログラミングにおけるポリモーフィズムとは、複数の異なる型に対して共通のインタフェースを提供することです。

3種類のポリモーフィズム

ポリモーフィズムには大きく分けて3種類あります。それぞれ見ていきます。ポリモーフィックな部分だけサンプルを軽く書いておきますので雰囲気だけ感じて頂ければと思います。

Ad hoc polymorphism

いくつかの限定された型に対して共通のインタフェースを提供するポリモーフィズムです。実装上は関数のオーバーロードによって実現されます。同じ名前の関数に対して、引数や戻り値の型を変更したものを用意します。

関数オーバーロードポリモーフィズムであるというのは少し意外に感じられる方もおられるのではないでしょうか。しかしこれも確かに「複数の異なる型に対して共通のインタフェースを提供」しています。

// DogとCatに対してtouchという共通インタフェースを提供する
class Breeder {
    func touch(_ dog: Dog) {
        dog.say()
    }
    func touch(_ cat: Cat) {
        cat.say()
    }
}

let breeder = Breeder()
breeder.touch(Dog()) // bowwow
breeder.touch(Cat()) // meow

Parametric polymorphism

様々な型に適用できるようなインタフェースを提供するポリモーフィズムです。実装上はいわゆるGenericsやTemplateと呼ばれる言語機能により実現されます。型を引数として渡すことで処理を様々な型に適用することを可能にします。

関数型言語において単にポリモーフィズムと言う場合、こちらを指します。

// Animalに適合するあらゆる型に対してtouchという共通インタフェースを提供する
func touch<A: Animal>(_ animal: A) {
    animal.say()
}

touch(Dog()) // bowwow
touch(Cat()) // meow

Subtyping

実行時にサブクラスのポインタをベースクラスのポインタとして扱うことにより、ベースクラスの共通なインタフェースから各サブクラスの異なる実装を呼べるようにするポリモーフィズムです。

オブジェクト指向言語において単にポリモーフィズムという場合、こちらを指します。

// AnimalとAnimalのサブクラスにtouchという共通インタフェースを提供する
func touch(_ animal: Animal) {
    animal.say()
}

touch(Dog()) // bowwow
touch(Cat()) // meow

なぜポリモーフィズムか?

なぜそのような言語機能があると嬉しいのか?という話ですが、これは僕の個人的な考えを書かせていただきます。

ポリモーフィズムは「複数の異なる型に対して共通のインタフェースを提供すること」であるというのは前述しましたが、その本質は「共通のインタフェースを用意する」という部分であると考えています。

「共通のインタフェースを用意する」という考え方は身の回りの道具のデザインにもあふれています。PCを見ればUSBやイヤホンジャックなど、共通化されたインタフェースばかりです。これがもたらすメリットは、インタフェースに適合している機器であれば何でも取り付けられるということです。言い換えれば、共通のインタフェースは柔軟な拡張性をもたらすものであり、それが良いインタフェースデザインであれば「疎結合」も促進します。

また、共通のインタフェースにはヒューマンインタフェース上のメリットもあります。ボタンやスイッチなどの使い方が共通であれば使い方を新たに覚える必要もありませんし、ミスの割合が減ることも期待できます。

プログラミング言語も人間が使う道具なので、同様のメリットがあると考えられます。つまり柔軟な拡張性と、ミスの低減です。新しい機能を追加するときに現状のコードにほとんど変更を加えることなく自然な形で追加できたり(柔軟な拡張性)、ifやswitchで分岐することなく共通インタフェースを呼び出すようにすることで、新たな機能を追加したときに書き加える箇所を減らすことができたり、連結や分解、探索、ソートなど汎用的な処理に対して呼び出すべき関数がすぐにわかったり(ミスの低減)します。

まとめると、なぜポリモーフィズムが嬉しいのか?に対する答えは「良いインタフェースを設計するのに便利だから」です。これが僕が考えるポリモーフィズムのメリットです。

その他の分野でのポリモーフィズム

ポリモーフィズムという言葉をより深く理解するため、他分野においてどのように使われているかもさらっと見てみましたのでご参考までにどうぞ。疎い分野なので間違いなどあったらすいません。

生物学

同じ種の生物の中で形や構造などが異なることをポリモーフィズムと言います。人間であれば男女の性差、目の色や肌の色、血液型などを指し、そのうち遺伝子に基づくものを遺伝的多型(Genetic polymorphism)といいます。

物質科学

同じ化学組成だが複数の異なる結晶形をとることをポリモーフィズムと言います。温度や圧力など、結晶が生成するときの条件によって結晶系が決まるようです。例えば、グラファイトとダイヤモンドはどちらもC(炭素)からなります。

おわりに

いかがでしたでしょうか。ポリモーフィズムについて様々な角度から見ていきつつ、その概念やメリットについて考察してみました。何か新たな発見があれば幸いです。

参考

結局ほとんど英語のWikipediaを参考にしました。

UIPasteboardをもっと理解する (後編)

「UIPasteboardをもっと理解する」の後編です。前編はこちらになります。

UIPasteboardをもっと理解する (part. 1) - 騒音のない世界 BLOG

後編は具体的な動作例とハマりがちな注意点について見ていきます。

具体的な動作を見る

ペーストボードの中身をログに吐いて、具体的な動作を見ていきます。環境はシミュレータ(10.2)です。ログに吐くコードはこんな感じです。

func printPb(_ pb: UIPasteboard) {
    for item in pb.items {
        for (key, value) in item {
            print("\(key): \(value)")
        }
    }
}

UITextViewやUITextFieldからテキストをコピー

画面を長押しして出るメニューからCopyを選んでHogeという文字をコピーします。

f:id:a_beco:20170124041619p:plain

public.utf8-plain-text: Hoge
com.apple.flat-rtfd: <72746664 00000000 ...>
Apple Web Archive pasteboard type: <3c21444f 43545950 ...>

複数の値が入りました。1つずつ見ていきます。

最初のpublic.utf8-plain-textutf-8エンコーディングのプレーンテキストのようです。エンコーディングごとにUTIが違うことがわかります。2つめのrtfdはRich Text Format Directoryというやつで、画像なども含められるAppleのリッチテキストフォーマットです。3つめのWeb ArchiveというのはSafariでつかうフォーマットで、htmlや画像、音などリソースも含めたウェブページを保存するフォーマットです。

このように、標準のUITextViewなどからコピーすると複数のフォーマットでコピーされることがあります。

写真アプリでコピー

標準の写真アプリで写真をコピーしてみます。

com.apple.mobileslideshow.asset.localidentifier: <31303645 39394131 ...>
public.jpeg: <UIImage: 0x608000092390>, {4288, 2848}

こちらも複数の値が入りました。

最初のものはよくわかりませんが、com.apple.mobileslideshowというのが写真アプリのバンドルIDなので、おそらく写真アプリの内部でのみ用いるデータフォーマットかと思われます。2つめは単にjpegフォーマットです。

また、テキスト、写真ともにUIからコピーするとそれまで保持していた値はクリアされるようです。

コードでコピー

コードから画像と文字列をコピーしてみます。

print("===")
UIPasteboard.general.image = UIImage(named: "sample")
printPb(UIPasteboard.general)
print("===")
UIPasteboard.general.string = "Hoge"
printPb(UIPasteboard.general)
print("===")

結果はこうなりました。

===
public.jpeg: <UIImage: 0x6080002809b0>, {512, 512}
com.apple.uikit.image: <UIImage: 0x60800009fd10>, {170.66666666666666, 170.66666666666666}
public.png: <UIImage: 0x60000009f630>, {512, 512}
===
public.utf8-plain-text: Hoge
===

imageプロパティに画像をセットするとjpeg, pngに加えてcom.apple.uikit.imageというものがコピーされました。元データのフォーマットによらず複数のフォーマットでコピーされます。ドキュメントを見てもpngとjpgで保存される旨が書いてあります。

テキストは単にセットするとUTF8のプレーンテキストになりました。

ペースト先の処理を考える

コピーしたデータをUI操作によりペーストできるかどうかは、ペースト先のアプリが対応しているUTIにより決まります。ペースト先のアプリはペースト操作が実行されるとペーストボードから対応UTIのデータ取得を試み、あればそのデータを取得してUIに反映します。

ドキュメントにも注意点として書かれていますが、自分でコピー処理を実装する場合、ペースト先のことを考えてさまざまなフォーマットでコピーしておくと親切です。例えば、画像をコピーするためにpublic.pngにのみ保存していると、public.jpegにしか対応していないアプリにはペーストできないことになり、ユーザが混乱してしまう可能性があるので注意が必要です。UITextViewや写真アプリでコピーした際に複数のフォーマットでコピーされるのもそのような理由があるものと思われます。

間違えがちなポイント

特定フォーマットの存在チェック

ドキュメントによると、stringプロパティやimageプロパティのnilチェックによってペーストボード内にデータがあるかどうかをチェックしてはいけません。代わりにiOS10から追加されたhasStringsなどを使うと良いようです。これはリソースを無駄に消費しないようにするためのようです。

注意点として、hasStringsstringの有無ではなくstringsの有無と対応する、ということがあります。stringは1つめのPasteboard Itemからしか文字列を返さないのに対し、stringsはペーストボード内のすべてのPasteboard Itemからすべての文字列を返します。なので2つめ以降のPasteboard Itemに文字列が入っている場合、hasStringsがtrueでもstringnilになることがあります。下記のコードで再現します。

let pb = UIPasteboard.general
        
pb.image = UIImage(named: "sampleImage") // 1つめのPasteboard Itemは画像を入れる
pb.addItems([["public.utf8-plain-text" : "hoge"]]) // 2つめのPasteboard Itemは文字列を入れる
        
print("hasStrings: \(pb.hasStrings)")
print("string: \(pb.string)")

結果

hasStrings: true
string: nil

stringの有無を調べたければcontains(pasteboardTypes: [String])を使うと良いです。こちらは先頭のPasteboard Itemしかチェックしません。文字列のUTIはUIPasteboardTypeListStringという配列から取得できるので、contains(pasteboardTypes: UIPasteboardTypeListString as! [String])のように書けます。先程の例に適用すると、この戻り値はfalseとなります。

ペーストボードからデータ削除

UIPasteboard deleteなどで検索すると空文字をセットするコードがたくさん出てきますが、これはあまりよくないかと思います。細かい話ではありますが、空文字は文字列としてペーストボードに保存されてしまうため、UITextViewで長押しした際のメニューで無駄にPasteが出てしまいます。

f:id:a_beco:20170124035844p:plain

データ削除はitemsプロパティを直接操作することで可能です。全て削除する場合は単にUIPasteboard.general.items.removeAll()とすれば良いです。先程のメニューはこうなります。

f:id:a_beco:20170124040000p:plain

おわりに

2記事に渡ってUIPasteboardについてまとめてみましたが、いかがでしたでしょうか。何か新しい発見があれば幸いです。

UIPasteboardをもっと理解する (前編)

UIPasteboardといえばstringプロパティやimageプロパティでコピーペーストできるやつ、ぐらいに思ってませんか。この記事ではその概念と注意点をしっかり押さえつつ、具体的な動作例を見ていくことでUIPasteboardの理解を深めていきます。

(ちょっと長すぎたので概念と具体例で前後編に分けます。今回は前編になります。)

UIPasteboardとは

公式docの冒頭を要約すると、UIPasteboardは「データを自分のアプリ内のある場所から他のあらゆるアプリに共有するもの」のようです。典型的な使い方はデータコピーなどの際にUIPasteboardを通してデータを保存しておいて、アプリ内や別なアプリから再びUIPasteboardを通してデータを読み出して使う、といった感じです。単純なテキストや画像のコピーだけではなく、バイナリや独自データを他のアプリに転送するような用途でも使用できます。

UIPasteboardの種類

大きく分けて2種類のペーストボードがあります。

The General Pasteboard

どのアプリからも自由にアクセスできる、システムワイドなペーストボードです。UIPasteboard.generalでシングルトンとしてアクセスできます。メモ帳アプリなどで普通にコピーするとこのペーストボードに保存されます。逆に言うと、General Pasteboardをプログラムから操作することによって現在コピーしているものが書き換わってしまい、ユーザーの混乱を招く恐れもあるので注意が必要です。

iOS10からはHandoffの新機能として、複数端末間でGeneral Pasteboardに保存したデータを共有できるようになりました。こちらは共有不可にする設定も可能です。

Named Pasteboard

アプリ内で完結するようなものや、Team ID が共通なアプリ間でのデータの受け渡しにはNamed Pasteboardが利用できます。これは、一意な名前をつけて生成され、システムを汚さず独占的に使用できるペーストボードです。

何を保存するのか

UIPasteboardは何を保存するのでしょうか。UIPasteboardのヘッダを見るとこういう部分があります。

// Direct access

open var items: [[String : Any]]

Direct accessという言葉が示すように、このitemsプロパティでアクセスできるitemが、ひとつのペーストボードに保存されるデータそのものです。これはPasteboard Itemと呼ばれるもので、ひとつのPasteboard Item[String : Any]というDictionaryとなっています。var宣言されているため追加、削除、挿入など何でもできます。そして、これ以外のメソッドやプロパティのほとんどはこのデータに便利にアクセスするものです。

つまり、ひとつのペーストボードが保存しているのは「何でも保存できる辞書」の配列ということです。これが意味するのは、ペーストボードが保持する値は1つではないということです。普段単純にコピーペーストなどしていると新しいものをコピーすると古いものは消えるのが当然のような感じがしますが、ペーストボード自体はプログラム的には(リソースが許す限り)いくつでも値を保持できます。

Pasteboard Type

Pasteboard ItemのDictionaryにおけるキーとなる値がPasteboard Typeです。この実体は単なるStringであり、意図せず重複してしまうことがなければどんな文字列でも指定することができますが、UTI (Uniform Type Identifier)の使用が推奨されています。

UTI (Uniform Type Identifier)

UTIはAppleが制定しているデータフォーマットを識別する識別子で、reverse-DNSで書きます。UTIについてはこちらのドキュメントが詳しいです。 Introduction to Uniform Type Identifiers Overview

例えば、com.apple.quicktime-moviecom.apple.pict public.jpegのように書きます。

また、UTIは継承でき、is-a関係を持ちます。public.dataを継承してpublic.textpublic.imageという型が定義され、public.imageを継承してpublic.jpegpublic.pngが定義される、といった具合です。

同じような役割を果たすものとしては、古くはOSTypeという"JPEG"のように4文字でファイルタイプを識別するものがあったり、他にはファイル拡張子やMIME typeなどもそうですが、UTIはより柔軟で拡張性が高いとのことです。

ちなみに、特定のアプリ内でしか使わないデータフォーマットの場合は<app-bundle-id>.foo.barのように命名するようです。

まとめ

  • ペーストボードにはGeneralとNamedの2種類ある
  • 1つのペーストボードは「何でも保存できる辞書」の配列を持つ
    • 1つの辞書をPasteboard Itemという
    • itemsプロパティですべてのデータにアクセスできる
    • stringプロパティやimageプロパティなどは単にitemsの中のデータに便利にアクセスするためのもの
  • 辞書のキーはPasteboard Type
    • 標準化されたデータフォーマット識別子であるUTI形式で指定する

いかがでしたでしょうか。前編はここまでです。後編では具体的な動作例や注意点について見ていきます。

UIPasteboardをもっと理解する (後編) - 騒音のない世界 BLOG

SoundCloudの再生数といいね数を振り返る('10~'16)

2010年8月より、SoundCloudで少しずつ自作の楽曲をアップロードしてきました。 騒音のない世界のサイトYoutubeチャンネルもありますが、楽曲は全てSoundCloudに上げています。

beco | Free Listening on SoundCloud

2017年1月12日現在、51曲アップロードされており、総再生数はおかげさまで35万回を超えています。 いつも楽曲を聴いてくださっている皆様、コメントや楽曲のご利用報告をくださる皆様ありがとうございます。

今回はこれまでを振り返る意味で、再生数の推移といいね数の推移について書きたいと思います。

再生数の推移

f:id:a_beco:20170112010008p:plain

2016年の総再生回数は129,270回でした。 2010年はほとんど見えませんが61回でした。

いいね数の推移

f:id:a_beco:20170112010435p:plain

2016年の総いいね数は506でした。 2010年は0回, 2011年は2回、2012年は8回です。 いいね数は再生数と比べて2016年の伸びが顕著ですね。

楽曲ごとのいいね数

f:id:a_beco:20170112011227p:plain

楽曲ごとのいいね数です。 公開時期などもあるので必ずしも人気順ではありませんが、1位は「新生活」でした。

2位は初音ミク楽曲の「強欲な娘」です。

インスト曲とミク曲が両方並んでいてなんかうれしいです。

おわりに

最初はアップロードしても全然聴かれていませんでしたが、徐々にコメントもらえたり、いろいろなところで使ってもらえるようになってきてありがたい限りです。音楽的にやりたいこともだんだん具体化してきたかなーという感じがしています。

これからももっと良い曲を届けていきますので、応援していただけるととても嬉しいです。 気になった方はサイトのお気に入り登録、フォロー・チャンネル登録、よろしくお願いします。ぐっときたらいいね・リツイートなどもしていただけるとありがたいです!

動画制作されてる方はBGMとしてのご利用も自由ですのでぜひどうぞ。商用利用も可です。詳しくはサイトを御覧ください。

では、2017年もよろしくお願いします〜。

Onion Architecture と Clean Architecture

以前にiOSにおける Clean Architecture 構成について、いくつか記事を書きました。Clean Architectureって何?という方はまずそちらの記事をご覧ください。

引き続き色々と調べておりまして、Clean Architectureの元になったアーキテクチャの1つであるOnion Architectureについて記事を読んでいて少し発見があったので書きます。

The Onion Architecture

背景

記事を書いてるのはJeffrey Palermoという方だそうです。Clear Measureというソフトウェア開発会社のCEOの方みたいですね。すごい経歴。。

記事は2008年ですが、その時点で何度か対外的に発表されているようなので大体10年前ぐらいのアイディアってことでしょうか。iPhoneの初出が2007年なので、スマホが現れ始めた頃ですね。

4つのルール

f:id:a_beco:20161231220524p:plain

  • アプリケーションは独立したオブジェクトモデルを中心にして構築される
  • 内側のレイヤがインタフェースを定義し、外側のレイヤが実装する
  • 依存の方向は内側に向かう
  • アプリケーションの核となるコードはインフラ(DBなど)とは分離してコンパイル&実行できる

どういうものに向いているか

  • 小さなウェブサイトには向いていない
  • 長く運用するアプリに向いている

どういうものなのか

  • 依存ルールがインタフェースの使用を強制し、疎結合化する
    • テスタブルにする
  • インフラを円の外部に起き、アプリケーションコードから依存しないようにする
    • インフラを置換しやすくする

伝統的なLayered Architectureの欠点

  • UI -> Business Logic -> Data という依存性
    • 各層が下層に依存し、各層が様々なインフラに依存する
  • 依存自体は必要なものだが、不要な依存を産んでいるのが問題
  • Business LogicがなければUIは機能せず、DataがなければBusiness Logicは機能しない
  • 特にデータアクセス技術は頻繁に変更される
    • 歴史的には3年ごとにデータアクセスのテクニックは変更される
    • 3年おきに入れ替える必要が出てくる
    • 密に結合していると、現実的に入れ替えが不可能になる危険がある

Clean Architectureとの関係性

そもそもClean ArchitectureはOnion Architectureを含むいくつかのアーキテクチャを自然な形で1つに統合するもの、という風に書かれています。なのでコアなコンセプトとしてはかなり似ています。目立って違うなあと感じたのは、

  • Onion Architectureは、依存が内側に向かっていれば層を飛び越えても良い
  • Onion Architectureには明示的なアダプタ層(Interface Adapter)が無い

といった点でしょうか。

依存を内側に向ける意味

前回、「依存を内側に向けることの意味がわからん」という話を書いたんですが、「インフラ技術は3年程度で新しくなる」という具体的な話があり、イメージが湧きました。3年以上運用することが明らかな場合、絶対に訪れるリスクとして備えておく意味があるなあと。Clear Measureでも古い技術のアップグレードとかやってるみたいなので、様々な現場のつらみから必要に迫られて生まれたアーキテクチャなのかなあと。そういう意味でも、やっぱりどちらかというと業務ソフトウェアを前提にしてるのかなあという印象が強まりました。

おわりに

アーキテクチャの話ってマクロで抽象的なのでコアなコンセプトとその意義を理解するのに時間がかかりますね。。引き続きアンテナ張っていきたいです。また何かあれば書きます。

サービス開発に Clean Architecture を採用するのは妥当か?

以前 iOS Clean Architecture という記事を書きました。Clean Architectureとは何か?とかClean Architectureの実装例などが見たい方はまずそちらをご覧ください。今回は開発の中で感じたモヤモヤを書きます。はっきりした主張がなくて論旨がわかりにくいのですが、ご承知の上で読み進めていただければ幸いです。

Clean Architecture が前提にしている「安定性」

前回も述べたように、Clean Architectureの重要なコンセプトとして「依存ルール」があります。依存関係は円の内側に向かうようにする必要があります。なぜならば、アプリケーションフレームワークやDBやWebサーバなどの外部の変更に対してアプリケーションのビジネスロジックが影響を受けないようにしたいからです。言い換えれば、「コードが変更された時の影響範囲を出来る限り狭めたい」という意図があります。これはいわゆる安定依存の法則であり、特にClean Architectureが起源のアイディアではありません。安定依存の法則に従うことによりソフトウェアの動作を安定させる効果もありますし、副次的に差分ビルドにかかる時間を短くし、素早いイテレーションを実現するといった効果もあるかと思います。

この依存ルールの裏には、「ビジネスロジックは比較的安定である」という前提があります。つまり、画面の見た目が変わったり、DBなどの保存先が変わることはあっても、ビジネスのルールが変わることは比較的少ないよね、という前提です。たぶん経験則によるものです。

ビジネスロジックは常に安定か?

ビジネスロジックが安定」という前提は、「ソフトウェアが既存の業務を置き換える」という状況においては(業種にもよりますが)そこそこ正しいのではないかという感じがします。スケジューラやTODOのような汎用的なツールについてもそうなんじゃないでしょうか。

しかし、近年のWebアプリやスマホアプリなどのサービス開発においてその前提はどこまで成り立つのでしょうか?SNS系のサービスは現実の作業を置き換えるものではなく、新たな体験と提供しているといえます。このような類のサービスにおけるビジネスロジックと、業務アプリケーションなど現実の作業を効率化する類のソフトウェアのビジネスロジックはどちらも同じく安定と言えるんでしょうか。

根拠に乏しい私感になりますが、このへんを全く同列に扱うのは少し乱暴だなあと何となく感じています。業務アプリケーションでないソフトウェアに対してビジネスロジックという言葉を使うのにも少し違和感を感じます。

もちろん共通的な部分もたくさんあるし、アプリケーションレイヤは全部不安定だろってわけではなくて「このへんは安定してるな」と思ったりもするんですが、Domainというよりは各層のコアな機能とか、Utility的なところだったりすることが多いなーと思うんですよね。例えば、HttpClientってデータ取得だからDomainというよりData的な外部の層におけるコア機能って感じで考えられるのかなって思うんですが、そんな頻繁に変えませんよね。まあそういう部分は基本的に「使われるもの」なので、安定依存の法則に違反するものではないんですが、Domainじゃないけど安定してるやんっていうか。

よくある変更に対する変更箇所が多すぎる問題

新規でイチから開発してるからかもしれませんが、決して特殊ではない変更に対して変更箇所が非常に多くなってしまうことがあり、安定依存って言ったじゃんヤダヤダとなることが理想と現実のギャップとしてあります。

例えば「ビューに追加で情報を表示したいからサーバから受け取るデータを変更する」という状況はクライアントの開発でよくある場面かと思いますが、この場合にCleanArchitectureを採用しているとどうなるかというと、Dataの変更をPresentationに伝えるため必然的にDomain(UseCase)も変更することになります。それぞれの層で使っているデータ構造をすべて書き換え、変換処理も書き換える必要があります。作業時間がボトルネックになるというほどではないですが、変更が機械的で退屈なことが多いです。

このあたりに関しては感覚的にはワーストケースに合わせているという感じがしています。なのでどうしてもシンプルな部分における変換層の単純作業感や記述の重複感があり、どうなのかなあと思うところです。

結局どこに依存すれば良いのか?

安定依存というコンセプト自体は間違っていないと思うのですが、結局安定な場所ってどこなんでしょうか?

Clean Architectureというより安定依存の話なのですが、これって要は「たくさん変更される場所」や「ほとんど変更されない場所」をあらかじめ予測せよってことなのかなって思ってます。で、アーキテクチャでそれをルール化してチーム内で強制するようなアプローチってなると、やっぱりClean Architectureみたいな、統一的にこのへんに依存せよ的な構成になっちゃうのかなあと思いつつ、現実的には安定的な部分ってアプリごとにもっとバラけてるんじゃないかな、と思ったりもしています。

だから、そこそこうまくいくけど最適なアプローチってわけでもないのかなって思うんですよね。

おわりに

お前は何が言いたいのかって感じですが、Clean Architectureの利点としてよく言われる「ファットコントローラを防げる」とか「テストが容易になる」とかって単にモデルをきちんと分離して疎結合な構成でルール化すればLayered Architectureで良くて、Clean Architectureならではな部分って「円の中心に向かって依存する」っていう依存ルールとか、Interface Adapterの存在とかかなて思うんです。で、安定してるところに依存するのが良いというのはわかるんですが、その円の中心って本当にそんないつも安定してるか?もっといい切り口があるのでは?みたいな話でした。

まあ多様な集合に対して統一的なルールを決めるのって大抵例外的なものがあるから100%うまくいくってことはなくて、大枠の方向性として70%ぐらいでそこそこうまくいけばいいかみたいな感じになりがちですよね。もしくは、結局ビジネスロジックが安定してるっていうのもあくまでも「比較的」っていうだけで期待するほどは安定していないものなのかもしれないですね。そういうのって何かしらの実験で数値化されてたりするんですかね?

あと、Onion ArchitectureとかはClean Architectureの元になってるっぽくて、情報が少し多いのでこの辺も合わせて読むとより理解が深まるかもしれないなあとちょっと思ってます。パッと見で違いがわからないぐらい似ているのですが。そのうち読みたいです。

→ 2016.12.31 追記: Onion Architectureについて書きました

ダラダラ書いてすいません。最後まで読んでいただいてありがとうございます。僕が感じてるモヤモヤを汲み取って「この本読むといいよ」とか教えてもらえたらとてもうれしいです。