シアトルで実践する「プログラミング的思考」[14] 抽象化(3)インターフェーイスによる抽象化
見出し画像

シアトルで実践する「プログラミング的思考」[14] 抽象化(3)インターフェーイスによる抽象化

ywatanabe

ソフトウェアは分解によっていくつもの小さなソフトウェア部品に分けられ、それらの部品が集まって連携することで問題を解決します。その際、それぞれの部品は抽象化された機能を提供し、部品と部品の間の「関わり」も抽象的になります。このような部品間の接続を「ゆるくつながっている」と表現したりします。(この「ゆるいつながり」は英語で loose coupling と呼んだりします)。

分かりやすい例を挙げるとすると、スマートフォンの「オペレーティングシステム(OS)」と「アプリ」があります。OSは部品と言うには複雑で大きめですが、ソフトウェアであることには違いありません。また、アプリはある特定の機能を持つ部品と捉えることもできます。この2つのソフトウェアはゆるく繋がっているので、簡単にインストールしたりアンインストールしたりできます。スマートフォンという物理的なデバイスからしてみれば、OSもアプリも命令の集合という点で同じです。ただ、高度に抽象化されているので人間には別々のもののように感じられます。

インターフェース

ソフトウェア部品同士は接しているので、その間には何らかの「やり取り=インターアクション (interaction)」が存在します。ほとんどの場合それは「一方がもう片方を使う」というインターアクションです。「使う、利用する」といった言葉で表し、英語ではUseで表すことが多いでしょう。このインターアクションを決定するのが「インターフェース」です。

画像1

インターフェースは「その部品をどのように使うか、どのような結果を期待できるか」などを定義します。定義するのは人間です。AIもそのうち出来るようになるか、一部分アシストしてくれるようになるかもしれませんが、現時点では人間がやる仕事です。その定義にしたがって、ある部品は別の部品を、規則性をもって使うことになります。このようにインターフェースは「使う側」と「使われる側」の中間に位置していて「使われる側」の具体的な仕組み(実装)を見えない状態にして「抽象化」します。「インターフェース=抽象」と理解しても良いでしょう。

「インターフェース」はソフトウェアに限定されておらず、ハードウェア層も含むコンピュータシステム上のあらゆるレイヤー(階層)に「抽象化レイヤー」として存在します。歴史的経緯から言うと、ソフトウェアインターフェースはハードウェアインタフェースを真似て、ソフトウェア部品をハードウェア部品と同じように組み合わせて扱えるようしているのです。そこで、まずはハードウェアインターフェースから解説していくことにします。

ハードウェアインターフェース

このインターフェースはハードウェアを抽象化するために不可欠な要素です。例えば、デバイスの物理的な機能を人間が使う時、出来るだけ人間にとって扱いやすいように「抽象化された使い方」が定義されています。キーボード、マウス、タッチパネル、マイク、スピーカー、スクリーン、などのデバイスは物理的に「使い方」が定めてあり、そのインターフェースを介して人間は抽象的にデバイスの機能を利用できるのです。

例えばキーボードで文字を入力する時、ユーザは実際にどのような電気信号がキーボードからコンピュータ本体に送信されているか知る必要はありません。もしこれが抽象化されていない入力デバイスであったなら、ユーザはその電気信号を知らなくてはなりません。例えば5Vの電圧の電流を特定の銅線に、特定の時間流す、といったことをしなければならないのです。キーボードの「Aキー」を押す代わりに、それを表す電気信号を入力しなければならないので文章を書くのにもすごく時間がかかってしまいますね。ハードウェアインターフェースはコンピュータに限らず、自動車や電化製品でも同じです。自動車のアクセルの代わりに手動でガソリン燃料をエンジンに入れるとしたらとても運転など出来ません。

ソフトウェアインターフェース

ソフトウェアのインターフェースも抽象化を実現するために重要な役割を持っています。ソフトウェア開発やプログラミング的思考というコンテクストで考える時、ほとんどの場合、インターフェースとは「ソフトウェアインターフェース」の事を言っています。

ソフトウェアインターフェースは最も簡単に表すとすれば関数の「入力と出力」を指します。関数が実装する機能を利用するためには、関数が定義し期待する入力を渡さなくてはなりません。また関数が返す値や結果の状態も関数によって定義されていますから、利用側はそれが定義に従って一定であることを期待出来ます。後述しますが、インターフェースには「契約」という意味合いもあるのはこのためです。

このようにソフトウェアがインターフェースを持つことで、まるでハードウェアの部品のように規則性のあるソフトウェア部品を定義することが出来るのです。

一枚板のプログラム

