Slide 1

Slide 1 text

コードでの契約ってなんだ? ツノ

Slide 2

Slide 2 text

自己紹介 ツノ BtoB Saasのフロントエンドエンジニア 銭湯とサウナを愛する20代 「完全に理解した」Talkは4回目 過去のIT 書籍「プログラマー脳」を完全に理解した 「世界一流のエンジニアの思考法」を完全に 理解した Vite完全に理解した Xの最近の投稿 ツノ @2nofa11·Follow やっぱり風呂だよ、風呂。あとサウナ。 10:55 PM · May 11, 2024 2 Reply Copy link Read more on X

Slide 3

Slide 3 text

LTの内容 読んだ本のアウトプット Good Code,Bad Code ~持続可能な開発のための ソフトウェアエンジニア的思考 本の概要 コーディングプラクティスの紹介本 メンタルモデル 具体的なHow 1年間近く積読していた LT駆動読書で読みました!

Slide 4

Slide 4 text

本題 📕

Slide 5

Slide 5 text

👦全人類「Goodコード書きたい 🔥」 Goodコード=高品質なコード

Slide 6

Slide 6 text

👦全人類「どうやって書けばいい? 🤯」

Slide 7

Slide 7 text

👨Tom Long「6つの柱を実践すればいい 👼」 Tom Long=著者。Googleでテックリード。

Slide 8

Slide 8 text

コード品質の柱 高品質なコードを書くための戦略として挙げられている6つの柱 1. 📖 : コードを読みやすくする 2. ❗️ : 想定外の事態をなくす 3. 👌 : 誤用しにくいコードを書く 4. 🤖 : コードをモジュール化 5. ♻️ : コードを再利用、汎用化しやすくする 6. ✏️ : テストしやすいコードを書く

Slide 9

Slide 9 text

柱の詳細(今回は2つのみ) ❗️ : 想定外の事態をなくす あるタスクをこなそうとした時に、想定外が起きることは悪いこと 例:レストランの注文で「マルゲリータ 🍕」と「マルガリータ 🍸」の注文は間違えやすい 👌 : 誤用しにくいコードを書く これをしないと作成者が想定していない利用方法で使われる 例:テレビの電源ケーブル 📺とHDMIケーブル 🖥は形が違うので誤用しようがない この2の柱を実現するために「コードでの契約」が必要!

Slide 10

Slide 10 text

ようやく本題「 『コードでの契約』だ 🧗‍♀️」

Slide 11

Slide 11 text

コードでの契約とは コードで他のエンジニアに、関数の使い方やどう言う挙動が期待できるかを伝えるメンタルモデル コード間で契約を結んでいるような世界観 呼び出し元は特定の義務を果たし、対価として呼び出したコードで値の取得や状態を変更できる

Slide 12

Slide 12 text

コードの契約:契約の中の細則 コードでの契約というメンタルモデルを頭に入れると、 「契約の中の細則」もコードの世界に落とし込める # 例:レンタサイクルのアプリ - 明確な契約 - レンタサイクルを借りられる - レンタル費用は150円/時 - 契約の中の細則 - 時速30kmを超えた場合は罰金5000円が発生する※ ※利用者が意識して監視する必要がある  (速度を抑制する機能がついていない) # 例:実際のコード - 明確な契約 - 関数とクラスの名前 - パラメーターの型 - 戻り値の型 - 契約の中の細則 - コメント - ドキュメント 言いたいこと 「契約の中の細則」は落とし穴のようなものになりがち → できる限りコードでの契約に寄せるべき

Slide 13

Slide 13 text

それでは、実際のコードを見てみよう 🤺 TypeScriptで記載しています

Slide 14

Slide 14 text

まずは、 「 ❗️想定外の事態をなくす」について

Slide 15

Slide 15 text

その1

Slide 16

Slide 16 text

