• 検索結果がありません。

DesignPattern(No3) 2003/07/11 Composite 特徴容器と中身を同一視し, 再帰的な構造を作るパターン容器内に同類の容器を入れることができるパターン ( ファイル構造のファイルとフォルダに類似 ) クラス図 Client Uses Component method1(

N/A
N/A
Protected

Academic year: 2021

シェア "DesignPattern(No3) 2003/07/11 Composite 特徴容器と中身を同一視し, 再帰的な構造を作るパターン容器内に同類の容器を入れることができるパターン ( ファイル構造のファイルとフォルダに類似 ) クラス図 Client Uses Component method1("

Copied!
16
0
0

読み込み中.... (全文を見る)

全文

(1)

Composite

特徴

容器と中身を同一視し,再帰的な構造を作るパターン 容器内に同類の容器を入れることができるパターン(ファイル構造のファイルとフォルダに類似)

クラス図

Client Component method1( ) method2( ) add( ) remove( ) getChild( ) Leaf method1( ) method2( ) Composite children method1( ) method2( ) add( ) remove( ) getChild( ) Uses 各クラスの説明 Leaf 葉:「中身」を表すクラス.この中には他のものを入れられない.

Composite 複合体:「容器」を表すクラス.Leaf や他の Composite が入れられる

Component Leaf と Composite を同一視するためのクラス.Component は Composite と Leaf

のスーパークラスとして実現

Client 依頼者:Composite パターンの利用者

Composite 役が含んでいる Component 役(Leaf か Composite)を親に対する「子供」に見立てており、 getChild メソッドは Component 役から「子供」を得るメソッドに相当する.

aComposite

a Leaf a Leaf aComposite a Leaf

a Leaf a Leaf オブジェクトの構造 a Leaf

適用例

(「Java 言語で学ぶデザインパターン入門」より) ファイルのディレクトリを表現するプログラムを考える.ファイルを表すクラスが File クラス,ディレクトリを表 すクラスが Directory クラスである.この両者をまとめるスーパークラスとして,Entry クラスが存在する. Entory クラスはディレクトリエントリを表すクラスで,File,Directory を同一視するクラス. 各クラスの説明

Entry File と Directory クラスを同一視する抽象クラス(メソッドは実装しない)

File ファイルを表すクラス

Directory ディレクトリを表すクラス

FileTreatmentException ファイルFile に Entry を追加する際に起こる例外クラス

(2)

Entry getName( ) getSize( ); printList( String ) add( ) File name size getName( ) getSize( ) printList(String ) Directory name directory getName( ) getSize( ) printList( String ) add( )

Entry.java

( 抽象クラス抽象クラス抽象クラス抽象クラス )

public abstract class Entry {

public abstract String getName( ); // 名前を得る public abstract int getSize( ); // サイズを得る public Entry add(Entry entry) throws FileTreatmentException { // エントリを追加する throw new FileTreatmentException();

}

public void printList() { // 一覧を表示する printList("");

}

protected abstract void printList(String prefix); // prefix を前につけて一覧を表示する public String toString() { // 文字列表現

return getName() + " (" + getSize() + ")"; }

}

File.java

( ファイルを作成するクラスファイルを作成するクラスファイルを作成するクラス )ファイルを作成するクラス public class File extends Entry { private String name;

private int size;

public File(String name, int size) { this.name = name;

this.size = size; }

public String getName() { return name;

}

public int getSize() { return size; }

protected void printList(String prefix) { System.out.println(prefix + "/" + this); } }

Directory.java

( ディレクトリを作成するクラスディレクトリを作成するクラスディレクトリを作成するクラスディレクトリを作成するクラス ) import java.util.Iterator; import java.util.Vector;

public class Directory extends Entry {

private String name; // ディレクトリの名前

抽象メソッド Entry クラスの継承 コンストラクタ 引数で与えられた名前とサイ ズを属性(変数)に設定 抽象クラスで宣言されたメソッ ドを実装 サイズ(大きさ)を変数にもつ

(3)

this.name = name; }

public String getName() { // 名前を得る return name;

}

public int getSize() { // サイズを得る int size = 0;

Iterator it = directory.iterator(); while (it.hasNext()) {

Entry entry = (Entry)it.next(); size += entry.getSize();

}

return size; }

public Entry add(Entry entry) { // エントリの追加 directory.add(entry);

return this; }

protected void printList(String prefix) { // エントリの一覧 System.out.println(prefix + "/" + this);

Iterator it = directory.iterator(); while (it.hasNext()) {

Entry entry = (Entry)it.next(); entry.printList(prefix + "/" + name); }

} }

FileTreatmentException.java

(例外処理のためのクラス:例外処理のためのクラス:例外処理のためのクラス:Composite パターンには関係ない例外処理のためのクラス: パターンには関係ないパターンには関係ないパターンには関係ない) public class FileTreatmentException extends RuntimeException {

public FileTreatmentException() { } public FileTreatmentException(String msg) { super(msg); } }

Main.java