ところで「インターフェースを持たないソフトウェア」を作る事は可能でしょうか? 答えは「イエス」です。コンピューターに仕事をさせるという目的を達成するのに、部品と言う概念のない「一塊のプログラム」を開発して利用することは十分可能です。ごく単純な仕事であればそのようなプログラムを書くことも一般的です。

「一塊のプログラム」は英語で monolithic (一枚板)と呼ばれ、部分や部品がありません。部品がないので内部にインターフェースなどはありません。びっしりと命令語の記述がある状態です。「スクリプト」と呼ばれる種類のプログラムによく見かけるフォーマットですが、どのようなプログラム言語でも一枚板のプログラムを作成することは可能です。

プログラミング初心者がそのような一枚板プログラムを作成することは良くあります。「動かすこと」に集中するので仕方ありませんし、ソフトウェアを分割するやり方もまだ学んでいる途中でしょうから無理もありません。初心者から中級・上級になるためには「分解」、そして部品の関わりを定義するインターフェースを理解しなくてはなりません。

ユーザーインターフェース

「ユーザーインターフェース」はコンピュータシステムと人間の間にあるインターアクションを指し、ハードウェアとソフトウェアの両方を含みます。ユーザーインターフェースの設計は、人間工学などに詳しいデザイナーが担当することが一般的です。ユーザーインターフェースも抽象化を実現します。スマートフォンなどのタッチスクリーンで「スワイプ」する動作は最も分かりやすい代表例のひとつですが、ありとあらゆるコンピュータの操作がユーザインターフェースを通して行われます。ほんの少しの例を挙げるとすると、文字情報を入力するテキストボックス、アクションを指示するボタン、選択肢を選ぶリスト、などがあります。

一般的に「直感的」に機能を使える時、ユーザーインターフェースが優れているとされます。直感的とは、自然であり、説明が必要ないことをいいます。

ユーザーエクスペリエンス

ユーザーインターフェースは「ユーザーエクスペリエンス(ユーザ体験)」という文脈でも登場します。ユーザー体験はインターフェースという見た目の話に止まらず「振る舞い」や「作法」、そして体験することで生じる感覚や感情なども含む「ユーザ体験全体」に関わってきます。

例えば、あるメーカーのスマートフォンで「アプリ」をインストールする体験は、他のメーカーのスマートフォンとは異なるかもしれません。同じ目的を達成するのに、異なるインターフェース、異なる手順、異なる経験が提供されているのです。

経験には「好ましい経験」とそうでないものがあり、それが機能以上に「価値」に貢献することがあります。新製品の箱を開ける「開封の儀」などと俗に呼ばれる体験がありますが、ソフトウェアを利用するときも同様に「体験」があるということです。「使いやすい」と感じるなら、そのユーザーエクスペリエンスは上手く設計されていると言えます。

API - アプリケーションプログラミングインターフェース

ソフトウェアインターフェースの一種として「アプリケーションプログラミングインターフェース」というインターフェースもあります。APIと略されることが一般的です(application programming interface の略)。通常APIは複数の関数から構成され、あるサブシステムもしくはサービスを利用するために必要な操作(オペレーション)を含みます。あるスマートフォンのアプリを開発するとしたら、そのスマートフォンのプラットフォームAPIを学んで、それを駆使してプログラムを実装することになるでしょう。

APIは専門的な言葉のように聞こえます。たしかに「プログラミング的思考」を学ぶ際に意識して知る必要はないかもしれません。ですが現実として、プログラミングを行うこと、そしてソフトウェアを開発するということはほとんどの場合「APIを使うこと」を意味します。なぜならプログラミングで作成する人工物であるプログラムは、それが動作するプラットフォームのAPIを利用して実装するからです。

micro:bitやscratchでコーティングをすることは、それぞれのプラットフォームのAPIを使っているという意味なのです。あるプラットフォームでプログラミングが出来ることは、そのプラットフォームのAPIを不自由なく使えるという意味でもあるのです。

余談ですがAPIには「公開されているAPI」と、そうでないものがあります。公開されていないAPIを使う「猛者」が現れることが多々あります。そういう行為を「ハック」などと呼ぶこともあります(他にも「脱獄」や「チート」という言葉もあります)。

プロトコル(ネットワーク上のシステム間のインターフェース)

「プロトコル」という言葉があります。コンピュータの世界では一般的にはネットワークに参加する「コンピュータ同士が通信をする際の取り決め」を指します。プロトコルはインターフェースよりやや上位の概念と言えるかもしれません。なぜならプロトコルは「手順」も規定することが一般的だからです。

例えばインターネット上でウェブサイトがブラウザとセキュアな通信(HTTPSと呼ばれるプロトコル)を行う場合、ウェブサイトのサーバとブラウザが安全な通信チャンネルを構築するまでのメッセージのやり取りなどを細かく規定しているからです(「握手をする=hand shake」と呼ばれる一連の手続きを指します)。

