抽象に依存せよ?むかしむかし、スペマの国でのできごと
むかしむかし
スペマの国があったそうな。
この国のお城では、毎晩宴が開かれ、美味しい食事と楽しい音楽で人々を楽しませていたそうです。
特に、季節ごとの行事や祭りでは、国中から名産品や珍しい食材が集められ、それを使ったご馳走が振る舞われるのが恒例となっていました。
ある年のこと、王様は「この国で一番おいしい料理を食べてみたい」と思い立ち、城下や近隣の町から腕利きのシェフたちを集めることにしました。
彼らはそれぞれ異なる技術と個性を持ち、王様のために毎日特別な料理を振る舞うことになったのです。
最初のうちは毎晩の宴がとても楽しみだった王様ですが、次第にある問題に悩まされ始めました。
シェフたちは、それぞれが自分たちの独自ルールを持っており、料理を作るための準備の仕方がバラバラで、指示を出す手間が大きかったのです。
- アレックスというシェフは、「具体的な材料を口頭で教えてください」とお願いしました。そのため、王様は具材から欲しいものまでを細かく説明しなければなりませんでした。
- ボーラは、「料理名だけを教えてください。あとは私が想像します」と言いましたが、出来上がった料理が王様の期待とズレることがよくありました。
- イズミは、「王様が食べたい料理のイラストを描いてください」と頼みましたが、毎回絵を描くのに困ってしまいました。
- ルカは、「食べたいものを紙でください」と求めました。また、「味が濃ければ美味しい」とも考えており、健康を気遣わず塩分多めの料理になることがありました。
最初は新しい調理人たちとのやりとりを楽しんでいた王様でしたが、日が経つにつれ次第に疲れを感じるようになりました。
「みなバラバラの方法すぎて、指示を出すのが面倒すぎるのじゃ」
王様はよいことを思いつきました。
「シェフたちの個性を認めつつ、コミュニケーションを統一するための御触書を出すのじゃ。これでどのコミュニケーションコストが爆下がりぞ!」
王様は早速シェフたちへの個別の指示を共通化するように落とし込んだ御触書を用意し、城中に知らせました。
御触書が出されてからというもの、王様の指示は統一され、新しく入ってきた個性豊かなシェフたちにも同じように伝わるようになりました。
みな、御触書きを見ればすぐに料理を作れるようになり、王様の負担は大きく軽減されました。
さらに、味付けの基準が決まったことで、どの料理も安定して美味しく、かつ健康に気を使った料理かどうかわかるものになりました。
こうして、スペマ城の宴は以前にも増して賑やかになり、王様もシェフも参加者たちも幸せに暮らしましたとさ。
おしまい
インタフェース = 御触書
この物語[1]では、料理が味の特徴と健康指標としていますが、ここで伝えたかったことは「御触書」がインタフェースの役割を持っているということです。
これまでバラバラで個人のルールの中で仕事をしていたシェフたちですが、この御触書に沿うことで共通のコミュニケーションをとることができます。
王様もシェフが増えるたびに個別の指示をしていたのでは身が持ちませんし、なにより覚えきれません。
王様から「こう指示するからこれを作って」というルールができたからこそ、効率よく誰でも働くことができます。
(この物語では、王様が独りよがりな御触書を出しておらず、シェフたちの指示をまとめあげたうえで共通のルールに落とし込んでいます。なんとよい王なのか。そしてこれが抽象化です。)
また、同じシェフでも今日から別の方法にしてくれと言われた日には 🤯 です。
これまでの状態をコードで表してみる
以前の状態をコードで表すと、このようになるでしょう。
// シェフたちのクラス
class Alex {
cook(...args: string[]) {
/** 処理 **/
return {
name: '料理名',
rank: 5 // 自信度
};
}
}
class Izumi {
cook(imagePath: string) {
/** 処理 **/
return {
description: '説明',
};
}
}
王様からの指示
const alex = new Alex()
// この材料で料理作って
const food = alex.cook('人参', 'じゃがいも', 'こんにゃく', '牛肉')
const izumi = new Izumi()
// がんばって描いたこの絵を元にを作って
const food = izumi.exec('食べたいもの.jpg')
上記では引数もバラバラ、呼び出すメソッド(お願いの仕方)もシェフによって異なります。アレックスは .cook
ですが、ボーラは .exec
ですね。戻り値もシェフそれぞれが定義した結果が返ることになります。
この状態は王様がそれぞれのシェフに依存している状態となります。
御触書以降の状態をコードで表してみる
共通のインタフェースができました。
コードで表すと例えばこのようになるでしょう。
interface SpmChef {
// お城のシェフは cook を実装する必要があり、入力は DishOrder、出力は DishResult とする
cook(order: DishOrder): DishResult
}
// 料理指示
interface DishOrder {
name: string; // 料理名
mainIngredients: string[]; // 主な食材
seasoning: '辛口' | '甘口' | 'スパイシー' | '薄味' | '濃味'; // 味付けの希望
}
// 料理の結果
interface DishResult {
name: string; // 料理名
taste: string; // 味の特徴
isHealthy: boolean; // 健康的かどうか(基準を満たしているか)
}
アレックスはこのインタフェースを実装することでこのようなクラスになります。
class Alex implements SpmChef {
cook(order: DishOrder): DishResult {
/** 処理 **/
return {
taste: 'さっぱりだが深みのある味付け',
isHealthy: true
};
}
}
王様が指示をしたい場合はこのようになります。
const alex = new Alex()
const dishOrder: DishOrder = {
name: '肉じゃが',
mainIngredients: ['人参', 'じゃがいも', 'こんにゃく', '牛肉'],
seasoning: '薄味'
}
const food: DishResult = alex.cook(dishOrder)
SpmChef というインタフェースを実装することで、他のシェフに対しても同じ指示の仕方ができるようになります。
この状態が「王様と各シェフがインタフェースに依存している」と表現でき、「インタフェース、つまり抽象に依存する」状態となります。(「依存性逆転の原則」として知られています)
王様は各シェフ(具体)の個別の指示方法ではなく、御触書(インタフェース: 抽象)を介して指示を送ることでどのシェフであろうと同じ注文をすることができます。
御触書はだれのものなのか
この物語では、王様が出したものになります。
ソフトウェア開発においては、ある機能を呼び出す側がそれに該当することになると思います。
DDDなどのアーキテクチャを実装していると、ドメイン層にリポジトリのインタフェースを置き、インタフェースを実装したリポジトリの具象クラスをインフラ層に配置します。
ドメイン層からリポジトリへ「これを取ってきて」や、「これを永続化して」などの指示が送られることになり、王様とシェフとの関係に近いと言えるでしょう。
もし、リポジトリが「こう実装したからこのように呼び出して!」と言っており、それに合わせてしまうと呼び出し側の処理が大変なことになってしまいます。
まとめ
御触書があることで、王様はシェフたちへ個別の指示をしなくても共通の指示でよくなりました。
また、シェフの気分で指示方法が変わったりすることもありません。
また、弊社の @lei さんが依存性の注入をテーマにした記事を書いてくれていますので合わせてご覧ください。
毎晩宴いいなと思った方はカジュアル面談しましょう!(スペースマーケットでは毎晩宴はしておりませんのでご注意ください!)
-
文化とか時代とかごちゃ混ぜです。ツッコミ所盛りだくさんなのはご勘弁 🙏 ↩︎
スペースを簡単に貸し借りできるサービス「スペースマーケット」のエンジニアによる公式ブログです。 弊社採用技術スタックはこちら -> whatweuse.dev/company/spacemarket
Discussion