(実行するためのメソッド)(実行するためのメソッド)(実行するためのメソッド)(実行するためのメソッド) public class Main {

public static void main(String[] args) { try {

System.out.println("Making root entries..."); Directory rootdir = new Directory("root"); Directory bindir = new Directory("bin"); Directory tmpdir = new Directory("tmp"); Directory usrdir = new Directory("usr"); rootdir.add(bindir); rootdir.add(tmpdir); rootdir.add(usrdir); bindir.add(new File("vi", 10000)); bindir.add(new File("latex", 20000)); rootdir.printList(); System.out.println("");

System.out.println("Making user entries..."); Directory yuki = new Directory("yuki"); Directory hanako = new Directory("hanako"); Directory tomura = new Directory("tomura"); usrdir.add(yuki); 抽象クラスで宣言されたメソッ ドを実装 サイズの合計を計算 directory 内に連続して格納 された Entry(この場合はフ ァイル)を順次調べて足して いく (ファイルのサイズ合計をディ レクトリのサイズとして返す) Entry(この場合はファイルと ディレクトリ)をdirectory に追 加する ※追加するデータの型(引数) がEntry 型(ファイルとディレ クトリ両方適用可能)である 戻り値は“自分”であり,Entry 型を対応させている ディレクトリの作成 rootdir, bindir, tmpdir, usrdir の作成 (Directory コンストラクタを呼 ぶ) rootdir ディレクトリに bindir, tmpdir, usrdir を格納する bindir デ ィ レ ク ト リ に フ ァ イ ル vi(サイズ 10000)と latex(サイズ 20000)を格納する ※File コンストラクタを呼ぶ ディレクトリの作成

(4)

usrdir.add(hanako); usrdir.add(tomura); yuki.add(new File("diary.html", 100)); yuki.add(new File("Composite.java", 200)); hanako.add(new File("memo.tex", 300)); tomura.add(new File("game.doc", 400)); tomura.add(new File("junk.mail", 500)); rootdir.printList(); } catch (FileTreatmentException e) { e.printStackTrace(); } } }

作成されるデータ構造

実行結果

¥>java Main

Making root entries... /root (30000) /root/bin (30000) /root/bin/vi (10000) /root/bin/latex (20000) /root/tmp (0) /root/usr (0)

Making user entries... /root (31500) /root/bin (30000) /root/bin/vi (10000) /root/bin/latex (20000) /root/tmp (0) /root/usr (1500) /root/usr/yuki (300) /root/usr/yuki/diary.html (100) /root/usr/yuki/Composite.java (200) /root/usr/hanako (300) /root/usr/hanako/memo.tex (300) /root/usr/tomura (900) /root/usr/tomura/game.doc (400) /root/usr/tomura/junk.mail (500) usrdir ディレクトリにディレクトリ yuki, hanako, tomura を格納

yuki デ ィ レ ク ト リ に フ ァ イ ル diary.html, Composite.java を格納 hanako ディレクトリにファイル memo.tex を格納 tomura ディレクトリにファイル game.doc, junk.mail を格納 File のコンストラクタを呼ぶ rootdir ――― bindir ―――― vi ―― tmpdir ―― latex

―― usrdir ―――― yuki ―――― diary.html ―― Composite.java ―― hanako ―――― memo.tex ―― tomura ―――― game.doc ―― junk.mail フォルダだけなので 0 vi と latex の合計 30000 diary.html と Composite.java の合計 300 game.doc と junk.mail の合計 900

フォルダyuki と hanako , tomura の

合計 1500 (=300+300+900)

フォルダbin と tmp , usr の合計 31500

(=30000+0+1500)

usr フォルダの下に各人のフォルダとフ ァイルを格納後の表示

(5)

Bridge

Bridge

Bridge

Bridge

特徴

「機能のクラス階層(抽象機能)」と「実装のクラス階層(内部実装)」を“橋渡し”したパターン このパターンにする(2種類の階層構造を分類する)で機能 (メソッド)の拡張を簡単にさせる. 継承,(インターフェース,抽象クラスの)実装,委譲を利用する.

クラス図

Abstraction impl method1 method2 method3 Implementor implMethodX( ) implMethodY( ) ConcreteImplementor1 implMethodX ( ) implMethodY ( ) ConcreteImplementor2 implMethodX ( ) implMethodY ( ) RefinedAbstraction refinedMethodA ( ) refinedMethodB( ) 「機能のクラス階層」とは“クラスの継承”により,子クラスで親クラスの機能に新しい機能を拡張するクラス 間の(親子間の)階層構造,. 「実装のクラス階層」とは,インターフェースや抽象クラスで宣言されたメソッド(操作)を,継承(あるいは実 装する)子クラスで具体的に定義すること,そのときの親子のクラス間の階層構造を指す. (下のクラス図「メソッドの関係の概要図」参照) そして、「機能のクラス階層」の親クラス(上位クラス)と「実装のクラス階層」の上位クラス(インターフェース もしくは抽象クラスが該当)関連付ける.この関連付けが Bridge である(と見ることができる). これにより,機能拡張(機能の追加)と,宣言されたメソッド名を利用した異なる定義を分割して行なうこと ができ,プログラムの拡張性が容易となる. Bridge(の関係)によって,「機能のクラス階層」の上位クラスのメソッドを「実装のクラス階層」の上位クラス のメソッドに関連付ける. 各クラスの説明 Abstraction 「機能のクラス階層」の上位クラス 予め基本的な機能が定義,実装されているクラス RefinedAbstraction Abstraction クラスを継承して,新しい機能(メソッド)を拡張(追加)したクラス Implementor 「実装のクラス階層」の上位クラス メソッドの宣言のみ(抽象メソッドのみ)持つ (このため,抽象クラスかインターフェースとなる) ConcreteImplementor1 ConcreteImplementor2 Implementor クラスの(抽象)メソッドを実装するクラス

実装レベルでの関係

