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
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
210
ERB, ancient and future
m_seki
3
800
ERB Hacks
m_seki
1
1.3k
わりこまれるはなし
m_seki
0
710
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
400
xpjug2019 A-4: チケットシステムの設計と実装、『あのチーム』の運用
m_seki
6
6.4k
Other Decks in Programming
See All in Programming
Recoilを剥がしている話
kirik
5
6.8k
Jakarta EE meets AI
ivargrimstad
0
250
これでLambdaが不要に?!Step FunctionsのJSONata対応について
iwatatomoya
2
3.7k
CQRS+ES の力を使って効果を感じる / Feel the effects of using the power of CQRS+ES
seike460
PRO
0
130
今年一番支援させていただいたのは認証系サービスでした
satoshi256kbyte
1
260
PHPとAPI Platformで作る本格的なWeb APIアプリケーション(入門編) / phpcon 2024 Intro to API Platform
ttskch
0
250
テストコードのガイドライン 〜作成から運用まで〜
riku929hr
4
580
アクターシステムに頼らずEvent Sourcingする方法について
j5ik2o
4
280
17年周年のWebアプリケーションにTanStack Queryを導入する / Implementing TanStack Query in a 17th Anniversary Web Application
saitolume
0
250
Kaigi on Railsに初参加したら、その日にLT登壇が決定した件について
tama50505
0
100
今年のアップデートで振り返るCDKセキュリティのシフトレフト/2024-cdk-security-shift-left
tomoki10
0
200
[JAWS-UG横浜 #76] イケてるアップデートを宇宙いち早く紹介するよ!
maroon1st
0
460
Featured
See All Featured
A Tale of Four Properties
chriscoyier
157
23k
No one is an island. Learnings from fostering a developers community.
thoeni
19
3k
Visualization
eitanlees
146
15k
The Power of CSS Pseudo Elements
geoffreycrofte
73
5.4k
CSS Pre-Processors: Stylus, Less & Sass
bermonpainter
356
29k
Rebuilding a faster, lazier Slack
samanthasiow
79
8.7k
What’s in a name? Adding method to the madness
productmarketing
PRO
22
3.2k
Distributed Sagas: A Protocol for Coordinating Microservices
caitiem20
330
21k
KATA
mclloyd
29
14k
Why You Should Never Use an ORM
jnunemaker
PRO
54
9.1k
Building a Modern Day E-commerce SEO Strategy
aleyda
38
7k
The World Runs on Bad Software
bkeepers
PRO
65
11k
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