Software Engineering Lecture 7/3

Menu Menu


先週の復習


非オブジェクト指向言語で、オブジェクト指向プログラミングできるのか?

C++ や、Java がある意味で主流になっているので、オブジェクト指向でない言語は軽視されがちである。しかし、オブジェクト指向にも若干の問題がある。

    オブジェクト指向は、大きなプログラムに適した技術 (Scripting などには向かない)
    メッセージ送信が重い
    オブジェクト間の調整に実行時間をとられ、実際の処理になかなかいきつかない
    コードが大きくなりがち
    クラスの設計が悪いとあとあとまで尾を引く、なので、まず設計に時間をかける必要がある

したがって、組み込み型システム(Embeded System)や、Real-time システムでは、非オブジェクト型のプログラム(たとえ、オブジェクト指向言語を使っていても)を行なうことが多い。

また、アルゴリズムとオブジェクト指向とは独立な概念であり、単に、プログラム勉強するだけならば、オブジェクト指向を勉強する必要はない。

一方で、オブジェクト指向言語が使えない場合でも、オブジェクト指向型のプログラムをすることが可能である。

    オブジェクト => 構造体に置き換え
    メッセージ送信 => 構造体をさすポインタを含む関数呼出し

とすれば良い。しかし、この方法では、オブジェクト指向言語が行なうような、メッセージ送信時に適切なオブジェクトのクラスで定義された関数を呼び出すことは、自分で面倒を見る必要がある。それには、いくつか方法がある。

    構造体に関数へのポインタの表を埋め込んでおく
	&{$self->{method1}}($arguments)
    構造体にパッケージ名を入れておき、それを呼び出す
	$self->{package}::btree_insert($self,$arg)
    オブジェクト側で、構造体のクラスを判定し、それを呼び出す
    メッセージ送信用の関数を作り、method をハッシュテーブルに入れて
	探索し呼び出す
    最初から自分で適切な関数を使う
	&btree_insert($self,$arg)
	&btree::insert($self,$arg)

実際には、メッセージ送信の時点で、どのメソッドが呼び出されるのかは、プログラマは理解しているのが大半であり、最後の方法が効果的である。これは、結局は、関数名に適切な名前や、パッケージ化をすることに相当する。

GhostScript は、C でオブジェクト指向を仮想的に実現している。

実際のオブジェクト指向言語でも、これらの方法をメッセージ送信時あるいはコンパイル時に判断しているので、プログラムとしての実質的な差はない。オブジェクト指向技術は人間のための技術であって、コンピュータのための技術ではないである。


スクロール / Call backのパターン

    my $c = $w->Canvas(
        -relief       => 'sunken',
        -bd           => 2,
        -scrollregion => ['-10c', '-10c', '50c', '20c'],
    );
    my $w_vscroll = $w->Scrollbar(-command => [$c => 'yview']);
    my $w_hscroll = $w->Scrollbar(-command =>
                                  [$c => 'xview'], -orient => 'horiz');
    $c->configure(-xscrollcommand => [$w_hscroll => 'set'],
                  -yscrollcommand => [$w_vscroll => 'set']);
    $w_vscroll->pack(-side => 'right', -fill => 'y');
    $w_hscroll->pack(-side => 'bottom', -fill => 'x');
    $c->pack(-expand => 'yes', -fill => 'both');


ドラッグ

    $c->bind('point', '<1>' => [sub {plot_down(@ARG)}, \%pinfo]);
    $c->bind('point', '<ButtonRelease-1>' => sub {shift->dtag('selected')});
    $c->Tk::bind('<B1-Motion>' => [sub {plot_move(@ARG)}, \%pinfo]);
    sub plot_down {
	my($w, $pinfo) = @ARG;
	my $e = $w->XEvent;
	my($x, $y) = ($e->x, $e->y);
	$w->dtag('selected');
	$w->addtag('selected', 'withtag', 'current');
	$w->raise('current');
	$pinfo->{'lastX'} = $x;
	$pinfo->{'lastY'} = $y;
    } # end plot_down
    sub plot_move {
	my($w, $pinfo) = @ARG;
	my $e = $w->XEvent;
	my($x, $y) = ($e->x, $e->y);
	$w->move('selected',  $x-$pinfo->{'lastX'}, $y-$pinfo->{'lastY'});
	$pinfo->{'lastX'} = $x;
	$pinfo->{'lastY'} = $y;
    } # end plot_move


ドラッグ&ドロップ

問題

以下の仕組みを協調図を使って表示して見よ。


ソフトウェア・パターン

オブジェクトは、組み合わせ(Object Composition) で作られていくようにすると、継承よりも、より柔軟な構造を作ることができる。これを、一つの知識体系としてまとめたものの一つが、デザイン・パターンあるいは、ソフトウェア・パターンと呼ばれるものである。オブジェクト指向プログラミングでは、GoF パターンと呼ばれるものが有名である。(Gang of Four)


パターン・コミュニティ

GoF パターン


オブジェクト作成のパターン

Abstract Factory

Builer

Factory Method

Prototype

Singleton


CardPlay のオブジェクト


CardPile

split/merge の取扱


Card


Board

Drag/Undo などの機能を持つ


Undo

Command Pattern を使った Undo の仕組みを理解しよう。

    sub log_move {
	my ($self) = shift;
	my ($undo) = @_;
	return if ($self->{'-undoing'}==1);
	return if ($undo==0);
	$self->{'-undo'}->[$self->{'-undo_index'}]=$undo;
	$#{$self->{'-undo'}} = $self->{'-undo_index'}++;
    #    print STDERR "log_move: "; $undo->print();
    }
    sub undo {
	my ($self) = shift;
	my ($undo);
	return if ($self->{'-undo_index'}==0);
	return if ($self->{'-undoing'}++>0);
	return if ($self->{'-undoing'}!=1);
	$self->disable_undo;
	while ( $self->{'-undo_index'} > 0 ) {
	    $undo = $self->{'-undo'}->[$self->{'-undo_index'}-1];
	    last if ($undo==-1);
	    last if ($undo!=0);
	    $self->{'-undo_index'} --;
	}
	while ( $self->{'-undo_index'} > 0 ) {
	    $undo = $self->{'-undo'}->[$self->{'-undo_index'}-1];
	    last if ($undo== -1);
	    $self->{'-undo_index'} --;
	    last if ($undo==0);
	    $undo->undo();
	}
	$self->{'-undoing'}=0;
	$self->enable_undo;
    }


委譲と継承

継承は、オブジェクト指向言語の機能として実現されていてるが、委譲は、それとは関係なく自分で実装することができる。Perl でも、

    package class1;
    sub method1 {
	my $self = shift;
	$self->{-delegate1}->method1(@_);
    }

みたいな形で委譲を実現することができる。これは、class1 のオブジェクトと、$self->{-deletage1} のオブジェクトの協調動作ということになる。

委譲を使うと、継承と異なり、委譲先を実行時に変更することができる。また、必要ならば、委譲先を、lazy に生成することもできる。また、メソッドの選択を継承のように言語の機能に頼ることなく、自分で行うことができるのも利点である。

ただし、メッセージの転送が必要になるので、実行のオーバヘッドは若干生じる。これは、適応性や再利用性をとるか、実行時の効率をとるかという問題になる。

オブジェクトは、このようなオブジェクトの組み合わせ(Object Composition) で作られていくようにすると、継承よりも、より柔軟な構造を作ることができる。


宿題

今日は宿題はなしとします。


Shinji KONO / Tue Jul 3 14:47:46 2001