DelFusa Blog 総本山

プログラミングの話題とかです。

NEW | PAGE-SELECT | NEXT

≫ EDIT

スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

| スポンサー広告 | --:-- | comments(-) | trackbacks(-) | TOP↑

≫ EDIT

テストファーストという開発手法

Delphiの人たちは、人数が少ないので技術の普及がちょっと遅いんですよね。

デザインパターンも広まらないし、テストファーストも広まらない。

残念なことです。


言語に依存せずに
開発の品質を劇的に高める方法が、発見・発明されて、
たくさんユーザーのいる言語では
そのやり方を知っている人がたくさんいて解説本やWebページも
より多く出来てくるからなじみやすいですが

Delphiだと、人力が少ないので
そういう流れにならないのが、残念なんですね。


デザパタ、や、テストファースト、は
私は、活用しまくっている...とは言いませんが
とりあえず知っています。

その他、はやりの開発スタイル、開発方式は
アジャイルとかアスペクトですか、私、さっぱりわかりません。

Delphiで例題を書いてくれないと、理解できん!理解しにくい!
ちゅーのが、実際の所ですし、
Delphi使いの多くの人にとっても、そうだと思います。

だから、Delphiがもっと普及すればいいと、私は思うんですが
某の不甲斐なさが目立つ昨今では.....
.....、まあのんびりやりましょう。

と、ぼやいてばかりでも仕方ありません。

ここでは、テストファーストについて、書いてみましょう。
※確か昔も書いた気がするんだが、、、妄想だったかな?




エクストリームプログラミング(eXtremeProgramming→XPと略す)という
開発手法が数年前からはやってきています。

XPをやっている中でも、Delphi人口は少ないはずなので
あんまり知らない方も多いかと思いますが
結構な盛り上がりを見せています。

そこで、テストを最初に書け!=テストファースト
という考え方がお話しされています。

さて、実際、どういう場面に適応できるでしょうか。

小数点以下切捨てについて
http://hpcgi1.nifty.com/MADIA/DelphiBBS/wwwlng.cgi?print+200607/06070011.txt

タイムリーなので、こちらの話題から拾ってみますぜ!

この命題を解きたい場合、テストファーストではどうすればいいか。
サンプルソースコードを書いてみますね。
まずは、質問者のソースを関数化してみましょう。

割り算して小数点13位以下を切り捨てる処理

function KetaKirisute(ValueA, ValueB: Extended; Arg2: Integer): Extended;
var
 Arg1: Extended;
 UsWork: String;
 UsTen : Integer;
begin
 Arg1 := ValueA / ValueB;
 UsWork := FloatToStrF(Arg1,ffFixed,18,18);
 UsTen := Pos('.',UsWork);
 Result := StrToFloat(Copy(UsWork,1,UsTen + Arg2));
end;
//※元ソースのDoubleでは精度がでなさそうなので、Extendedにしちゃいました。
//※それでFloatToStrFも使っています。


…それにしても小数点指定部分で四捨五入するために
文字列変換って、極悪なコードだな…いかにも業務アプリ屋っぽいわ。
俺も昔、触った事もないJavaで1週間で納品しなきゃいけない事になって
それに似たような事書いたことあるけど、
技術力が著しく足りないってことは、自分でも分かるわけで
でも、納品しなきゃいけない、って縛りがあって、
つらいんですよね。

質問者の方も"正しいんでしょうか?"って書いてますが
やっぱり、いいわけないでしょ.....

こういうソースコードは基本的にダメだよね....

まあ、引き継いだ時のソースが9割方
こんなソースだったりする現場環境では
そんなことは、通じませんのは、知ってますけどね。


まあ、愚痴っているよりもコードを書きましょう。
テストコードが書けました。

procedure Check(A, B: Extended);
begin
 if not (A = B) then
  ShowMessage('あんたまちがってるよ! '+
    FloatToStr(A)+':'+FloatToStr(B));
end;

procedure CheckKetaKirisute;
begin
 Check(0.0000001666666, TestShishaGokyu(0.000002, 12, 13));
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
 CheckKetaKirisute;
end;

0.000002を12で割った時は0.000000166...と続きますので
小数点以下13桁目で切り捨てるわけです。

これでテストコードができました。



これがテストファーストです。

KetaKirisute関数が実装される前に(今回はされちゃってましたけど)
テストコードで結果を予測して、書いておく、
これがテストファースト。

もちろん、このテストは実は次の関数も通過してしまいます。


function KetaKirisute(ValueA, ValueB: Extended; Arg2: Integer): Extended;
begin
 Result := 0.0000001666666;
end;

いやあ、あまりにばかばかしいけど、テストを通過してしまいますよね。

でも、これではよろしくないわけで、
このコードを通過しないように
テストコードを改変する余地があるわけです。

改良しちゃいましょう。

procedure CheckKetaKirisute;
begin
 Check(0.0000001666666, KetaKirisute(0.000002, 12, 13));
 Check(0.0, KetaKirisute(0.000002, 12, 1));
 Check(0.00, KetaKirisute(0.000002, 12, 2));
 Check(0.000000, KetaKirisute(0.000002, 12, 6));
 Check(0.0000001, KetaKirisute(0.000002, 12, 7));
 Check(0.00000016, KetaKirisute(0.000002, 12, 8));
 Check(0.000000166, KetaKirisute(0.000002, 12, 9));
 Check(0.0000001666, KetaKirisute(0.000002, 12, 10));
 Check(0.3, KetaKirisute(1, 3, 1));
 Check(0.33, KetaKirisute(1, 3, 2));
 Check(0.333, KetaKirisute(1, 3, 3));
 Check(0.3333333333333, KetaKirisute(1, 3, 13));