「機能のクラス階層」間の関係はクラスの“継承” 「実装のクラス階層」間の関係はクラスの“継承”もしくはインターフェースの“実装” Bridge 間はクラスの“委譲” 機能のクラス 機能のクラス 機能のクラス 機能のクラス 階層 階層階層 階層 実装のクラス 実装のクラス実装のクラス 実装のクラス 階層 階層 階層 階層 Bridge

(6)

メソッドの関係の概要図 Abstraction impl method1( ) method2( ) method3( ) Implementor implMethodX( ) implMethodY( ) ConcreteImplementor1 implMethodX ( ) implMethodY ( ) ConcreteImplementor2 implMethodX ( ) implMethodY ( ) RefinedAbstraction refinedMethodA ( ) refinedMethodB( ) method1( )により,implMethodX( ) の操作を行う method2( )により,implMethodY( ) の操作を行う 継承した子クラスで メソッドの実装を行う このクラスではメソッドmethod1,2,3 と refinedMethodA,B を持つ 移譲

適用例

(「Java 言語で学ぶデザインパターン入門」より) プログラム例 「機能のクラス階層」では Display クラス(メソッドの定義あり.インスタンスの作成が可能)とそのクラスを拡張し たCountDisplay クラス(メソッドの定義あり.インスタンスの作成が可能)を設定. CountDisplay クラス内の操作(メソッド)には“Display クラスのメソッドと新しいメソッド“を持つ 「実装のクラス階層」では抽象クラス DisplayImpl(メソッドの宣言のみ.インスタンスの作成が不可)と,その子 ク ラ ス StringDisplayImpl( メ ソ ッ ド の 定 義 あ り . イ ン ス タ ン ス の 作 成 が 可 能 ) が 設 定 さ れ て い る . (StringDisplayImpl クラスでは DisplayImpl で宣言したメソッド名で具体的に定義したものを持つ.) さらに,Display クラスのメソッドは DisplayImpl のメソッドを移譲する.

Display の各メソッドでは,print メソッドで指示された文字列を表示し,文字列の前に open メソッドでバー

ナーを,文字列の後にclose メソッドでバーナーを表示するとする. Display impl open( ) print( ) close( ) display( ) DisplayImpl rawOpen( ) rawPrint( ) rawClose( ) StringDisplayImpl rawOpen( ) rawPrint( ) rawClose( ) CountDisplay multiDisplay ( ) 継承した 継承した継承した 継承した 子クラスで 子クラスで子クラスで 子クラスで メソッドの メソッドのメソッドの メソッドの 実装を 実装を実装を 実装を 行っている 行っている行っている 行っている このクラスではメソッド このクラスではメソッド このクラスではメソッド

このクラスではメソッドopen, print, close,open, print, close,open, print, close,open, print, close, display

display display

display ととととmultiDisplaymultiDisplaymultiDisplaymultiDisplay を持つを持つを持つを持つ

“移譲”の関係 “移譲”の関係 “移譲”の関係 “移譲”の関係 Main Display d1 Display d2 CountryDisplay d3 このクラスを自由に設定することでDisplay クラス およびその子クラスの(インスタンス)メソッドの内容が 自由に変更できる そして,その文字列の表示方法や,文字列の前後に表示するバーナーの内容を StringDisplayImpl 内の メソッドによって定義する.プログラム例では文字列の表示では 文字列の(表示の)前後に‘|’を表示する.文 字列前後の表示では“+---+”を表示する.

さらに,CountDisplay クラスの multiDisplay では Display クラスのメソッドを拡張し,複数回文字列を表示 するメソッドを定義する. 実装のクラス 実装のクラス実装のクラス 実装のクラス 階層 階層 階層 階層 機能のクラス 機能のクラス機能のクラス 機能のクラス 階層 階層 階層 階層

(7)

<Display.java> public class Display {

private DisplayImpl impl;

public Display(DisplayImpl impl) this.impl = impl;

}

public void open( ) { impl.rawOpen( ); }

public void print( ) { impl.rawPrint( ); }

public void close( ) { impl.rawClose( ); }

public final void display( ) { open( ); print( ); close( ); } } <CountDisplay.java>

public class CountDisplay extends Display { public CountDisplay(DisplayImpl impl) { super(impl);

}

public void multiDisplay(int times) { // times回繰り返して表示する回繰り返して表示する回繰り返して表示する回繰り返して表示する open( );

for (int i = 0; i < times; i++) { print(); } close(); } } <DisplayImpl.java>

public abstract class DisplayImpl { public abstract void rawOpen(); public abstract void rawPrint(); public abstract void rawClose(); }

< StringDisplayImpl .java>

