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
1
1.1k
ぼくのかんがえたさいきょうのけいやくによるプログラミング
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
RWC 2024 DICOM & ISO/IEC 2022
m_seki
0
330
ERB, ancient and future
m_seki
3
830
ERB Hacks
m_seki
1
1.4k
わりこまれるはなし
m_seki
0
760
Learn Ractor
m_seki
1
2.5k
How many copies did you buy the first print?
m_seki
1
700
Create my own search engine.
m_seki
1
5.2k
Rinda in the real-world embedded systems.
m_seki
0
410
xpjug2019 A-4: チケットシステムの設計と実装、『あのチーム』の運用
m_seki
6
6.5k
Other Decks in Programming
See All in Programming
コードを読んで理解するko build
bells17
1
110
Djangoアプリケーション 運用のリアル 〜問題発生から可視化、最適化への道〜 #pyconshizu
kashewnuts
1
260
Code smarter, not harder - How AI Coding Tools Boost Your Productivity | Angular Meetup Berlin
danielsogl
0
100
『GO』アプリ データ基盤のログ収集システムコスト削減
mot_techtalk
0
150
PHP ステートレス VS ステートフル 状態管理と並行性 / php-stateless-stateful
ytake
0
110
Datadog DBMでなにができる? JDDUG Meetup#7
nealle
0
140
推しメソッドsource_locationのしくみを探る - はじめてRubyのコードを読んでみた
nobu09
2
140
PHPのバージョンアップ時にも役立ったAST
matsuo_atsushi
0
220
コミュニティ駆動 AWS CDK ライブラリ「Open Constructs Library」 / community-cdk-library
gotok365
2
240
たのしいSocketのしくみ / Socket Under a Microscope
coe401_
8
1.1k
もう僕は OpenAPI を書きたくない
sgash708
5
1.9k
DRFを少しずつ オニオンアーキテクチャに寄せていく DjangoCongress JP 2025
nealle
2
260
Featured
See All Featured
Chrome DevTools: State of the Union 2024 - Debugging React & Beyond
addyosmani
4
360
Principles of Awesome APIs and How to Build Them.
keavy
126
17k
I Don’t Have Time: Getting Over the Fear to Launch Your Podcast
jcasabona
32
2.1k
Building Better People: How to give real-time feedback that sticks.
wjessup
367
19k
"I'm Feeling Lucky" - Building Great Search Experiences for Today's Users (#IAC19)
danielanewman
226
22k
Why Our Code Smells
bkeepers
PRO
336
57k
For a Future-Friendly Web
brad_frost
176
9.6k
Visualization
eitanlees
146
15k
Put a Button on it: Removing Barriers to Going Fast.
kastner
60
3.7k
Into the Great Unknown - MozCon
thekraken
35
1.6k
Git: the NoSQL Database
bkeepers
PRO
427
65k
Typedesign – Prime Four
hannesfritz
40
2.5k
Transcript
ぼくのかんがえたさいきょう の契約によるプログラミング seki@ruby-lang.org これを酒匂さんの前で話すのすごいぞ!
今日の話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