Upgrade to PRO for Only $50/Year—Limited-Time Offer! 🔥
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
ぼくのかんがえたさいきょうのけいやくによるプログラミング
Search
seki at druby.org
August 05, 2023
Programming
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
140
ERB, ancient and future
m_seki
3
780
ERB Hacks
m_seki
1
1.3k
わりこまれるはなし
m_seki
0
700
Learn Ractor
m_seki
1
2.4k
How many copies did you buy the first print?
m_seki
1
660
Create my own search engine.
m_seki
1
5.2k
Rinda in the real-world embedded systems.
m_seki
0
390
xpjug2019 A-4: チケットシステムの設計と実装、『あのチーム』の運用
m_seki
6
6.4k
Other Decks in Programming
See All in Programming
新規学習のハードルを下げる方法とは?/ How to Make Learning Something New Easier?
nobuoooo
1
140
ソフトウェアの振る舞いに着目し 複雑な要件の開発に立ち向かう
rickyban
0
830
HTTP compression in PHP and Symfony apps
dunglas
2
1.5k
TypeScript でバックもやるって実際どう? 実運用で困ったこと3選
yuichiro_serita
17
7.6k
flutterkaigi_2024.pdf
kyoheig3
0
470
複雑な仕様に立ち向かうアーキテクチャ
myohei
0
130
MoQとか勉強会#2 発表資料
yuki_uchida
2
650
React CompilerとFine Grained Reactivityと宣言的UIのこれから / The next chapter of declarative UI
ssssota
7
3.4k
あれやってみてー駆動から成長を加速させる / areyattemite-driven
nashiusagi
1
160
我々のデザインシステムは Chakra v3 にアップデートします
shunya078
2
3k
42 best practices for Symfony, a decade later
tucksaun
1
140
Djangoの開発環境で工夫したこと - pre-commit / DevContainer
hiroki_yod
1
670
Featured
See All Featured
Improving Core Web Vitals using Speculation Rules API
sergeychernyshev
0
53
Become a Pro
speakerdeck
PRO
25
5k
Docker and Python
trallard
41
3.1k
Building an army of robots
kneath
302
44k
Build your cross-platform service in a week with App Engine
jlugia
229
18k
Learning to Love Humans: Emotional Interface Design
aarron
273
40k
The World Runs on Bad Software
bkeepers
PRO
65
11k
Making the Leap to Tech Lead
cromwellryan
133
9k
StorybookのUI Testing Handbookを読んだ
zakiyama
27
5.3k
The Cult of Friendly URLs
andyhume
78
6.1k
Statistics for Hackers
jakevdp
796
220k
Building a Scalable Design System with Sketch
lauravandoore
459
33k
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