public class StringDisplayImpl extends DisplayImpl {

private String string; // 表示するべき文字列表示するべき文字列表示するべき文字列表示するべき文字列

private int width; // バイト単位で計算した文字列のバイト単位で計算した文字列の「幅」バイト単位で計算した文字列のバイト単位で計算した文字列の「幅」「幅」「幅」 public StringDisplayImpl(String string) { // コンストラクタで渡された文字列コンストラクタで渡された文字列コンストラクタで渡された文字列コンストラクタで渡された文字列 string を、を、を、を、 this.string = string; // フィールドに記憶しておくフィールドに記憶しておくフィールドに記憶しておくフィールドに記憶しておく

this.width = string.getBytes().length; // それからバイト単位の幅もフィールドにそれからバイト単位の幅もフィールドにそれからバイト単位の幅もフィールドにそれからバイト単位の幅もフィールドに

// 記憶しておいて、後で使う記憶しておいて、後で使う記憶しておいて、後で使う記憶しておいて、後で使う }

public void rawOpen() { printLine(); }

public void rawPrint() {

System.out.println("|" + string + "|"); // 前後に前後に"|"をつけて表示前後に前後に をつけて表示をつけて表示をつけて表示 ( ““““|文字列||文字列||文字列||文字列|”を表示”を表示”を表示”を表示 ) }

public void rawClose() { printLine(); }

private void printLine() {

抽象クラス メソッドはすべて抽象メソッド(メソッドの宣言)のみ DisplayImpl DisplayImpl DisplayImpl DisplayImpl クラスのインスタンスを持つクラスのインスタンスを持つクラスのインスタンスを持つクラスのインスタンスを持つ ((((コンストラクタの引数により設定コンストラクタの引数により設定コンストラクタの引数により設定コンストラクタの引数により設定)))) DisplayImpl DisplayImpl DisplayImpl DisplayImpl への移譲への移譲への移譲への移譲 各 メ ソ ッ ド の 処 理 が そ の ま ま DisplayImpl インスタンスのメソッド を読んでいる Display DisplayDisplay Display クラスからのクラスからのクラスからのクラスからの ((((メソッドのメソッドのメソッドの))))拡張部分メソッドの拡張部分拡張部分拡張部分

open( )[前バーナー],print( )[文字列],close( )[後バ ーナー]の順で表示を行なう

(8)

System.out.print("+"); // 枠の角を表現する枠の角を表現する枠の角を表現する枠の角を表現する"+"マークを表示するマークを表示するマークを表示するマークを表示する for (int i = 0; i < width; i++) { // width個の個の"-"を表示して、個の個の を表示して、を表示して、を表示して、

System.out.print("-"); // 枠線として用いる枠線として用いる枠線として用いる枠線として用いる } System.out.println("+"); // 枠の角を表現する枠の角を表現する"+"マークを表示する枠の角を表現する枠の角を表現する マークを表示するマークを表示するマークを表示する } } <Main.java> public class Main {

public static void main(String[ ] args) {

Display d1 = new Display(new StringDisplayImpl(" 1st sentence ")); Display d2 = new CountDisplay(new StringDisplayImpl(" 2nd sentence ")); CountDisplay d3 = new CountDisplay(new StringDisplayImpl(" 3rd sentence ")); d1.display(); d2.display(); d3.display(); d3.multiDisplay(3); } } 【結果】 【結果】 【結果】 【結果】 E:¥JAVA¥sample>java Main +---+ | 1st sentence | +---+ +---+ | 2nd sentence | +---+ +---+ | 3rd sentence | +---+ +---+ | 3rd sentence | | 3rd sentence | | 3rd sentence | +---+ 変数 変数 変数

変数d1,d2d1,d2 はd1,d2d1,d2はははDisplayDisplayDisplayDisplay クラスの型クラスの型クラスの型クラスの型 中身

中身 中身

中身ははは d1は d1 d1 は d1ははは Display Display Display Display インスタンス,インスタンス,d2インスタンス,インスタンス,d2d2d2 ははははCountDisplayCountDisplayCountDisplayCountDisplay インスタンスインスタンスインスタンスインスタンス また,これらは移譲の対象となる また,これらは移譲の対象となる また,これらは移譲の対象となる また,これらは移譲の対象となる StringDisplayImpl クラスのインスタンスをクラスのインスタンスをクラスのインスタンスをクラスのインスタンスを 引数として内部に持たせている 引数として内部に持たせている 引数として内部に持たせている 引数として内部に持たせている d1.display( ) d1.display( ) d1.display( )

d1.display( )による結果による結果による結果による結果 ( Display インスタンスの display メソッドを実行しており, 結局,StringDisplayImpl インスタンス内のメソッドを実行する)

d2.display( ) d2.display( ) d2.display( )

d2.display( )による結果による結果による結果による結果 ( CountDisplay インスタンスの display メソッドを実行しており, 結局,StringDisplayImpl インスタンス内のメソッドを実行する)

d3.display( ) d3.display( )d3.display( )

d3.display( )による結果による結果による結果による結果 ( 変数の型は CountDisplay であり,CountDisplay イン スタンスのdisplay メソッドを実行し,後は同上である. )

d3.multiDisplay( ) d3.multiDisplay( )d3.multiDisplay( )

d3.multiDisplay( )による結果による結果による結果による結果 (CountDisplay の拡張したメソッド multiDisplay を 実行している.Display より継承しているメソッドは StringDisplayImpl を移譲してい るので上記の表示を利用した結果となる.

(9)

Decorator

Decorator

Decorator

Decorator

特徴

ある処理を行う操作に別の操作を包むパターン. (クラスのインターフェースを再帰的に含ませることで処理を包めることを可能とする(クラス図参照)) ある処理内で異なる任意の処理を実行するパターンであり,(ユーザーに対して)処理の透過性を良くし,処 理の機能を柔軟に追加または削除することができる. 動的に構築できるオーバーレイ(階層構造)やビュー(表示ツール)に関するアプリケーション作成に適する. ある結果の出力に対して,異なるバーナーを階層的に表示していくためのパターン. (表示例: バーナー①,②,③をそれぞれ異なるクラス(クラス図の ConcreteDecorator に相当)を持つ) *************************** ← バーナー ③ #################### ← バーナー ② --- ← バーナー ① ある結果表示 --- ← バーナー ① #################### ← バーナー ② ************************** ← バーナー ③

クラス図

Component method1( ) method2( ) method3( ) ConcreteComponent method1( ) method2( ) method3( ) Decorator component ConcreteDecorator method1( ) method2( ) method3( ) Implementor 内の メソッドの実装 抽象クラス (抽象メソッド) 透過的なインターフェースを利用している.具体的には,Decrator(飾り)クラスと Component(中身)クラス は同一として扱う.(Decorator は Component を継承しているので,Decorator 内には Component 内のメソ ッド(宣言のみ)を持つ)Decorate(あるいは Wrap)する為の操作(メソッド)名(クラス図では method1,2,3)は同 一である.なお,“透過的”は,具体的には,Decorate(あるいは Wrap)を行っていても,他のクラスからこれ らのメソッド(Decorate(あるいは Wrap)する為のクラス)は利用できることを指す. 各クラスの説明 Component 機能を追加する中心となるクラス. 必要とする操作を宣言するクラス ConcreteComponent Component クラス内のメソッドを実装するクラス 具体的な操作(処理)を含むクラス

Decorator Component と同じインターフェース(API(Application Interface))を持ち,

Component クラスと(システム内で)同じ役割を持つ.

ConcreteDecorator 具体的な Decorartor の役割を行う.Deorator クラス(もしくはインターフェース)

(10)

プログラム例

Display getColumns( ) getRows( ) getRowText( ) show( ) StringDisplay String string getColumns( ) getRows( ) getRowText( ) Border Display display SideBorder borderChar getColumns( ) getRows( ) getRowText( ) Display クラス内の メソッドの実装 抽象クラス (抽象メソッド) FullBorder getColumns( ) getRows( ) getRowText( ) makeLine( ) 抽象クラスであり,Display クラスを継 承 し て い る の で getColumns, getRows, getRowText の抽象メソッド も引き継いでいる Main Display b1, b2, b3, b4 Display b1, b2, b3, b4 Display b1, b2, b3, b4 Display b1, b2, b3, b4 Display 型の変数に格納する インスタンスへの関連 飾り付けを行なうための 処理を含むクラス プログラム例に用いている各クラスの説明 Display: 文字列表示用の抽象クラス (共通のメソッド名の提供,飾りを含む文字列の表示は getRowText で行なう) StringDisplay: 1 行だけからなる文字列表示のクラス (飾り文字なし) Border: 「飾り枠」を表す抽象クラス (飾り文字を含む表示用のクラス同一視するとともに,飾り文字の表示に包含構造をもたせるためのクラス(イン スタンス変数にDisplay 型の変数を持つ)) SideBorder: 左右にのみ飾り枠を付けるクラス (文字列の前後に‘|’を入れて表示する) FullBorder: 上下左右に飾り枠を付けるクラス (文字列の前後に‘|’を,上下に“+---+”を入れて表示する) Main: 動作テスト用のクラス < Display > 抽象クラス public abstract class Display {

public abstract int getColumns(); // 横の文字数を得る横の文字数を得る横の文字数を得る横の文字数を得る public abstract int getRows(); // 縦の行数を得る縦の行数を得る縦の行数を得る縦の行数を得る

public abstract String getRowText(int row); // row 番目の文字列を得る番目の文字列を得る番目の文字列を得る番目の文字列を得る public final void show() { // 全部表示する全部表示する全部表示する全部表示する

for (int i = 0; i < getRows(); i++) { System.out.println(getRowText(i)); }

} }

< StringDisplay> 実装するクラス:実装するクラス: getColumns実装するクラス:実装するクラス: getColumnsgetColumns( ), getColumns( ), ( ), ( ), getRowsgetRows( ), getRowsgetRows( ), ( ), ( ), getRowTextgetRowTextgetRowTextgetRowText( )( )( )( )を実装するを実装するを実装するを実装する

public class StringDisplay extends Display {

private String string; // 表示文字列表示文字列表示文字列表示文字列

public StringDisplay(String string) { // 引数で表示文字列を指定引数で表示文字列を指定引数で表示文字列を指定引数で表示文字列を指定

抽象メソッド

(11)

return string.getBytes().length; }

public int getRows() { // 行数は行数は行数は行数は 1 return 1;

}

public String getRowText(int row) { // row がが 0 のときのみ返すが のときのみ返すのときのみ返すのときのみ返す if (row == 0) { return string; } else { return null; } } } < Border > 抽象クラス

public abstract class Border extends Display {

protected Display display; // この飾り枠がくるんでいる「中身」を指すこの飾り枠がくるんでいる「中身」を指すこの飾り枠がくるんでいる「中身」を指すこの飾り枠がくるんでいる「中身」を指す protected Border(Display display) { // インスタンス生成時に「中身」を引数で指定インスタンス生成時に「中身」を引数で指定インスタンス生成時に「中身」を引数で指定インスタンス生成時に「中身」を引数で指定 this.display = display;

} }

< SideBorder > 実装するクラス:実装するクラス:実装するクラス:実装するクラス: getColumnsgetColumnsgetColumnsgetColumns( ), ( ), ( ), ( ), getRowsgetRows( ), getRowsgetRows( ), ( ), ( ), getRowTextgetRowTextgetRowText( )getRowText( )( )( )を実装するを実装するを実装するを実装する

public class SideBorder extends Border {

private char borderChar; // 飾りとなる文字飾りとなる文字飾りとなる文字飾りとなる文字

public SideBorder(Display display, char ch) { // コンストラクタでコンストラクタでコンストラクタで Display と飾り文字を指定コンストラクタで と飾り文字を指定と飾り文字を指定と飾り文字を指定 super(display);

this.borderChar = ch; }

public int getColumns() { // 文字数は中身の両側に飾り文字分を加えたもの文字数は中身の両側に飾り文字分を加えたもの文字数は中身の両側に飾り文字分を加えたもの文字数は中身の両側に飾り文字分を加えたもの return 1 + display.getColumns() + 1;

}

public int getRows() { // 行数は中身の行数に同じ行数は中身の行数に同じ行数は中身の行数に同じ行数は中身の行数に同じ return display.getRows();

}

public String getRowText(int row) { / 指定行の内容は、中身の指定行の両側に飾り文字をつけたもの指定行の内容は、中身の指定行の両側に飾り文字をつけたもの指定行の内容は、中身の指定行の両側に飾り文字をつけたもの指定行の内容は、中身の指定行の両側に飾り文字をつけたもの return borderChar + display.getRowText(row) + borderChar;

} }

< FullBorder.java> 実装するクラス:実装するクラス:実装するクラス: getColumns実装するクラス: getColumnsgetColumns( ), getColumns( ), ( ), ( ), getRowsgetRows( ), getRowsgetRows( ), ( ), ( ), getRowTextgetRowTextgetRowTextgetRowText( )( )( )( )を実装するを実装するを実装するを実装する

public class FullBorder extends Border { public FullBorder(Display display) { super(display);

}

public int getColumns() { // 文字数は中身の両側に左右の飾り文字分を加えたもの文字数は中身の両側に左右の飾り文字分を加えたもの文字数は中身の両側に左右の飾り文字分を加えたもの文字数は中身の両側に左右の飾り文字分を加えたもの return 1 + display.getColumns() + 1;

}

public int getRows() { // 行数は中身の行数に上下の飾り文字分を加えたもの行数は中身の行数に上下の飾り文字分を加えたもの行数は中身の行数に上下の飾り文字分を加えたもの行数は中身の行数に上下の飾り文字分を加えたもの return 1 + display.getRows() + 1;

}

public String getRowText(int row) { // 指定した行の内容指定した行の内容指定した行の内容指定した行の内容

if (row == 0) { // 枠の上端枠の上端枠の上端枠の上端 return "+" + makeLine('-', display.getColumns()) + "+";

} else if (row == display.getRows() + 1) { // 枠の下端枠の下端枠の下端枠の下端 return "+" + makeLine('-', display.getColumns()) + "+";

} else { // それ以外それ以外それ以外それ以外 return "|" + display.getRowText(row - 1) + "|";

} }

private String makeLine(char ch, int count) { // 文字文字文字 ch を文字 ををを count 個連続させた文字列を作る個連続させた文字列を作る個連続させた文字列を作る個連続させた文字列を作る StringBuffer buf = new StringBuffer();

(12)

buf.append(ch); } return buf.toString(); } } <Main.java> public class Main {

public static void main(String[ ] args) {

Display b1 = new StringDisplay("1st sentence"); Display b2 = new SideBorder(b1, '#');

Display b3 = new FullBorder(b2); b1.show( ); b2.show( ); b3.show( ); Display b4 = new SideBorder( new FullBorder( new SideBorder( new FullBorder(

new StringDisplay("2nd sentence") ), '*' ) ), '/' ); b4.show( ); } } < << <出力結果出力結果出力結果出力結果>>>>

E:¥JAVA¥sample> java Main 1st sentence #1st sentence# +---+ |#1st sentence#| +---+ /+---+/ /|*+---+*|/ /|*|2nd sentence|*|/ /|*+---+*|/ /+---+/ Display クラスの型の変数

(13)

Flyweight

Flyweight

Flyweight

Flyweight

特徴

インスタンスをできるだけ共有することで,(インスタンスの数を減らして)メモリの無駄使いを避ける.(メモ リの使用量を小さくし,軽く(Flyweight)する) アプリケーションで利用されるインスタンスの数が多く,かつ同じインスタンスが数多く存在する場合に利 用される. ほとんどのインスタンスが類似するものである場合,異なる部分を同一の部分から切り離し,同一部分を 共有化するよう利用される.

クラス図

Flyweight methodA( ) methodB( ) FlyweightFactory pool getFrightweight( ) Client Creates ▲▲▲▲ Uses ▲▲▲▲ Uses Main Uses インスタンスの作成を管理するクラス Flyweight の同一条件のインスタンスを 一つだけ作るよう制御するクラス

Client は(FlyweightFactory を通して,)Flyweight のインスタンスを作成し,利用する.この際,複数 作成される Flyweight のインスタンス中で同一のものを共有化することでメモリの負荷を低減する.(インス タンスを共有化する仕組みを持つクラスがFlyweightFactory クラスである.) 各クラスの説明 Flyweight 共有した方が良いものを表すクラス (インスタンスが共有されるクラス) FlyweightFactory Flyweight の作成と管理を行うクラス. Flyweight のインスタンスを共有化するクラス

Client FlyweightFactory を利用して Flyweight のインスタンスを利用するクラス

Flyweight のインスタンスの参照を保持するクラス プログラム例 ある整数 num から連続する 100 件の整数値(すなわち num~num+100)を一塊のデータとして扱うことと する.この一塊のデータを格納するクラスをData とし,任意の整数値 num から自動的にデータを作成し,その インスタンス内に格納する.そして,格納されたデータをコンソール画面に表示するプログラムを考える. 実行時の引数から任意の整数を任意の数だけ設定することにより,各整数に対してData インスタンスを作成 し、その内部にデータを保有する.引数は任意の整数を設定できるため同一のデータを保有する Data インス タンスが作成される可能性がある。このため,Flyweight パターンを用いて,同一のデータを必要とする場合は インスタンスの共有化を行なう.

(14)

Data xdata print( ) DataFactory pool singleton getInstance( ) getData( ) DataStore Sdata print( ) Creates ▲▲▲▲ Uses ▲▲▲▲ Uses Main Uses インスタンスの作成を管理するクラス Data の同一条件のインスタンスを 一つだけ作るよう制御するクラス (DataFactory を複数作成しないため このクラスを singlton パターンとする) 共 通 化 し た Data イ ン ス タ ン ス は DataStore の中に格納される (利用される) 共有化を行なうデータ num~num+100 の 整数を配列に持つ DataFactory クラスがこのパターン で最も重要 各クラスの説明 Data データを格納するクラス 条件によって複数のインスタンスが作成されるクラスであり,このインスタンスが共有されるよ うに制御が行なわれる. このプログラム例では引数で与えられた初期値(整数)から連続する100件の整数値をデータ として格納する(例: 引数が 1 とすると 1~100 の整数を持つ)

DataFactory Data インスタンスを共有しながら生成するクラス(Data インスタンスを管理するクラス)

同一のData インスタンスを複数作成させないため,singlton パターンとする Data インスタンス(の参照)を Hashtable で持つ.(同一のインスタンスは複数作成しない) 同一 のインスタン スを複数 作成しない ように ,Data インスタンスを作成するメソッドに synchronized を設定 DataStore 共有化されたData インスタンスを格納(保持)するクラス(実際はインスタンスの参照を保持) Main 動作テスト用のクラス <Data.java> public class Data { public class Data { public class Data { public class Data {

private private private private int[int[int[int[ ] ] ] ] xdata;xdata;xdata;xdata;

private private private private int datanum=100;int datanum=100;int datanum=100;int datanum=100; // // // // コンストラクタコンストラクタコンストラクタコンストラクタ public Data( public Data( public Data(

public Data(int int int int num) {num) {num) {num) {

System.out.println("Data System.out.println("Data System.out.println("Data

System.out.println("Data コンストラクタが呼ばれました。引数はコンストラクタが呼ばれました。引数はコンストラクタが呼ばれました。引数はコンストラクタが呼ばれました。引数は "+ "+ "+ "+num+" num+" num+" num+" ですですですです");");");"); xdata=new

xdata=new xdata=new

xdata=new int[int[int[int[datanum];datanum];datanum];datanum]; for(

for( for(

for(int i=0; int i=0; int i=0; int i=0; i<i<i<datanum; i++){i<datanum; i++){datanum; i++){datanum; i++){ xdata[

xdata[ xdata[

xdata[i]=i]=i]=i]=num+i;num+i;num+i;num+i; }}}} } } } } // // // // データを表示するデータを表示するデータを表示するデータを表示する public void print( public void print( public void print( public void print( ) {) {) {) {

System.out.print("System.out.print("System.out.print("System.out.print("データを表示しますデータを表示しますデータを表示しますデータを表示します¥n");¥n");¥n");¥n"); for(

for( for(

for(int i=0; int i=0; int i=0; int i=0; i<i<i<datanum; i++){i<datanum; i++){datanum; i++){datanum; i++){

System.out.print("¥System.out.print("¥System.out.print("¥System.out.print("¥t"+xdata[t"+xdata[t"+xdata[t"+xdata[i]);i]);i]);i]); if(i%5 if(i%5 if(i%5 if(i%5 ==== =4)=4)=4)=4) System.out.print("¥n"); System.out.print("¥n"); System.out.print("¥n"); System.out.print("¥n"); コンストラクタでは引数に 100 件のデータの初期整数が渡され る.配列xdata 内に 100 件のデータが格納される データを画面上に(5つずつ) 表示するメソッド コンストラクタが呼ばれるたびに 引数の値と一緒に表示される xdata は 100 件のデータを格納するための配列 datanum は定数でデータ量 100 を表す

(15)

<DataFactory.java> import java.util.*;

public class DataFactory {

// すでに作ったすでに作ったすでに作ったすでに作った Data のインスタンスを管理のインスタンスを管理のインスタンスを管理のインスタンスを管理 private Hashtable pool = new Hashtable( ); // Singleton パターンパターンパターンパターン

private static DataFactory singleton = new DataFactory(); // コンストラクタコンストラクタコンストラクタコンストラクタ

private DataFactory( ) { }

// 唯一のインスタンスを得る唯一のインスタンスを得る唯一のインスタンスを得る唯一のインスタンスを得る

public static DataFactory getInstance() { return singleton;

}

// Data のインスタンス生成(共有)のインスタンス生成(共有)のインスタンス生成(共有)のインスタンス生成(共有)

public synchronized Data getData(int num) {

Data da = (Data) pool.get(Integer.toString(num)); // キーから該当するキーから該当するキーから該当する Data インスタンスをキーから該当する インスタンスをインスタンスをインスタンスを // HashTable より取り出すより取り出すより取り出すより取り出す if (da == null) { // 該当する場所にインスタンスが存在しない場合該当する場所にインスタンスが存在しない場合該当する場所にインスタンスが存在しない場合該当する場所にインスタンスが存在しない場合 da = new Data(num); // (1) Data のインスタンスを生成のインスタンスを生成のインスタンスを生成のインスタンスを生成

pool.put(Integer.toString(num), da); // (2) HashTable に格納に格納に格納に格納 }

return da; }

}

<DataStore.java> public class DataStore {

// データを格納するための配列データを格納するための配列データを格納するための配列データを格納するための配列 private Data[ ] Sdata;

// コンストラクタコンストラクタコンストラクタコンストラクタ

public DataStore(int[] inputdata) { Sdata = new Data[inputdata.length];

DataFactory factory = DataFactory.getInstance(); for (int i = 0; i < Sdata.length; i++) {

Sdata[i] = factory.getData(inputdata[i]); }

} // 表示表示表示表示

public void print() {

for (int i = 0; i < Sdata.length; i++) { Sdata[i].print();

} } }

<Main.java> public class Main {

public static void main(String[] args) { if (args.length == 0) {

System.out.println("Usage: java Main 整数の並び整数の並び整数の並び整数の並び");

System.out.println("Example: java Main 12 31 2 19 43 123"); System.exit(0);

}

int numData=args.length;

int[] inputData= new int[numData]; for(int i=0; i<numData; i++){

Hashtable を利用するためパッケージをインポート Hashtable インスタンスの作成 Singlton パターンに利用するた め(自分の)インスタンスをクラス 変数として設定する 自分のインスタンスを返す (初めに呼ばれた場合はフィールド内のイ ンスタンスの生成が先に行なわれる) Synchronized にすることで getData メソッドは常に1つ だけ実行させる. 戻り値はData インスタンスの参照 DataFactory のインスタンスを 得る.(この呼び出しでインスタン スも作成される) Data インスタンス(の参照)を格 納する配列 Sdata 配列への(参照の)格納 getData メソッドの戻り値が Data イン スタンスの参照 なお,i は実行時の引数で設定された整 数の順番 実行時の引数によって作成された Data イン スタンス(Sdata[i])の print メソッドを実行 numData は引数の整数の数は引数の整数の数は引数の整数の数は引数の整数の数 inputData は引数の整数を格納する配列は引数の整数を格納する配列は引数の整数を格納する配列は引数の整数を格納する配列

(16)

inputData[i]=Integer.parseInt(args[i]); }

//---データを格納するインスタンス(データを格納するインスタンス(データを格納するインスタンス(DataStore)の作成と格納データを格納するインスタンス( )の作成と格納)の作成と格納)の作成と格納 DataStore ds = new DataStore(inputData);

//---データの表示データの表示データの表示データの表示 ds.print( ); } } <実行結果実行結果実行結果実行結果> E:¥JAVA¥Sample>java Main 52 28 52 Data コンストラクタが呼ばれました。引数はコンストラクタが呼ばれました。引数はコンストラクタが呼ばれました。引数は 52 ですコンストラクタが呼ばれました。引数は ですですです Data コンストラクタが呼ばれました。引数はコンストラクタが呼ばれました。引数はコンストラクタが呼ばれました。引数は 28 ですコンストラクタが呼ばれました。引数は ですですです データを表示します データを表示します データを表示します データを表示します 52 53 54 55 56 <途中省略途中省略途中省略途中省略> 147 148 149 150 151 データを表示します データを表示します データを表示します データを表示します 28 29 30 31 32 <途中省略途中省略途中省略途中省略> 123 124 125 126 127 データを表示します データを表示します データを表示します データを表示します 52 53 54 55 56 <途中省略途中省略途中省略途中省略> 147 148 149 150 151

52

52

52

52

28

28

28

28

52 53 54 55 56 57 58 59 60 61 <途中省略途中省略途中省略>途中省略 147 148 149 150 151 28 29 30 31 32 <途中省略途中省略途中省略途中省略> 123 124 125 126 127

52

52

52

52

Data Sdata[ ] 配列配列配列配列 内部にはインスタンスの参照が 格納 Data Data Data Data インスタンス インスタンス インスタンス インスタンス int inputData[ ] 配列 配列配列 配列

情報の共有化に関する用語

intrinsic な情報 (イントリンジック) 場所や状況に依存しない情報 (共有化不可) (いかなる場所や状況においても変化しない情報,状態に依存しない情報) extrinsic な情報 (エクストリンジック) 場所や状況に依存する情報 (共有化可) (いかなる場所や状況において変化する情報,状態に依存する情報)

参照

関連したドキュメント

パターン1 外部環境の「支援的要因(O)」を生 かしたもの パターン2 内部環境の「強み(S)」を生かした もの

(a) ケースは、特定の物品を収納するために特に製作しも

である水産動植物の種類の特定によってなされる︒但し︑第五種共同漁業を内容とする共同漁業権については水産動

a.と同一の事故シナリオであるが,事象開始から約 38 時間後に D/W ベン トを実施する。ベント時に格納容器から放出され,格納容器圧力逃がし装置 に流入する

点検方法を策定するにあたり、原子力発電所耐震設計技術指針における機

実験に使用した装置を図 1 に示す。装置は照射容器,液相循環ライン,気相サンプリング ライン,ガス注入ライン等から成る。照射容器はステンレス製で,容量は

更に、このカテゴリーには、グラフィックタブレットと類似した機能を

これらの事例は、照会に係る事実関係を前提とした一般的