- PR -

デザパタ練習問題で。。。

投稿者投稿内容
ほむら
ぬし
会議室デビュー日: 2003/02/28
投稿数: 583
お住まい・勤務地: 東京都
投稿日時: 2003-09-01 12:20
ども、ほむらです。
yamasa氏の出していた練習問題やってみました。
こんな感じでよいのでしょうか。。。
ちなみに、(b)だけですが。。。(a)も包含しているはずです。
--------------
引用:

本屋に行けばデザインパターン本はたくさん見つかるので
ここでの解説は割愛します。
# 初学者なら結城さんの本( http://www.hyuki.com/dp/ )がお勧めです。

かわりにちょっと練習問題を。
コード:
public class Sample {
  public void doSomething() {
    /* 処理A */
    /* 処理B */
    /* 処理C */
    /* 処理D */
  }
}



上記のコードに、以下のような修正をしてみてください。

(a) 処理Bの内容を処理B'に切り替えて使うことができるようにしたい。
これを Template Method または Strategy パターンを使って実現せよ。

(b) (a)に加え、処理Cを処理C'や処理C''に切り替えて使えるようにしたい。
ただし、処理Bと処理Cはそれぞれ独立して切り替えられなければならない。
これを Template Method または Strategy パターンを使って実現せよ。