マジックバリュー(固定値)を戻り値に使わない 例:ユーザーに関する情報を保存するクラスがあった場合 🙅 BadCode 🙆 GoodCode コードの作成者とレビュアーはこの動作を確認してい るはずだが 新規開発者が平均年齢を出す処理を getAge() を使っ て実装を行う場合バグる 呼び出し元でnullが来ることが検知でき、 新規開発者が落とし穴があることが把握できる class User { private age = null getAge():number{ if(this.age === null){ return -1 // 入力がない場合は-1を返す } return this.age } } class User { private age = null getAge():number | null{ if(this.age === null){ return null } return this.age } }

Slide 17

Slide 17 text

その2

Slide 18

Slide 18 text

呼び出し元を意識したnullオブジェクト利用を心がける 例:HTMLの class を見て強調しているか確認しているコード 🙅 BadCode 🙆 GoodCode getClassNames でnullが帰ってくる →呼び出しもとでnullチェックを 呼び出し元のコードがスッキリした function getClassNames(element: HTMLElement): Set | const attribute = element.getAttribute("class"); if (attribute === null) { return null; } return new Set(attribute.split(" ")); } function isElementHighlighted(element: HTMLElement): boolea const classNames = getClassNames(element); if (classNames === null) { // 使用まえにnullチェック return false; } return classNames.has("highlighted"); } function getClassNames(element: HTMLElement): Set { const attribute = element.getAttribute("class"); if (attribute === null) { return new Set(); } return new Set(attribute.split(" ")); } function isElementHighlighted(element: HTMLElement): boolea // null値チェックの必要がない return getClassNames(element).has("highlighted"); }

Slide 19

Slide 19 text

その3

Slide 20

Slide 20 text

将来的にも有効に使えるように列挙型処理を利用する 例:列挙型の条件分岐(下のコードに新しい列挙 Top が追加された場合) 🙅 BadCode 🙆 GoodCode コンパイルエラーを検知できない コンパイルエラーを検知できる 実装時にエラーに気づくことができる const Position = { Right: 0, Left: 1, } as const; type Position = (typeof Position)[keyof typeof Position]; // 上は type Position = 0 | 1 と同じ意味になります function toJapanese(position: Position) { if (position === Position.Right) { return "右"; } return "左"; } const Position = { Right: 0, Left: 1, } as const; type Position = (typeof Position)[keyof typeof Position]; // 上は type Position = 0 | 1 と同じ意味になります function toJapanese(position: Position) { switch (position) { case Position.Right: return "右"; case Position.Left: return "左"; } }

Slide 21

Slide 21 text

次に、 「 👌 : 誤用しにくいコードを書く」について

Slide 22

Slide 22 text

その1

Slide 23

Slide 23 text

全てを不変にする(ことを検討する) 例:フォント情報を保存するクラス 🙅 BadCode 🙆 GoodCode fontFamily が呼び出し元で書き換えられる fontFamily が ReadonlyArray なので不変 安心して使えるね class TextOptions { constructor( private fontFamily: Font[], private fontSize: number ){} getFontFamily(): Font[] { return this.fontFamily; } getFontSize(): number { return this.fontSize; } } class TextOptions { constructor( fontFamily: ReadonlyArray, private fontSize: number ) {} getFontFamily(): ReadonlyArray { return this.fontFamily; } getFontSize(): number { return this.fontSize; } }

Slide 24

Slide 24 text

その2 ⭐️最後 ⭐️

Slide 25

Slide 25 text

汎用的なデータ型は避ける 例:地図上に指定位置をマークするメソッド 🙅 BadCode 🙆 GoodCode class LocationDisplay { private readonly map: DrawableMap; /** * 与えられたすべての座標の位置を地図上にマークする * * リストのリストを受け入れる。内部のリストには正確に2つの値が含ま * 1番目の値は緯度、2番目の値は経度 (両方とも度) */ markLocationsOnMap(locations: Array<[number, number]>): for (const location of locations) { this.map.markLocation(location[0], location[1]) } } } /** 緯度と経度を度数で表す */ class LatLong { constructor( private readonly latitude: number, private readonly longitude: number) {} getLatitude = () => this.latitude; getLongitude =() => this.longitude; } } class LocationDisplay { private readonly map: DrawableMap; markLocationsOnMap(locations: LatLong[]): void { for (const location of locations) { this.map.markLocation(location.getLatitude,loca } } } 契約の中の細則(コメント)でカバーしようとしている 何を表しているかが明確なため誤用を避けられる

Slide 26

Slide 26 text

まとめ 高品質なコード(GoodCode)を書くには 6つの柱を満たす必要がある 今回は「 ❗️ : 想定外の事態をなくす」 、 「 👌 : 誤用しにくいコードを書く」を特集した コードでの契約 上記2つを達成する戦略として、コード上で縛りを入れる 具体例 マジックバリュー(固定値)を戻り値に使わない 呼び出し元を意識したnullオブジェクト利用を心がける 将来的にも有効に使えるように列挙型処理を利用する 全てを不変にする(ことを検討する) 汎用的なデータ型は避ける

Slide 27

Slide 27 text

No content