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を付けること。