長いのでコードは別に書きます。
ほむら
ぬし
会議室デビュー日: 2003/02/28
投稿数: 583
お住まい・勤務地: 東京都
投稿日時: 2003-09-01 12:23
ほむらです。
作ってみたコードです。(コメントないけどいいですよね(^^;;;;)
デザインパターンは
Template Method デザインパターンに
Strategy デザインパターンを組み合わせたつもりです。
問題点あれば遠慮なく添削お願いします。
--------------
コード:

import java.lang.*;

interface ISamethingC
{
int PRINT_PATTERN_NUM_01 = 1;
int PRINT_PATTERN_NUM_02 = 2;
int PRINT_PATTERN_NUM_03 = 3;
abstract void trace();
}

abstract class SamethingC01 implements ISamethingC
{
public void trace(){ print();}
abstract void print();
}
class SamethingC01_01 extends SamethingC01
{
public void print(){ System.out.println("処理 C"); }
}
class SamethingC01_02 extends SamethingC01
{
public void print(){ System.out.println("処理 C’"); }
}

class SamethingC01_03 extends SamethingC01
{
public void print(){ System.out.println("処理 C lie"); }
}

interface ISample{ abstract void doSamething(); }
abstract class Sample implements ISample
{
protected ISamethingC _iC;
protected abstract void doSamethingB();
public void setSamethingC(int patC)
{
switch( patC ){
case ISamethingC.PRINT_PATTERN_NUM_01:
_iC = new SamethingC01_01();
break;
case ISamethingC.PRINT_PATTERN_NUM_02:
_iC = new SamethingC01_02();
break;
case ISamethingC.PRINT_PATTERN_NUM_03:
default:
_iC = new SamethingC01_03();
break;
}
}
public void doSamething()
{
/* 処理A */
/* 処理B */
doSamethingB();
/* 処理C */
_iC.trace();
/* 処理D */
}
}

class SampleA extends Sample
{
public SampleA( int patC ){
setSamethingC( patC );
}
protected void doSamethingB()
{
System.out.println("処理B");
}
}

class SampleB extends Sample
{
public SampleB( int patC ){
setSamethingC( patC );
}
protected void doSamethingB()
{
System.out.println("処理B' ");
}
}


public class p
{
public static void main(String[] argv)
{
ISample s;
try {
if( argv[0].equals("01") == true ){
if( argv[1].equals("01") == true ){
// 処理B 処理C
s = new SampleA( ISamethingC.PRINT_PATTERN_NUM_01 );
}
else {
// 処理B 処理C'
s = new SampleA( ISamethingC.PRINT_PATTERN_NUM_02 );
}
}
else {
// 処理B' 処理C'
s = new SampleB( ISamethingC.PRINT_PATTERN_NUM_02 );
}
}
catch( ArrayIndexOutOfBoundsException ae ){
/* 引数が足りない */
System.out.println("引数が足りません");
// 処理B' 処理C lie
s = new SampleB( ISamethingC.PRINT_PATTERN_NUM_03 );
}
s.doSamething();
}
}



# コメントに嘘を発見したので修正

[ メッセージ編集済み 編集者: ほむら 編集日時 2003-09-01 15:27 ]
かずくん
ぬし
会議室デビュー日: 2003/01/08
投稿数: 759
お住まい・勤務地: 太陽系第三惑星
投稿日時: 2003-09-01 17:04
せっかくだから、swatch - caseはなくしたいですね。
処理を独立に切り替えるという観点から、以下のようなものはいかがでしょう。
コード:
interface ProcessA {public abstract void processA();}
interface ProcessB {public abstract void processB();}
interface ProcessC {public abstract void processC();}
interface ProcessD {public abstract void processD();}

class CocreteProcessA1 implements ProcessA {
	public void processA() {
		System.out.println("処理 A");
	}	
}

class CocreteProcessA2 implements ProcessA {
	public void processA() {
		System.out.println("処理 A’");
	}	
}

class CocreteProcessB1 implements ProcessB {
	public void processA() {
		System.out.println("処理 B");
	}	
}

class CocreteProcessB2 implements ProcessB {
	public void processB() {
		System.out.println("処理 B’");
	}	
}

class CocreteProcessC1 implements ProcessC {
	public void processC() {
		System.out.println("処理 C");
	}	
}

class CocreteProcessC2 implements ProcessC {
	public void processC() {
		System.out.println("処理 C’");
	}	
}

class CocreteProcessD1 implements ProcessD {
	public void processD() {
		System.out.println("処理 D");
	}	
}

class CocreteProcessD2 implements ProcessD {
	public void processD() {
		System.out.println("処理 D’");
	}	
}


class Sample {
	ProcessA mA = new ConcreteProcessA1();
	ProcessB mB = new ConcreteProcessB1();
	ProcessC mC = new ConcreteProcessC1();
	ProcessD mD = new ConcreteProcessD1();
	
	public void setProcessA(ProcessA inProcess) {
		mA = inProcess;
	}
	
	public void setProcessB(ProcessB inProcess) {
		mB = inProcess;
	}
	
	public void setProcessA(ProcessC inProcess) {
		mC = inProcess;
	}
	
	public void setProcesD(ProcessD inProcess) {
		mD = inProcess;
	}
	
	public void doSomething() {
		mA.processA();
		mB.processB();
		mC.processC();
		mD.processD();
	}
}

public class Main {
	public static void main(String[] inArgs) {
		Sample aSample = new Sample();
		
		// 処理 A, B, C, Dが実行
		aSample.doSomething();
		
		aSample.setProcessA(new ConcreteProcessA2());
		aSample.setProcessB(new ConcreteProcessB2());
		aSample.setProcessC(new ConcreteProcessC2());
		aSample.setProcessD(new ConcreteProcessD2());

		// 処理 A', B', C', D'が実行
		aSample.doSomething();
	}
}



処理A, B, C, Dは互いに共通のインターフェースを持たないという前提で、作成しました。

問題領域にもよりますが、今回のような、独立に処理を切り替える問題に対してTemplate Methodを適用すると、classの爆発的増大を招いてしまうので、使用しない方が良いでしょう。

# Sampleのインターフェースを変更してはいけなかった?
# 実行して確認していないので、動くかどうか分かりませ〜ん。
ほむら
ぬし
会議室デビュー日: 2003/02/28
投稿数: 583
お住まい・勤務地: 東京都
投稿日時: 2003-09-02 11:35
ども、ほむらです。
ご指摘ありがとうございます。
---------------
かずくん氏へ
引用:

せっかくだから、swatch - caseはなくしたいですね。

--- 中略です。 ご容赦>< ---

問題領域にもよりますが、今回のような、独立に処理を切り替える問題に対してTemplate Methodを適用すると、classの爆発的増大を招いてしまうので、使用しない方が良いでしょう。


switch-caseについては。必要ありませんでしたね^^;;;;
ここもstrategyでかけばよかったと今になって思います。
(なんで、使ったんだろ(笑 )

template methodを適用してしまったのは、
A〜Dの処理は一連の操作だからと思ったからです。
一連の操作=template methodかな?と。。。
でも、使わないほうがさっぱりしているし汎用性もでてくるように思います。
もしかして、template methodは使う場面少ないのでしょうか。。。。
strategyでほとんど事足りる場合がほとんど?

># Sampleのインターフェースを変更してはいけなかった?
># 実行して確認していないので、動くかどうか分かりませ〜ん。
処理Bを分散させるためとその中で処理Cを切り替えるつもりだったので
Sampleインターフェイスを作ってしまっただけです。
本来は必要ないものなので変更や削除自体問題ありません。

かずくん
ぬし
会議室デビュー日: 2003/01/08
投稿数: 759
お住まい・勤務地: 太陽系第三惑星
投稿日時: 2003-09-02 12:54
引用:

template methodを適用してしまったのは、
A〜Dの処理は一連の操作だからと思ったからです。
一連の操作=template methodかな?と。。。
でも、使わないほうがさっぱりしているし汎用性もでてくるように思います。
もしかして、template methodは使う場面少ないのでしょうか。。。。
strategyでほとんど事足りる場合がほとんど?



Template Methodを使用するか、Strategyを使用するかを決定する方針の1つは、
静的に強く結び付けたいか、動的に実装を切り替えたいかということが挙げられます。

今回の問題では、独立に処理を切り替えたいという仕様が含まれていたため、Strategyがより適切であると考えました。

このままでは、「Template Methodって使えないんじゃないの?」と思われてしまうので、
いくつか例を考えてみました。

ひとつは、Decoratorの骨格実装抽象クラス間の連結部分への適用
コード:
class abstract AbstractDecorator implements Decorator {
    private Decorator mDecorated;

    public void foo() {
        fooSub();
        mDecorated.foo();
    }

    protected abstract void fooSub(); // Template Method
}


Decoratorの具象クラスはTemplate MethodであるfooSub()だけを意識するだけで済みます。
(foo()がfinal methodではないのは、Template Methodだけでは物事を解決できず、直接foo()を実装しなければならない場面に遭遇してしまったため。foo()は出来る限りfinal methodである方が良いと思われる)

他の例として、Prototype + Template Methodというのも考えられます。
コード:
public SomeObject create(String inID) {
    SomeObject aObj = (SomeObject)sObjectMap.get(inID); // ex) java.util.Map<String, SomeObject>
    aObj = aObj.copy(); // Prototype
    aObj.initialize(); // TemplateMethod

    return aObj;
}



以上、あくまで一例として...。
探せば結構Template Methodを適用できる場所はあるものです。
ほむら
ぬし
会議室デビュー日: 2003/02/28
投稿数: 583
お住まい・勤務地: 東京都
投稿日時: 2003-09-02 14:10
ども、ほむらです。
-----------------
かずくん氏へ
引用:

Template Methodを使用するか、Strategyを使用するかを決定する方針の1つは、
静的に強く結び付けたいか、動的に実装を切り替えたいかということが挙げられます。

今回の問題では、独立に処理を切り替えたいという仕様が含まれていたため、Strategyがより適切であると考えました。


ふむふむ。つまり今回の場合は。
引用:

(a) 処理Bの内容を処理B'に切り替えて使うことができるようにしたい。
これを Template Method または Strategy パターンを使って実現せよ。

(b) (a)に加え、処理Cを処理C'や処理C''に切り替えて使えるようにしたい。
ただし、処理Bと処理Cはそれぞれ独立して切り替えられなければならない。


という条件の中で独立して切り替えられなければならない。
という部分においてStrategyの適用がより効率的だと言う判断になったわけですね。

引用:

このままでは、「Template Methodって使えないんじゃないの?」と思われてしまうので、
いくつか例を考えてみました。


サンプルは割愛させていただきましたが、このやり方を参考にさせていただいて
Template Methodで僕のコードを書き直すと該当部分は
コード:
abstract class Sample implements ISample
{
  private ISamethingC _iC;
  protected abstract void doSamethingB();
  public void setSamethingC(ISamethingC newISamethingC)
  {
    _iC = newISamethingC;
  }
  protected final void trace(){ _iC.trace(); }
  public void doSamething()
  {
    /* 処理A */
    /* 処理B */
    doSamethingB();
    /* 処理C */
    trace();
    /* 処理D */
  }
}


こんな形に修正してあげれば仕様の条件がTemplate Methodを適用できた場合、
よりよかったということですね。
ところで、一応確認なのですが。。。
引用:

(foo()がfinal methodではないのは、Template Methodだけでは物事を解決できず、直接foo()を実装しなければならない場面に遭遇してしまったため。foo()は出来る限りfinal methodである方が良いと思われる)


ここでいうfinal methodとしたほうが良いというfoo()は
Decorator.foo()ではなくてabstractDecorator.foo()のほうでよいのですよね?
あれ?でもそれだとオーバーライドが????

# あっ処理は静的という前提だからオーバーライドされたら困るということ?
yamasa
ベテラン
会議室デビュー日: 2003/02/15
投稿数: 80
投稿日時: 2003-09-02 16:50
yamasaです。

# 自分で振ったネタなのに、参加が遅くなってしまいました…

かずくんさんの挙げられた例が、私の意図していたものとほぼ同じですね。

ここでのキモは、

(1)
交換可能にする部分がシンプルな1メソッドだけならば、
Strategy パターンよりも Template Method パターンのほうが
見た目はシンプルになる。

(2)
複数のメソッドを独立に交換可能にしようとすると、
とたんに Template Method パターンは破綻してしまう。

ということです。
Template Method パターンを適用する場合は、そのクラス本来の目的に関係する
重要な部分1箇所までにしておき、残りの交換可能な部分は Strategy パターンで
実装するのが良いのではないかと思います。

# 元ネタのスレッドで、Jun氏が「interfaceに非publicなメンバを宣言して…」なんて
# 言っていたのは、Template Method を無茶な形でむりやり使おうとしてたからなのかなあ、
# なんて思ってます。

ところで、かずくんさんの
コード:
public SomeObject create(String inID) {
    SomeObject aObj = (SomeObject)sObjectMap.get(inID); // ex) java.util.Map<String, SomeObject>
    aObj = aObj.copy(); // Prototype
    aObj.initialize(); // TemplateMethod

    return aObj;
}


や、ほむらさんの
コード:
abstract class Sample implements ISample
{
  private ISamethingC _iC;
  protected abstract void doSamethingB();
  public void setSamethingC(ISamethingC newISamethingC)
  {
    _iC = newISamethingC;
  }
  protected final void trace(){ _iC.trace(); }
  public void doSamething()
  {
    /* 処理A */
    /* 処理B */
    doSamethingB();
    /* 処理C */
    trace();
    /* 処理D */
  }
}


が使っているのは Template Method パターンではないと思います。
Template Method パターンというのは、
単一インスタンス内で、サブクラスによってオーバーライドされたメソッドを
スーパークラスのメソッドが呼び出す」
というものですよね。
別インスタンスのメソッドを呼び出すのは、単なる委譲だと思いますが…
かずくん
ぬし
会議室デビュー日: 2003/01/08
投稿数: 759
お住まい・勤務地: 太陽系第三惑星
投稿日時: 2003-09-02 16:59
コード:
public SomeObject create(String inID) {
    SomeObject aObj = (SomeObject)sObjectMap.get(inID); // ex) java.util.Map<String, SomeObject>
    aObj = aObj.copy(); // Prototype
    aObj.initialize(); // TemplateMethod

    return aObj;
}



すんません。ぜんぜんTemplate Methodじゃなかったです。不適切な記述をお許し下さい。

スキルアップ/キャリアアップ(JOB@IT)