Javaデザインパターン入門【第2回】

1,167 views
1,006 views

Published on

Published in: Technology, Education
0 Comments
3 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
1,167
On SlideShare
0
From Embeds
0
Number of Embeds
59
Actions
Shares
0
Downloads
9
Comments
0
Likes
3
Embeds 0
No embeds

No notes for slide

Javaデザインパターン入門【第2回】

  1. 1. Javaデザインパターン入門 第2回
  2. 2. 今日の講義 1. 2. 3. 4. Factory Method Singleton Abstarct Factory State
  3. 3. インスタンス生成をサブクラスに任せる
  4. 4. Factory Methodパターン • 目的 ▫ オブジェクトを生成するときのインターフェース だけを規定して、実際にどのクラスをインスタン ス化するかはサブクラスが決めるようにする • 具体的な施策 ▫ インスタンス化はすべてサブクラス側で行う
  5. 5. Factory Methodサンプル • 身分証明書(IDカード)を作る工場 • ProductクラスとFactoryクラスは、framework というパッケージに属し、この2つのクラスがイ ンスタンス生成のための枠組み(フレームワー ク)の役割を果たす • IDCardクラスとIDCardFactoryクラスは、実際 の肉付けを行う • Mainクラスは動作テストのためのクラス
  6. 6. サンプル クラス一覧 パッケージ クラス名 解説 framework Product 抽象メソッドuseのみ定義されている抽象 クラス framework Factory メソッドcreateを実装している抽象クラス idcard IDCard メソッドuseを実装しているクラス idcard IDCardFactory メソッドcreateProduct,registerProductを 実装しているクラス 無名 Main 動作テスト用のクラス
  7. 7. サンプル クラス図
  8. 8. Product.java package framwork; public abstract class Product { public abstract void use(); }
  9. 9. Factory.java package framwork; public abstract class Factory { public final Product create(String owner) { Product product = createProduct(owner); registerProduct(product); return product; } protected abstract Product createProduct(String owner); protected abstract void registerProduct(Product product); }
  10. 10. IDCard.java package idcard; import framwork.Product; public class IDCard extends Product { private String owner; public IDCard(String owner) { System.out.println(owner + "のカードを作ります"); this.owner = owner; } @Override public void use() { System.out.println(owner + "のカードを使います"); } public String getOwner() { return this.owner; } }
  11. 11. IDCardFactory.java package idcard; import java.util.ArrayList; import java.util.List; import framwork.Factory; import framwork.Product; public class IDCardFactory extends Factory { private List owners = new ArrayList(); public List getOwners() { return owners; } @Override protected Product createProduct(String owner) { return new IDCard(owner); } @Override protected void registerProduct(Product product) { owners.add(((IDCard)product).getOwner()); } }
  12. 12. Main.java import framwork.Factory; import framwork.Product; import idcard.IDCardFactory; public class Main { public static void main(String[] args) { Factory factory = new IDCardFactory(); Product card1 = factory.create("田中"); Product card2 = factory.create("鈴木"); Product card3 = factory.create("高島"); card1.use(); card2.use(); card3.use(); } }
  13. 13. サンプルプログラム補足 • インスタンス生成-メソッドの実装方法 ▫ サンプルプログラムでは、Factoryクラスの createProductメソッドは抽象メソッドとなってい る。つまり、このメソッドはサブクラスで実装す ることを期待されている。 ▫ createProductメソッドの記述方法は3通りある 1. 抽象メソッドにする 2. デフォルトの実装を用意しておく 3. エラーにする
  14. 14. 関連しているパターン • Template Methodパターン ▫ Factory Methodパターンは、Template Methodパターンの 典型的な応用。サンプルプログラムのcreateメソッドが Template Methodになっている • Singletonパターン ▫ Creater役を務めるクラスは、多くの場合、Singletonパ ターンとして作ることが出来る。 • Compositeパターン ▫ Product役にCompositeパターンを当てはめることが出来る 場合がある • Iteratorパターン ▫ IteratorパターンでiteratorメソッドがIteratorのインスタン スを生成するときにFactory Methodパターンが使われるこ とがある
  15. 15. 演習1 • サンプルプログラムのIDCardクラスにカードの 通し番号をつけ、IDCardFactoryクラスが通し 番号と所持者の対応表を持つように修正しなさ い
  16. 16. たった1つのインスタンス
  17. 17. Singletonパターン • 目的 ▫ 指定したクラスのインスタンスが絶対に1個しか 存在しないことを保証したい ▫ インスタンスが1個しか存在しないことをプログ ラム上で表現したい
  18. 18. サンプル クラス一覧 クラス名 解説 Singleton インスタンスが1つしか存在しないクラス Main 動作テスト用のクラス
  19. 19. サンプル クラス図
  20. 20. Singleton.java public class Singleton { private static Singleton singleton = new Singleton(); private Singleton() { System.out.println("インスタンスを生成 しました"); } public static Singleton getInstance() { return singleton; } }
  21. 21. Main.java public class Main { です"); public static void main(String[] args) { System.out.println("Start."); Singleton obj1 = Singleton.getInstance(); Singleton obj2 = Singleton.getInstance(); if (obj1 == obj2) { System.out.println("obj1とobj2は同じインスタンス } else { System.out.println("obj1とobj2は同じインスタンス ではありません"); } System.out.println("End."); } }
  22. 22. 関連しているパターン • • • • AbstractFactoryパターン Builderパターン Facadeパターン Prototypeパターン
  23. 23. 関連する部品を組み合わせて製品を作る
  24. 24. Abstract Factoryパターン • 目的 ▫ オブジェクト指向における「抽象的」という言葉 は、「具体的にどのように実装されているかにつ いては考えず、インターフェース(API)だけに注 目している」状態を指す ▫ 部品の具体的な実装だけには注目せず、インター フェース(API)に注目し、そのインターフェース (API)だけを使って、部品を製品にまとめる
  25. 25. Abstract Factoryサンプル • 階層構造を持ったリンク集をHTMLファイルと して作る • factoryパッケージは、抽象的な構造・部品・製 品を含むパッケージ • 無名パッケージは、Mainクラスを含むパッケー ジ • listfactoryパッケージは具体的な構造・部品・製 品を含むパッケージ
  26. 26. サンプル クラス一覧 パッケージ クラス名 解説 factory Factory 抽象的な工場を表すクラス(Link,Tray,Pageを作 る) factory Item LinkとTrayを統一的に扱うためのクラス factory Link 抽象的な部品:HTMLのリンクを表すクラス factory Tray 抽象的な部品:LinkやTrayを集めたクラス factory Page 抽象的な部品:HTMLのページを表すクラス 無名 Main 動作テスト用のクラス
  27. 27. サンプル クラス一覧 パッケージ クラス名 解説 listfactory ListFactory 具体的な工場を表すクラス (ListLink,ListTray,Listpageを作る) listfactory ListLink 具体的な部品:HTMLのリンクを表すクラス listfactory ListTray 具体的な部品:LinkやTrayを集めたクラス listfactory ListPage 具体的な製品:HTMLのページを表すクラス
  28. 28. サンプル クラス図
  29. 29. Item.java package factory; public abstract class Item { protected String caption; public Item(String caption) { this.caption = caption; } public abstract String makeHTML(); }
  30. 30. Link.java package factory; public abstract class Link extends Item { protected String url; public Link(String caption, String url) { super(caption); this.url = url; } }
  31. 31. Tray.java package factory; import java.util.ArrayList; public abstract class Tray extends Item { protected ArrayList<Item> tray = new ArrayList<Item>(); public Tray(String caption) { super(caption); } public void add(Item item) { tray.add(item); } }
  32. 32. Page.java package factory; import java.io.FileWriter; import java.io.IOException; import java.io.Writer; import java.util.ArrayList; public abstract class Page { protected String title; protected String author; protected ArrayList<Item> content = new ArrayList<Item>(); public Page(String title, String author) { this.title = title; this.author = author; } public void add(Item item) { content.add(item); }
  33. 33. Page.java public void output() { try { String filename = title + ".html"; Writer writer = new FileWriter(filename); writer.write(this.makeHTML()); writer.close(); System.out.println(filename + "を作成し ました"); } catch (IOException e) { e.printStackTrace(); } } public abstract String makeHTML(); }
  34. 34. Factory.java package factory; public abstract class Factory { public static Factory getFactory(String classname) { Factory factory = null; try { factory = (Factory) Class.forName(classname).newInstance(); } catch(ClassNotFoundException e) { System.out.println("クラス " + classname + " が見つかりません"); } catch (Exception e) { e.printStackTrace(); } return factory; } public abstract Link createLink(String caption, String url); public abstract Tray createTray(String caption); public abstract Page createPage(String title, String author); }
  35. 35. Main.java package abstractfactory; import factory.Factory; import factory.Link; import factory.Page; import factory.Tray; public class Main { public static void main(String[] args) { // Linkfactory Factory factory = Factory.getFactory("listfactory.ListFactory"); Link asahi = factory.createLink("朝日新聞", "http://www.asahi.com/"); Link yomiuri = factory.createLink("読売新聞", "http://www.yomiuri.co.jp/"); Link us_yahoo = factory.createLink("Yahoo!", "http://www.yahoo.com/"); Link jp_yahoo = factory.createLink("Yahoo!Japan", "http://yahoo.co.jp/"); Link excite = factory.createLink("Excite", "http://www.excite.com/"); Link google = factory.createLink("Google", "http://www.google.com/");
  36. 36. Main.java Tray traynews = factory.createTray("新聞"); traynews.add(asahi); traynews.add(yomiuri); Tray trayyahoo = factory.createTray("Yahoo!"); trayyahoo.add(us_yahoo); trayyahoo.add(jp_yahoo); Tray traysearch = factory.createTray("サーチエンジン"); traysearch.add(trayyahoo); traysearch.add(excite); traysearch.add(google); Page page = factory.createPage("LinkPage", "yukiko kato"); page.add(traynews); page.add(traysearch); page.output(); } }
  37. 37. ListFactory.java package listfactory; import factory.Factory; import factory.Link; import factory.Page; import factory.Tray; public class ListFactory extends Factory { @Override public Link createLink(String caption, String url) { return new ListLink(caption, url); } @Override public Tray createTray(String caption) { return new ListTray(caption); } @Override public Page createPage(String title, String author) { return new ListPage(title, author); } }
  38. 38. ListLink.java package listfactory; import factory.Link; public class ListLink extends Link { public ListLink(String caption, String url) { super(caption, url); } @Override public String makeHTML() { return " <li><a href="" + url + "">" + caption + "</a></li>n"; } }
  39. 39. ListTray.java package listfactory; import java.util.Iterator; import factory.Item; import factory.Tray; public class ListTray extends Tray { public ListTray (String caption) { super(caption); } @Override public String makeHTML() { StringBuffer sb = new StringBuffer(); sb.append("<li>n"); sb.append(caption + "n"); sb.append("<ul>n"); Iterator<Item> it = tray.iterator(); while(it.hasNext()) { Item item = (Item) it.next(); sb.append(item.makeHTML()); } sb.append("</ul>n"); sb.append("</li>n"); return sb.toString(); } }
  40. 40. ListPage.java package listfactory; import java.util.Iterator; import factory.Item; import factory.Page; public class ListPage extends Page{ public ListPage(String title, String author) { super(title, author); } public String makeHTML() { StringBuffer sb = new StringBuffer(); sb.append("<html><head><title>" + title + "</title></head>n"); sb.append("<body>n"); sb.append("<h1>" + title + "</h1>n"); sb.append("<ul>n"); Iterator<Item> iterator = content.iterator(); while (iterator.hasNext()) { Item item = iterator.next(); sb.append(item.makeHTML()); } sb.append("</ul>n"); sb.append("<hr><address>" + author + "</address>"); sb.append("</body></html>n"); return sb.toString(); } }
  41. 41. 関連しているパターン • Builderパターン ▫ AbstractFactoryパターンは、インターフェース(API)が定まって いる抽象的な部品を組み合わせて複雑な構造を持ったインスタン スを作る。 ▫ Builderパターンは、段階を追って大きなインスタンスを作る • FactoryMethodパターン ▫ AbstractFactoryパターンで製品や部品を作るところは、 FactoryMethodパターンになることがある • Compositeパターン ▫ AbstractFactoryパターンで作られる製品は、Compositeパターン になることがある • Singletonパターン ▫ AbstractFactoryパターンの具体的な工場は、Singletonになるこ とがある
  42. 42. 演習2 • サンプルプログラムに具体的な工場 TableFactoryを追加し、tableタグを使用した表 組みのデザインになるようにしなさい。 • なお、tableタグの構成は以下である。 <table> <tr> <td>カラム</td> </tr> </table>
  43. 43. 演習2 表示例
  44. 44. 状態をクラスとして表現する
  45. 45. Stateパターン • 目的 ▫ 「状態」というものをクラスとして表現する ▫ 状態をクラスとして表現していれば、クラスを切 り替えることによって「状態の変化」が表わせる ことができ、新しい状態を追加しなければならな い時に、何をプログラムすればよいかはっきりす る
  46. 46. サンプルプログラム • 金庫警備システム ▫ 時刻ごとに警備の状態が変化する金庫警備システ ム ▫ 警備センターへの呼び出しは、画面に呼び出し状 況を表示することで対応 ▫ 時間経過は、プログラム上の1秒を1時間と仮定す る
  47. 47. 金庫警備システム要件 • • • • • • • • • • • • • 金庫が1つある 金庫は警備センサーと接続されている 金庫には非常ベルと通常通話用の電話が接続されている 金庫には時計がついていて、現在の時刻を監視している 昼間は9:00〜16:59、夜間は17:00〜23:59及び0:00〜8:59の範囲 金庫は昼間だけ使用できる 昼間、金庫を使用すると、警備センターに使用記録が残る 夜間、金庫を使用すると、警備センターに非常事態の通報が行く 非常ベルはいつでも使用できる 非常ベルを使用すると、警備センターに非常ベルの通報が行く 通常通話の電話はいつでも使用できるが、夜間は録音のみとする 昼間、電話を使用すると、警備センサーが呼び出される 夜間、電話を使用すると、警備センサーの留守録が呼び出される
  48. 48. Stateパターンを使用しない金庫警備シ ステムの擬似コード 警備システムのクラス { 金庫使用時に呼ばれるメソッド() { if (昼間) { 警備センターに利用の記録 } else if (夜間) { 警備センターに非常事態の通報 } } 非常ベル使用時に呼ばれるメソッド() { 警備センターに非常ベルの通報 } 通常通話時に呼ばれるメソッド() { if (昼間) { 警備センターの呼び出し } else if (夜間) { 警備センターの留守録の呼び出し } } }
  49. 49. Stateパターンを使用した金庫警備シス テムの擬似コード 昼間という状態を表現するクラス { 金庫使用時に呼ばれるメソッド() { 警備センターに利用の記録 } 非常ベル使用時に呼ばれるメソッド() { 警備センターに非常ベルの通報 } 通常通話時に呼ばれるメソッド() { 警備センターの呼び出し } } 夜間という状態を表現するクラス { 金庫使用時に呼ばれるメソッド() { 警備センターに非常事態の通報 } 非常ベル使用時に呼ばれるメソッド() { 警備センターに非常ベルの通報 } 通常通話時に呼ばれるメソッド() { 警備センターの留守録の呼び出し } }
  50. 50. パターンを適用した時の差異 • Stateパターンを使用しないコードではif文を使 用するが、Stateパターンを使うことにより、昼 間・夜間という状態がクラスとして表現される ため、状態チェックのためのif文は使用しない
  51. 51. サンプル クラス一覧 クラス名 解説 State 金庫の状態を表すインターフェース DayState Stateを実装しているクラス。昼間の状態を表す NightState Stateを実装しているクラス。夜間の状態を表す Context 金庫の状態変化を管理し、警備センターと連絡を取るインター フェース SafeFrame Contextを実装しているクラス。ボタンや画面表示などのユー ザインターフェースを持つ Main 動作テスト用のクラス
  52. 52. サンプル クラス図
  53. 53. State.java(インターフェース) public interface State { public abstract void doClock(Context context, int hour); public abstract void doUse(Context context); public abstract void doAlarm(Context context); public abstract void doPhone(Context context); }
  54. 54. DayState.java public class DayState implements State { private static DayState singleton = new DayState(); private DayState() { } public static State getInstance() { return singleton; } @Override public void doClock(Context context, int hour) { if (hour < 9 || 17 <= hour) { context.changeState(NightState.getInstance()); } }
  55. 55. DayState.java @Override public void doUse(Context context) { context.recordLog("金庫使用(昼間)"); } @Override public void doAlarm(Context context) { context.callSecurityCenter("非常ベル(昼間)"); } @Override public void doPhone(Context context) { context.callSecurityCenter("通常の通話(昼間)"); } @Override public String toString() { return "[昼間]"; } }
  56. 56. NightState.java public class NightState implements State { private static NightState singleton = new NightState(); private NightState() { } public static State getInstance() { return singleton; } @Override public void doClock(Context context, int hour) { if (9 <= hour && hour < 17) { context.changeState(DayState.getInstance()); } }
  57. 57. NightState.java @Override public void doUse(Context context) { context.callSecurityCenter("非常:夜間の金庫使用!"); } @Override public void doAlarm(Context context) { context.callSecurityCenter("非常ベル(夜間)"); } @Override public void doPhone(Context context) { context.recordLog("夜間の通話録音"); } @Override public String toString() { return "[夜間]"; } }
  58. 58. Context.java(インターフェース) public interface Context { public abstract void setClock(int hour); public abstract void changeState(State state); public abstract void callSecurityCenter(String msg); public abstract void recordLog(String msg); }
  59. 59. SafeFrame.java import java.awt.BorderLayout; import java.awt.Button; import java.awt.Color; import java.awt.Frame; import java.awt.Panel; import java.awt.TextArea; import java.awt.TextField; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; public class SafeFrame extends Frame implements Context , ActionListener{ // 現在時刻表示 private TextField textClock = new TextField(60); // 警備センター出力 private TextArea textScreen = new TextArea(10, 60); // 金庫使用ボタン private Button buttonUse = new Button("金庫使用"); // 非常ベルボタン private Button buttonAlarm = new Button("非常ベルボタン"); // 通常通話ボタン private Button buttonPhone = new Button("通常通話"); // 終了ボタン private Button buttonExit = new Button("終了"); private State state = DayState.getInstance();
  60. 60. SafeFrame.java public SafeFrame(String title) { super(title); setBackground(Color.lightGray); setLayout(new BorderLayout()); // textClockを配置 add(textClock, BorderLayout.NORTH); textClock.setEditable(false); // textScreenを配置 add(textScreen, BorderLayout.CENTER); textScreen.setEditable(false); // パネルにボタンを格納 Panel panel = new Panel(); panel.add(buttonUse); panel.add(buttonAlarm); panel.add(buttonPhone); panel.add(buttonExit); // そのパネルを配置 add(panel, BorderLayout.SOUTH); // 表示 pack(); setVisible(true); // リスナーの設定 buttonUse.addActionListener(this); buttonAlarm.addActionListener(this); buttonPhone.addActionListener(this); buttonExit.addActionListener(this); }
  61. 61. SafeFrame.java // ボタンが押されたらここに来る public void actionPerformed(ActionEvent e) { System.out.println(e.toString()); if (e.getSource() == buttonUse) { // 金庫使用ボタン state.doUse(this); } else if (e.getSource() == buttonAlarm) { // 非常ベルボタン state.doAlarm(this); } else if (e.getSource() == buttonPhone) { // 通常通話ボタン state.doPhone(this); } else if (e.getSource() == buttonExit) { // 終了ボタン System.exit(0); } else { System.out.println("?"); } }
  62. 62. SafeFrame.java // 時刻の設定 public void setClock(int hour) { String clockstring = "現在時刻は"; if (hour < 10) { clockstring += "0" + hour + ":00"; } else { clockstring += hour + ":00"; } System.out.println(clockstring); textClock.setText(clockstring); state.doClock(this, hour); }
  63. 63. SafeFrame.java // 状態変化 public void changeState(State state) { System.out.println(this.state + "から" + state + "へ状態が変化し ました。"); this.state = state; } // 警備センター警備員呼び出し public void callSecurityCenter(String msg) { textScreen.append("call! " + msg + "n"); } // 警備センター記録 public void recordLog(String msg) { textScreen.append("record ... " + msg + "n"); } }
  64. 64. Main.java public class Main { public static void main(String[] args) { SafeFrame frame = new SafeFrame("State Sample"); while (true) { for (int hour = 0; hour < 24; hour++) { frame.setClock(hour); // 時刻の設定 try { Thread.sleep(1000); } catch (InterruptedException e) { } } } } }
  65. 65. Stateパターンの登場人物 • State(状態)の役 ▫ 状態を表す ▫ 状態ごとに異なる振る舞いをするインターフェース (API)を定める ▫ このインターフェース(API)は、状態に依存した振る 舞いをするメソッドの集まりになる • ConcreteState(具体的な状態)の役 ▫ 具体的な個々の状態を表現する ▫ State役で定められたインターフェースを実装する • Context(状況、前後関係、文脈)の役 ▫ 現在の状態を表すConcreteState役を持つ ▫ Stateパターンの利用者に必要なインターフェース (API)を定める
  66. 66. 関連しているパターン • Singletonパターン ▫ ConcreteState役はSingletonパターンとして実装 される場合がある  状態を表すクラスは、インスタンスフィールド(つ まりインスタンスの状態)を持っていないため • Flyweightパターン ▫ 状態を表すクラスはインスタンスフィールドを持 たないため、Flyweightパターンを使って、 ConcreteState役を複数のContext役で共有出来る 場合がある
  67. 67. 演習3 • サンプルプログラムに、「昼食時」(12:00~ 12:59)という以下のような新しい状態を追加し なさい ▫ 昼食時、金庫を使用すると、警備センターに非常 事態の通報が行く ▫ 昼食時、非常ベルを使用すると、警備センターに 非常ベルの通報が行く ▫ 昼食時、電話を使用すると、警備センターの留守 録が呼び出される
  68. 68. 参考 • [増補改訂版]Java言語で学ぶデザインパターン 入門 ▫ 結城浩著

×