Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
ぼくのかんがえたさいきょうのけいやくによるプログラミング
Search
seki at druby.org
August 05, 2023
Programming
2
1.2k
ぼくのかんがえたさいきょうのけいやくによるプログラミング
toRuby拡大版 (2023-08-05)
seki at druby.org
August 05, 2023
Tweet
Share
More Decks by seki at druby.org
See All by seki at druby.org
XP, Testing and ninja testing
m_seki
2
110
RWC 2024 DICOM & ISO/IEC 2022
m_seki
0
440
ERB, ancient and future
m_seki
3
890
ERB Hacks
m_seki
1
1.5k
わりこまれるはなし
m_seki
0
830
Learn Ractor
m_seki
1
2.7k
How many copies did you buy the first print?
m_seki
1
740
Create my own search engine.
m_seki
1
5.3k
Rinda in the real-world embedded systems.
m_seki
0
440
Other Decks in Programming
See All in Programming
Cursor AI Agentと伴走する アプリケーションの高速リプレイス
daisuketakeda
1
120
C++20 射影変換
faithandbrave
0
500
AIコーディング道場勉強会#2 君(エンジニア)たちはどう生きるか
misakiotb
1
240
『自分のデータだけ見せたい!』を叶える──Laravel × Casbin で複雑権限をスッキリ解きほぐす 25 分
akitotsukahara
1
300
今ならAmazon ECSのサービス間通信をどう選ぶか / Selection of ECS Interservice Communication 2025
tkikuc
11
2.8k
来たるべき 8.0 に備えて React 19 新機能と React Router 固有機能の取捨選択とすり合わせを考える
oukayuka
2
820
関数型まつり2025登壇資料「関数プログラミングと再帰」
taisontsukada
2
840
無関心の谷
kanayannet
0
180
Bytecode Manipulation 으로 생산성 높이기
bigstark
2
360
生成AIコーディングとの向き合い方、AIと共創するという考え方 / How to deal with generative AI coding and the concept of co-creating with AI
seike460
PRO
1
320
既存デザインを変更せずにタップ領域を広げる方法
tahia910
1
240
セキュリティマネジャー廃止とクラウドネイティブ型サンドボックス活用
kazumura
1
190
Featured
See All Featured
Design and Strategy: How to Deal with People Who Don’t "Get" Design
morganepeng
130
19k
実際に使うSQLの書き方 徹底解説 / pgcon21j-tutorial
soudai
PRO
181
53k
Reflections from 52 weeks, 52 projects
jeffersonlam
351
20k
A Modern Web Designer's Workflow
chriscoyier
693
190k
Building Applications with DynamoDB
mza
95
6.5k
[Rails World 2023 - Day 1 Closing Keynote] - The Magic of Rails
eileencodes
35
2.3k
Speed Design
sergeychernyshev
31
1k
A better future with KSS
kneath
239
17k
CSS Pre-Processors: Stylus, Less & Sass
bermonpainter
357
30k
How To Stay Up To Date on Web Technology
chriscoyier
790
250k
The Cost Of JavaScript in 2023
addyosmani
51
8.4k
Save Time (by Creating Custom Rails Generators)
garrettdimon
PRO
31
1.2k
Transcript
ぼくのかんがえたさいきょう の契約によるプログラミング
[email protected]
これを酒匂さんの前で話すのすごいぞ!
今日の話2 契約によるプログラミングの紹介 20世紀に書いたときの自慢(めっちゃバグ少ないよ) Unit Testingへ 2
歴史(自分にとっての) 1980 - 構造体を発明して喜んでいたころ 1988 - ADT, OOP 1990 -
オブジェクト指向入門!!! 1999 - Ruby Workshop ???? - TDD ???? - 形式手法のなかま ???? は忘れてしまったの意 3
情報が非常に少ない80年代 雑誌記事くらいしかなかった ダンプリストを打ち込む日々 構造体を再発明していた 同じ属性を集めて配列(添え字がオブジェクトの識別 子)にするBASIC的なプログラミングよりも、オブ ジェクトごとにデータをまとめて配置するほうが書きや すいぞ! のちに構造体という概念を知る 4
ADTとOOP (88-89年) 抽象データ型の授業があっておもしろかった! テキストはModula-2だったが処理系はなかった THINK Pascal(Object Pascal)で実験 英語のテキストを先生が和訳してくれていた(と思う) 5
オブジェクト指向入門!!! めっちゃ読んだ! 全然入門書ではない Eiffelという言語の神話(試せないんだもん) 型を使うっていう意味を知る いろいろすごい本だが表明を使ったプログラミングが めっちゃすごい 契約によるプログラミング 初版の著者名が誤っている(酒しか合っていない) 6
表明(assertion) 実行可能なコードにその目的を表したものを付加 実現方法とは無関係にその要素が何をしなければならな いのかを記述する たしかにCのassert()はそうなってるなー 7
RubyWorkshop(1999) 日本オラクルでやってた https://www.jus.or.jp/workshop/ruby/ruby.html encoding: euc-jp Rubyのrequire, ensureはEiffelから、みたいなこと を言ってたよ! ただし意味は全く違う Matzもオブジェクト指向入門を読んでいた!!
8
契約によるプログラミング クラス(モジュール)と顧客(アプリケーション)の関係は それぞれの権利と義務を表した契約と考える pre の条件を満たした状態で r を呼び出すことを約束 してくださるならば、お返しに私は post の条件を満た
す状態を最終的に実現することをお約束します (p.159) エラーチェックを誰が行うべきか明確にする 今世紀では「契約による設計」"DbC"ということが多いかも 9 pre-condition 事前条件
事前条件が満たされないとき r (ルーチン・メソッド)はなにをしてもよい クラッシュするかもしれないし、無限ループかもしれ ないし、デタラメな値を返すかもしれない 満たされない場合に備えたコードを書かないってこと 10
冗長な検査はよくないよ 渡されたデータが正しい処理のための条件を満たしてい るかどうか、絶えず検査することになる → 複雑さの原 因 ルーチンの中でチェックする?それとも呼び出し側? モジュールの責任分担を決めないと、全くチェックされ ないか、念のため何度もチェックすることになる 冗長な検査がシステムの中に散らばってしまうと、簡素
さが失われ、拡張性、わかりやすさ、保守性も失われる 冗長な検査は複雑さを招くし遅くなるしわかりにくく!技術的負債だよ 11 この方が好ましいと する派閥もある
組織的に事前条件を使おう 事前条件が満たされているものとしてルーチンを書ける コードは簡素化され、読みやすさ、保守性があがるぞ プログラミングスタイルが変わると思うからやってみて 呼び出し時の表明の違反を検出できる おかしな引数を渡すことはほぼなくなる そういうテストも不要 12
Eiffelによる支援 表明の例 引数の型の情報と表明が両方そろってインターフェイスである 13 class STACK export ... push(x: T)
is require not full do ... ensure not empty; top = x; nb_elements = old nb_elements + 1 end; require = 事前条件 fullのときは呼べないよ ensure = 事後条件 emptyではないよ 一番上の要素はxだよ 要素数は旧要素数よりも1増えるよ oldで変更前の値を参照できるぞ oldのほかにnochangeもあるらしい
定義域と値域 事前条件は関数の定義域、事後条件は値域 関数を合成するときに値域と定義域がマッチしていなく てはらないぞ! 定義域に相当するクラスを作る (利用者は引数の型と表明を注意深く見る) 14 foo(bar(x))
定義域と値域と継承 互換性をもって拡張するとは(つまり継承とか) 事前条件をより弱く、事後条件をより強く 定義域をより広く、値域はより狭く 15
Cでやってみた 契約によるプログラミングをCでやろう 事前条件だけでも効果あるな assert()使えば記述できそうだな 定義域に相当するクラスを使おう 値域をできるだけ狭くしよう プログラミングでやるぞ 90年代前半の試み。GUI/Network/重い並行処理とかあるようなシステムでやったよ 16
assert()で記述できそうだ 真となる式を書いて表明 偽だと式を印字してクラッシュしてくれる コンパイルオプションで無効にできる assert()を満たせないのはプログラムの誤りなので続行しちゃだめ 17 void MyStackPush(MyStack_p self, Element_p
element) { assert(self); assert(element); assert(! myStackIsFull(self)); ... }
定義域に相当するクラス 検査済みという情報をオブジェクトの存在で表現する そのオブジェクトが存在するならvalidation済み validならURLオブジェクト、invalidならNULL UIや通信など外からやってくる文字列や数値をそのまま扱わない 18 URL_p URLCreate(const char *str)
{ ... assert(str); if (! urlParse(str, &desc)) return NULL; ... return self; }
不透明な型の表現 ADTをopaqueな型で書く インターフェイスに実装を載せない うっかり自分で生成したりしないですむ C++(を自然に使う)よりも実装を隠せてよい 19 struct App_s; typedef struct
App_s * App_p; App_p AppCreate(Bar_p bar, Baz_p baz); void AppForget(App_p app); https://www.jpcert.or.jp/sc-rules/c-dcl12-c.html Create / ForgetはEiffelを 真似した
値域を狭くする 呼び出し結果を適切に扱うのは呼び出し側の責任 例:ときどきエラーを返します! エラーをハンドリングするのはアプリ側...しんどい 実行時エラーを極小にする 事前条件を工夫して呼び出し前に気づけるようにとか malloc()のエラーなんて現実的には回避策がないので 単にクラッシュするのはあきらめる ありえない領域を要求するのもバグの一種だぞ どうせならvoidが好き
20
プログラミング 設計!というよりプログラミング中にやったよ 一行書くたびに契約について考える OOPは好きだけどOODはよくわからない 21
利用者側から書く だいたい利用者側から書く 使う側はどんなオブジェクトがあったら楽かな 操作から必要な型を考える 22 ... StrList_p list; list =
StrListCreate(); for (i = 1; i < argc; i++) { if (! argv[i]) break; StrListPush(list, argv[i]); } s = StrListJoin(list, ",");
クラス側もほぼ同時に書く インターフェイスと同時にassertを書く 実装はそのあと(意識はするけど) 絶えずコンパイルを通しながら書く malloc()のエラーはとりあえず考えないことが多かった 23 void StrListPush(StrList_p self, const
char * str) { assert(self); assert(str); /* ͋ͱͰ */ }
やっと中身を実装する 書くの楽 自分の事前条件は満たされてる 自分が誰かを呼ぶ時の事前条件を満たすことに集中 この辺でやっと「実行して試す」状況になる インターフェイスにまつわる問題のバグはないから、動 作そのものを確かめる いわゆるUnitTestはやらなくて、小さなアプリケー ションでテストしてた 24
結果 めちゃくちゃコードが短くなった バグ少ない せきのコードのバグが異常に少ないのはなぜか問題 契約に導かれたプログラミングはとてもよい Cの表明の表現力が貧弱だとしても、表明を読み書き しながらプログラミングするのは効果があった 21世紀も一部で引き継がれている 何割かはMFCというかC++に汚染されてしまった 25
自慢はここまでだ TDD テスト駆動開発 UnitTest TDDでなくても使う言葉? ここでは使い分けるのめんどくさいからUnitTestって 呼びます 26
事前条件・事後条件を見てると UnitTestのtest caseを連想する UnitTestは表明のようなロジックではなく、例示(値の組)で表現されてる気がする 27 class STACK export ... push(x:
T) is require not full do ... ensure not empty; top = x; nb_elements = old nb_elements + 1 end;
表明とUnitTest Eiffelの表明は実行時検査できる つまり実行しないとダメ UnitTestのtest caseって表明の部分を切り出して実 行するようなモノだろうか 28
表明とUnitTest インターフェイスって比較的安定してるからその領域の テストなら頻繁に試す必要あるのかな... 自分たちのプロダクツでUnitTestは非常に少なくアプ リケーション全体の振る舞いのテストが大半を占めるの を思い出した 29
表明とモデル検査 test caseで例示できる値の組みは表明が表現してる集 合のごく一部 ほんとに大丈夫なのか? 形式手法のモデル検査みたいな方がよいのでは? こっちの話題は疎いので自信ない 30
プログラミングスタイルとして テストに導かれてプログラミングする 表明に導かれてプログラミングする どちらも似てるけど表明の方がわかりやすい印象でっす 型でプログラミングするってこういうことだと思う IDEの補完のためじゃないだろ 31