ですが、プロトコルはインターフェースという役割も持っています。あるプロトコルを実装するコンピュータは「抽象化」を実現していて、そのコンピュータのハードウェアやソフトウェアの細かい種類、実装、性能などに関わらずネットワークに参加することを可能にしています。異なるメーカーのデバイスが、お互い通信できるのはプロトコルがあるからなのです。様々なメーカーのスマートフォンを同じWiFiアクセスポイントに接続して利用出来るのもプロトコルのおかげなのです。

コントラクト(契約)

ソフトウェアインターフェース、とくにAPIの文脈で理解しておくべき概念に「コントラクト」があります。コントラクトとは「契約」の意味で、インターフェースを「提供する側」と、インターフェースを通して機能を「利用する側」の間での「取り決め」のことです。

コントラクトは大きく2つの種類に分けることができます。「オペレーションコントラクト(操作契約)」と「データコントラクト(データ契約)」です。通常、オペレーションはデータのやり取りを行うので、2つの契約はセットになってコントラクトを構成します。

コントラクトは契約なので「守ること」が重要です。契約を守る限り、その部品を使う側は、部品から常に一定の結果を期待できるので、機能と品質をある程度担保できます。例えばスマートフォン向けのアプリを開発したとして、プラットフォームの内部に何度も変更があったとしても、契約が変わらない限りそのアプリは機能し続けますが、それはプラットフォーム側が契約を履行しているからです。スマートフォンのOSは常に更新されますが、既存のアプリは機能し続けるのは契約が守られているからです。

ところで契約を破ることになる更新も起こります。英語では Breaking Change などと表し、下位互換性のない変更を含みます。そのような事態を回避するために、新規バージョンとして発表して、複数のバージョンを提供するやり方もあります。この辺りはやや高度なトピックになるので割愛します。

インターフェースの利点

ソフトウェア開発のベストプラクティス(最良の実践)のひとつとして「program against interface インターフェースに対してプログラミングせよ」というものがあります。これは実装を直接そのまま使うのではなく、実装の上に位置するインターフェースを使うべきだと言っています。もしインターフェースが無い場合は、わざわざ設計の一部として新規のインターフェースを「使う側」と「実装」の間に挟みこむこともあります。そのぐらい重要な方法論なのです。

なぜインターフェースが間にあることが望ましいかというと「使う側」と「実装」がゆるく繋がることで、双方がより自由に変更することが出来るようになるからです。もちろんこれはインターフェースが定める「契約」をお互いが守るという前提に立ちます。

例えば、今持っているスマートフォンはおそらく何度かのアップグレードを経て来たことでしょう。4年前に購入してずっと使ってきたスマートフォンを最新のものにアップグレードしたとしても、基本的な操作は変わっていませんよね? もし操作方法が毎回大きく変わってしまうと困りますよね。インターフェースの契約はあまり変わらないので、スマートフォンの中身(実装)をいくらでも改善出来るのです。ソフトウェア部品同士の関わり合いでも同じことが言えます。

ソフトウェア部品は常にバグが発見され、修正されます。使う側になんら影響を及ぼすことなく、実装が少しずつですが更新されていきます。使う側は更新があった事実すら気づかないかもしれません。例えば利用する銀行ATMの内部のプログラムが更新されたとしてもATM利用はこれまでと変わらず利用することができるのに似ています。

先に挙げた「一枚岩のプログラム」では、このような変更は難しいとされます。一枚岩なので、そもそも「プログラム部品」という構成要素がありません。もちろん、全体に影響を与えない変更は可能ですが、それでも全体がひとつなので、ほんの一部の変更も「全体の変更」となってしまいます。すると、どこかで不具合が生じるかわからないので全てをテストし直さなくてはならないかもしれません。

それよりも、「分解」によって「プログラム部品」に分割して、プログラム部品同士の関わりをインターフェースを使って定義し、組み合わせて全体を構成する。そうすることで、変化の激しいユーザの要求に対応できるソフトウェアを開発し保守することができます。

プログラミング的思考としての抽象化のより具体的な方法論としてインターフェースを解説しました。プログラミング初心者はあまり意識しなくてもよい概念ですが、初心者から中級・上級と腕を磨いていくと自然と分解、そして分解で生じる部品間の関わりとしてのインターフェースを意識していくようになるでしょう。

この記事が気に入ったら、サポートをしてみませんか?
気軽にクリエイターの支援と、記事のオススメができます!
ywatanabe
福岡市出身。シアトルのマイクロソフト本社シニアソフトウェアエンジニア。ビジネス向けCRMツールDynamicsにAI機能を統合する仕事をしています。電子書籍で支援いただければ嬉しいです→ https://www.amazon.co.jp/dp/B09LM24XBK