end;
>
こんなもんですかね。これならどうでしょう。
テストコードは非常に簡単に増やせます。
さすがに、②のコードは通過するわけがありません。

①のコードはどうでしょうか?

おーー、実際にやってみるとちゃんと
メッセージは一つも表示されることなく、テストコードを通過してくれます。

これで、この
①のKetaKirisute関数は、仕様を満たした

と考えることができます。
テストコードを見てすぐに
品質を確保したという確証が得られるわけです。

まあ、ただし内部で文字列変換したりしていますので
速度という面はいまひとつなので

改良する方がいいんじゃないでしょうか。


こういう、関数名と外部への仕様が同じで中身の構造を
変更したりすることを、"リファクタリング"といいます。

これまた、XPで提唱されている方法です。

いいと思う事は、どんどん取り組むべきなんです。
TestShishaGokyuの元の実装が、文字列を使っているから低速であるから
改善の余地があれば改善すべきなんです!!!

ところが、世間のレベルの低い業務アプリ屋さんの間では通説になっているような
こんなセリフ、知ってます?

『中身の動きがよくわからないから、この関数はいじるな。』

業務系の仕事していたら、誰もが言われたことありません?こういうセリフ。

かーー、、、( ゜д゜)、ペッ

だから、おいら、業務アプリ屋さんには魅力感じないし尊敬もしないんす。
業界全体が低レベル過ぎる価値観、規約が多いんですもん。
低レベルを維持しなければいけないプログラム開発なんてつまらないでしょ。

中にはそんなくだらない規約がない、技術が高くてよい場所もあるでしょうから、
なるべくならレベルを下げてくれるような規約の無い場所で生きていきたいです。

だって、"糞みたいな関数"を放置しておかないといけないプログラムなんて
全体の品質が高いわけないじゃないですか。


まあ、いいや。
話を戻しましょう。

XPでは、リファクタリングが難しいと思われる場面でも
テストをしっかり書いておけば、ちゃんとリファクタリングすることができる。
と、主張されています。

まさにその通りです。


テストコードであるCheckKetaKirisuteをしっかり書いていれば、
KetaKirisute関数、このコードの中身の実装がどのようなものであれ
”テストを通過した”
という事実が成り立つわけです。

だから、テストを通過する、
KetaKirisute関数、の実装を文字列を使わずに行うときも

必ず、このテストを通過するように関数を作ればいいわけです。

既にそっくりの仕組みを、回答者のママんさんが提示してくれています。

ですので、さっきのテストをそっくりに書き換えてしまいましょう。

function CutOffDecimalPlace(Value:Extended ; DP:Byte):Extended;
var
 i:Integer;
 i64,p:Int64;
begin
 p:=1;
 for i:=0 to DP-1 do
  p:=p*10;
 i64:= Trunc(Value * p);
 Result := i64 / p;
end;

procedure CheckCutOffDecimalPlace;
begin
 Check(0.0000001666666, CutOffDecimalPlace(0.000002/12, 13));
 Check(0.0, CutOffDecimalPlace(0.000002/12, 1));
 Check(0.00, CutOffDecimalPlace(0.000002/12, 2));
 Check(0.000000, CutOffDecimalPlace(0.000002/12, 6));
 Check(0.0000001, CutOffDecimalPlace(0.000002/12, 7));
 Check(0.00000016, CutOffDecimalPlace(0.000002/12, 8));
 Check(0.000000166, CutOffDecimalPlace(0.000002/12, 9));
 Check(0.0000001666, CutOffDecimalPlace(0.000002/12, 10));
 Check(0.3, CutOffDecimalPlace(1/3, 1));
 Check(0.33, CutOffDecimalPlace(1/3, 2));
 Check(0.333, CutOffDecimalPlace(1/3, 3));
 Check(0.3333333333333, CutOffDecimalPlace(1/3, 13));
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
 CheckCutOffDecimalPlace;
end;

すべて通過です。
ママんさん、お見事。


元スレは途中から、違う話になってきちゃってますので、こんなものでおいておきましょう。

10.222222を、100倍して、
小数点を切り捨てて、100で割れば
10.22になる。ってことですね。

ちょっぴり毒も混じってネタにつかっちゃいました。

質問者の鳥さん、ごめんなさいね。
技術力アップさせていってください。

スポンサーサイト

| 未分類 | 21:45 | comments:1 | trackbacks:0 | TOP↑

COMMENT

なんだか、相当、毒を吐いていますね。⇒自分

嫌なことでもあったのかなあ。

さて、テストファーストについては、ここの書込もみるといいです。

きっと勉強になって、よい品質のコードが書けるようになるよーん。

ipアドレスが正しいかどうかを判定するには?
http://hpcgi1.nifty.com/MADIA/DelphiBBS/wwwlng.cgi?print+200801/08010007.txt


| ミ・д・彡 | 2008/03/14 00:46 | URL | ≫ EDIT















非公開コメント

TRACKBACK URL

http://delfusa.blog65.fc2.com/tb.php/24-e5bced2c

TRACKBACK

PREV | PAGE-SELECT | NEXT

上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。