Software Engineering Lecture 6/20

Menu Menu


先週の復習


シーケンス図と協調図

シーケンス図と協調図は、オブジェクト間の通信の様子を表す。

協調図はメッセージに順序付けのための番号が付加されている。


メッセージの表記の仕方は以下のようになっている。

    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 で制御する。

/usr/contrib/bin/widget を参考にすること


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 でもだいたいのことを知ることができる。




状態図

協調図は、実際に行われる動作の一部を表すに過ぎない。より完全なオブジェクトの動作は、状態モデルによって表される。


状態モデル

オブジェクトは、いつも同じような動作をするわけではない。あるメッセージを受け取った後には、異なる振る舞いをするのが普通である。例えば、
  1. 電子レンジの扉を開ける
  2. 食品を入れる
  3. 扉を閉める
  4. スィッチを入れる
  5. 加熱中
  6. 加熱終了
  7. 扉を開ける
  8. 食品を出す
  9. 扉を閉める
という操作を考えよう。この時、同じ「スィッチを入れる」という操作でも扉が開いている時には、加熱が始まってしまってはいけない。関数型言語では本当は、そういうことはない。同じ入力ならば、同じ出力を返すのが関数なはずである。このように、同じ入力でも違った動作をすることを、オブジェクトが状態を持つという。

しかし、オブジェクトはまったく予想できない動作をするわけではない。この場合でも、この一連の動作は一種のループになっていて、ループの各点では同じ動作をすると期待される。このようなループがオブジェクトライフサイクルを構成する。

「扉を閉める」「スィッチ」などがオブジェクトの状態を変えるきっかけである。これらを「イベント」と呼ぶのが普通である。オブジェクトの状態はイベントによって変わる、あるいは、オブジェクトの状態を変えるものがイベントである。

この(個々の)オブジェクトの振る舞いを表すのには、状態遷移図を使うのが普通である。例えば、一番簡単な例として電灯の状態遷移図を書いてみよう。


問題

電子レンジの振る舞いを表す状態遷移図を記述してみよ

    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 

を付けること。

Shinji KONO / Mon Jun 26 12:10:44 2000