Software Engineering Lecture 6/20
Menu Menu
先週の復習
- 図の送り方
- Use case
- 単一継承と多重継承
シーケンス図と協調図
シーケンス図と協調図は、オブジェクト間の通信の様子を表す。
協調図はメッセージに順序付けのための番号が付加されている。
メッセージの表記の仕方は以下のようになっている。
return := message(parameter : parameterType) : returnTypeさらに繰り返しの記号を付けることもできる。
1*: [i := 1..10] li := nextLineItem(): SaleLineItem
問題
カードゲームのオブジェクトには、カードの集合を表すカードパイルがある。カードパイルにopen_all()を送ると、そのカードパイルのカード全体がオープンされる様子を協調図で示せ。
Perl/Tk
Perl/Tk はPerlを使ってX-Windowを操作するオブジェクト指向ライブラリである。Perl/Tkの例題
use Tk; $top = MainWindow->new(); $text = $top->Text(); $text->pack(); $text->insert('end',"test string"); $text->see('end'); $top->update; $top->MainLoopl
Widget
Perl/Tk では、Wigetという部品を組み合わせてアプリケーションを作る。部品の画面表示の組み合わせ方は、pack で制御する。
- expand 伸び縮みするかどうか
- fill 埋めるかどうか
- side どちらに寄せるか
Perl/Tkによる記述
Perl/Tk では、MainWindow を作り、そこに、いろいろなwidgetをpack していく。
my $main = MainWindow->new();とすれば、MainWindowができる。MainWindowのインスタンスとして、いろいろなものを使うことができる。
Label | 文字を印として置く |
Text | 編集用のテキスト |
Entry | 入力カラム |
Canvas | 図形表示 |
Frame | いくつかのwidgetをまとめる |
Canvas とタグ
Canvasには以下のような図を書くことができる。
oval | 楕円 |
rectangle | 箱 |
line | 線 |
polygon | 多角形 |
bitmap | イメージ |
text | テキスト |
これらは、CanvasにタグやIDで指定することによって移動したり変形したりすることができる。
$canv->move($ball, $deltax, $deltay); | 移動 |
$canvas->itemconfigure($view,-fill=>'black'); | 色などの変更 |
$canvas->delete($view); | 削除 |
これらに関しては man canvas でもだいたいのことを知ることができる。
状態図
協調図は、実際に行われる動作の一部を表すに過ぎない。より完全なオブジェクトの動作は、状態モデルによって表される。
状態モデル
オブジェクトは、いつも同じような動作をするわけではない。あるメッセージを受け取った後には、異なる振る舞いをするのが普通である。例えば、- 電子レンジの扉を開ける
- 食品を入れる
- 扉を閉める
- スィッチを入れる
- 加熱中
- 加熱終了
- 扉を開ける
- 食品を出す
- 扉を閉める
しかし、オブジェクトはまったく予想できない動作をするわけではない。この場合でも、この一連の動作は一種のループになっていて、ループの各点では同じ動作をすると期待される。このようなループがオブジェクトライフサイクルを構成する。
「扉を閉める」「スィッチ」などがオブジェクトの状態を変えるきっかけである。これらを「イベント」と呼ぶのが普通である。オブジェクトの状態はイベントによって変わる、あるいは、オブジェクトの状態を変えるものがイベントである。
この(個々の)オブジェクトの振る舞いを表すのには、状態遷移図を使うのが普通である。例えば、一番簡単な例として電灯の状態遷移図を書いてみよう。
問題
電子レンジの振る舞いを表す状態遷移図を記述してみよ
package SignalView; use Tk; sub new { my $type = shift; my %params = @_; my $self = {}; my $signal = Signal->new(%params); my $main = $params{-main}; my $canvas; my $states = $signal->{'States'}; my %views = (); if(! defined( $main)) { $main = MainWindow->new(%params); } $canvas = $main->Canvas(-width=>50,-height=>150); $x = 5; $y = 5; $r = 40; for $state ( @$states ) { $views{$state} = $canvas->create('oval', $x, $y, $x+$r, $y+$r, -width => 1, -outline => 'black', -fill => 'black'); $y += 50; } $canvas->pack(); $self->{'MainWindow'} = $main; $self->{'Canvas'} = $canvas; $self->{'Views'} = \%views; $self->{'Signal'} = $signal; bless $self; } sub next { my ($self) = shift; my $state = $self->{'Signal'}->{'State'}; my $view = $self->{'Views'}->{$state}; my $canvas = $self->{'Canvas'}; $canvas->itemconfigure($view,-fill=>'black'); $state = $self->{'Signal'}->next(); $view = $self->{'Views'}->{$state}; $canvas->itemconfigure($view,-fill=>$state); } sub update { my ($self) = shift; $self->{'MainWindow'}->update(); } package Main; $signal = SignalView->new(); $signal->update(); while($i++<100) { $signal->next(); $signal->{'Signal'}->print(); print "\n"; $signal->update(); sleep(1); }この方法では、信号は自動的に点滅するという感じではない。そのためには、after という機能を使うことができる。
package MainWindow; sub Signal { my ($self) = shift; return SignalView->new(-main=>$self); } package SignalView; sub new { my $type = shift; my %params = @_; my $self = {}; my $signal = Signal->new(%params); my $main = $params{-main}; my $canvas; my $states = $signal->{'States'}; my %views = (); $self->{'Interval'} = 500; $canvas = $main->Canvas(-width=>50,-height=>150); $x = 5; $y = 5; $r = 40; for $state ( @$states ) { $views{$state} = $canvas->create('oval', $x, $y, $x+$r, $y+$r, -width => 1, -outline => 'black', -fill => 'black'); $y += 50; } $canvas->pack(-side=>left); $self->{'MainWindow'} = $main; $self->{'Canvas'} = $canvas; $self->{'Views'} = \%views; $self->{'Signal'} = $signal; bless $self; } sub next { my ($self) = shift; my $state = $self->{'Signal'}->{'State'}; my $view = $self->{'Views'}->{$state}; my $canvas = $self->{'Canvas'}; $canvas->itemconfigure($view,-fill=>'black'); $state = $self->{'Signal'}->next(); $view = $self->{'Views'}->{$state}; $canvas->itemconfigure($view,-fill=>$state); } sub update { my ($self) = shift; $self->{'MainWindow'}->update(); } sub run { my ($self) = shift; my %params = @_; my $main=$self->{'MainWindow'}; if(defined($params{'Interval'})) { $self->{'Interval'} = $params{'Interval'}; } if(! $self->{'Running'}) { $self->{'Running'} = 1; $main->after($self->{'Interval'},sub {$self->running()}); } } sub running { my ($self) = shift; my $interval = $self->{'Interval'}; if($self->{'Running'}) { $self->next(); $self->update(); $self->{'MainWindow'}->after($interval,sub {$self->running()}); } } sub stop { my ($self) = shift; $self->{'Running'} = 0; } package Main; use Tk; $main = MainWindow->new(); $signal = $main->Signal(); $signal->run(); &MainLoop();
Call Back と Controller
この信号をさらに外から制御するには、controller をViewに付加する。
ボタンからの情報を処理するにはCall Backを使う。Call Back では、Call Back する先を、my 変数により保持する。これをClosure という。オブジェクトを使っても同じことが実現できるが、Closureの方が構文的には軽い。
package SingalView; sub controller { my ($self) = shift; my $main=$self->{'MainWindow'}; # my $c= $main->Toplevel(); my $c= $main->Frame(); my $int= $c->Frame(); my $int_entry = $int->Entry(-relief => 'sunken',-width=>5); my $int_label= $int->Label(-text=>"Interval:"); my $quit = $c->Button( -text => 'Quit', -width => 10, -command => sub {$main->destroy}); my $run = $c->Button( -text => 'Run', -width => 10, -command => sub { $self->run(Interval=>($int_entry->get())) } ); my $stop = $c->Button( -text => 'Stop', -width => 10, -command => sub {$self->stop}); $int->pack(-side => 'top', -fill => 'x'); $int_entry->pack(-side => 'right'); $int_label->pack(-side => 'left'); $c->pack( -side => 'right', -expand => 'no', -pady => 2); $int->pack(-side => 'top', -expand => 'no', -pady => 2); $run->pack( -side => 'top', -expand => 'no', -pady => 2); $stop->pack(-side => 'top', -expand => 'no', -pady => 2); $quit->pack(-side => 'top', -expand => 'no', -pady => 2); # set default value $int_entry->insert(0,$self->{'Interval'}); $self->{'Controller'}=$c; $self->update(); } # end
問題
この信号の実装を表す概念モデルを作成せよ。信号の振舞を記述する協調図とシーケンス図を作成せよ。
宿題
電子レンジの振る舞いを表す状態遷移をPerlで記述してみよ。また、Perl/Tk による GUI を付けよ。解答は、kono@ie.u-ryukyu.ac.jp まで、来週までにメールで送ること。Subject には、
Report on Software Engineering Lecture 6/20を付けること。