Javaデザインパターン(3/23):デコレータパターン

意味

デコレータパターンは、オブジェクトに責任を動的に付加します。機能を拡張するために、デコレータは継承のより柔軟な代替手段を提供します

ケース1:3時は何時ですか

元のクラスのデザインであるコーヒーチェーンの注文システムを更新します。

ビジネスを拡大するために、コーヒーショップでは、顧客が次のようなさまざまな調味料を飲料に追加できるようにしています。

  1. 蒸しミルク
  2. 豆乳大豆
  3. モカ(チョコレート味)モカ
  4. ミルク泡立て器を覆う

追加された調味料は別の料金がかかります。

初挑戦

クラス爆発

これは、2つの重大な設計原則に違反しています。

  1. 構成をより多く使用し、継承をより少なくします。
  2. 相互作用するオブジェクト間で緩く結合された設計を目指します。

再試行

インスタンス変数と継承を使用して、これらのスパイスを追跡します。

この試みの制限:

  1. 調味料の価格を変更すると、既存のコードが変更されます。
  2. 新しい調味料が表示されたら、新しいメソッドを追加し、スーパークラスのcost()メソッドを変更する必要があります。
  3. 将来、新しい飲料が開発される可能性があります。これらの飲料(例:アイスティー)の場合、一部の調味料は適切でない場合がありますが、この設計では、Tea(tea)サブクラスは、次のような不適切なメソッドを継承します:hasWhip()(with milk)Bubble) 。
  4. 顧客がダブルモカを望んでいる場合はどうなりますか?

設計原則:クラスは拡張のために開いており、変更のために閉じている必要があります

私たちの目標は、既存のコードを変更することなく、クラスを簡単に拡張して新しい動作を組み込むことができるようにすることです。

これを実現できれば、そのような設計は変更に対して回復力があり、変化するニーズを満たすために新しい機能を受け入れることができます。

矛盾しているように見えるかもしれませんが、コードを直接変更せずにコードを拡張できる手法がいくつかあります。

拡張が必要な​​コードの部分を選択するときは注意してください。どこにでもオープンクローズの原則を適用することは無駄で不必要であり、コードを複雑で理解しにくくします。

(MyNote:順応性がある)

デコレータパターンを試してください

飲み物を本体として、実行時にスパイスで「飾る」ことにします。たとえば、顧客が
モカとミルク泡立て器のダークローストを希望する場合は、次のようにします。

1.DarkRoastオブジェクトを取得します

2.モカオブジェクトで飾ります

3.ホイップオブジェクトで飾ります

4. cost()メソッドを呼び出し、調味料のコストを追加するためにデリゲートに依存し
ます。

デコレータパターンの機能

  • デコレータと装飾されたオブジェクトは同じスーパータイプを持っています。

  • オブジェクトを1つ以上のデコレータでラップできます。

  • デコレータと装飾されたオブジェクトは同じスーパータイプであるため、元のオブジェクト(ラップされたもの)が必要な場合は常に、装飾されたオブジェクトを代わりに使用できます。

  • デコレータは、特定の目的を達成するために、委任されたデコレータの動作の前および/または後に独自の動作を追加できます。

  • オブジェクトはいつでも装飾できるため、実行時にお気に入りのデコレータを使用してオブジェクトを動的かつ無制限に装飾できます。

この例のクラス図

グレーディングオーバー

飲み物のカテゴリ

public abstract class Beverage {
    
    
	String description = "Unknown Beverage";
  
	public String getDescription() {
    
    
		return description;
	}
 
	public abstract double cost();
}
HouseBlend
public class HouseBlend extends Beverage {
    
    
	public HouseBlend() {
    
    
		description = "House Blend Coffee";
	}
 
	public double cost() {
    
    
		return .89;
	}
}
DarkRoast
public class DarkRoast extends Beverage {
    
    
	public DarkRoast() {
    
    
		description = "Dark Roast Coffee";
	}
 
	public double cost() {
    
    
		return .99;
	}
}
エスプレッソ
public class Espresso extends Beverage {
    
    
  
	public Espresso() {
    
    
		description = "Espresso";
	}
  
	public double cost() {
    
    
		return 1.99;
	}
}

カフェイン抜き
public class Decaf extends Beverage {
    
    
	public Decaf() {
    
    
		description = "Decaf Coffee";
	}
 
	public double cost() {
    
    
		return 1.05;
	}
}
調味料の装飾
public abstract class CondimentDecorator extends Beverage {
    
    
	Beverage beverage;
	public abstract String getDescription();
}
牛乳
public class Milk extends CondimentDecorator {
    
    
	public Milk(Beverage beverage) {
    
    
		this.beverage = beverage;
	}

	public String getDescription() {
    
    
		return beverage.getDescription() + ", Milk";
	}

	public double cost() {
    
    
		return .10 + beverage.cost();
	}
}
モカ
public class Mocha extends CondimentDecorator {
    
    
	public Mocha(Beverage beverage) {
    
    
		this.beverage = beverage;
	}
 
	public String getDescription() {
    
    
		return beverage.getDescription() + ", Mocha";
	}
 
	public double cost() {
    
    
		return .20 + beverage.cost();
	}
}
大豆
public class Soy extends CondimentDecorator {
    
    
	public Soy(Beverage beverage) {
    
    
		this.beverage = beverage;
	}

	public String getDescription() {
    
    
		return beverage.getDescription() + ", Soy";
	}

	public double cost() {
    
    
		return .15 + beverage.cost();
	}
}
ホイップ
public class Whip extends CondimentDecorator {
    
    
	public Whip(Beverage beverage) {
    
    
		this.beverage = beverage;
	}
 
	public String getDescription() {
    
    
		return beverage.getDescription() + ", Whip";
	}
 
	public double cost() {
    
    
		return .10 + beverage.cost();
	}
}

テストクラスを実行する

public class StarbuzzCoffee {
    
    
 
	public static void main(String args[]) {
    
    
		Beverage beverage = new Espresso();
		System.out.println(beverage.getDescription() 
				+ " $" + beverage.cost());
 
		System.out.println("---");
		
		
		Beverage beverage2 = new DarkRoast();
		beverage2 = new Mocha(beverage2);
		beverage2 = new Mocha(beverage2);
		beverage2 = new Whip(beverage2);
		System.out.println(beverage2.getDescription() 
				+ " $" + beverage2.cost());
		
		System.out.println("---");
 
		Beverage beverage3 = new HouseBlend();
		beverage3 = new Soy(beverage3);
		beverage3 = new Mocha(beverage3);
		beverage3 = new Whip(beverage3);
		System.out.println(beverage3.getDescription() 
				+ " $" + beverage3.cost());
	}
}

演算結果:

Espresso $1.99
---
Dark Roast Coffee, Mocha, Mocha, Whip $1.49
---
House Blend Coffee, Soy, Mocha, Whip $1.34

ケース2:独自のJava I/0デコレータを作成する

JDKのIOストリームの概要

java.ioパッケージのクラスの多くはデコレータです。

以下は、デコレータを使用して機能を組み合わせ
てファイルデータを読み取るオブジェクトの典型的なコレクションです。

BufferedInputStreamとLineNumberInputStreamはどちらもFilterInputStreamから拡張されており、FilterInputStreamは抽象装飾クラスです。

java.ioは実際にはあまり違いはありません。java.io APIを絞り込んで、ドキュメントを簡単に表示し、目的に合わせてさまざまな「入力」ストリームデコレータを組み合わせることができるようにしました。

OutputStreamが同じように設計されていることがわかります。また、Reader / Writerストリーム(文字データに基づく入力および出力として)とInputStream / OutputStreamクラスはかなり類似していることがわかります(いくつかの小さな違いと矛盾がありますが、それらは非常に類似しているため、これらを理解できるはずです。クラス)。

ただし、Java I / Oには、デコレータパターンの欠点もあります。デコレータパターンを使用すると、デザインに小さなクラスが多数発生し、その数が多すぎるため、このAPIを使用するプログラマーに問題が発生する可能性があります。

ただし、デコレータがどのように機能するかを理解したので、他の人の高度に装飾されたAPIを使用すると、パッケージ化された方法で必要なものを取得するために、デコレータクラスがどのように編成されているかを簡単に識別できます。

グレーディングオーバー

このアイデアはどうですか。入力ストリーム内のすべての大文字を小文字に変換するデコレータを作成します。

例:「私はデコレータパターンを知っているので私はルールです!」を読むと、デコレータはそれを「私はデコレータパターンを知っているので私は支配します!」に変換します。

LowerCaseInputStream

すべてのInputStreamの抽象デコレータであるFilterInputStreamを拡張します。

import java.io.*;

public class LowerCaseInputStream extends FilterInputStream {
    
    

	public LowerCaseInputStream(InputStream in) {
    
    
		super(in);
	}
 
	public int read() throws IOException {
    
    
		int c = in.read();
		return (c == -1 ? c : Character.toLowerCase((char)c));
	}
		
	public int read(byte[] b, int offset, int len) throws IOException {
    
    
		int result = in.read(b, offset, len);
		for (int i = offset; i < offset+result; i++) {
    
    
			b[i] = (byte)Character.toLowerCase((char)b[i]);
		}
		return result;
	}
}
テストファイルを作成するD:\ test.txt
I know the Decorator Pattern therefore I RULE!
テストクラスを実行する
import java.io.*;

public class InputTest {
    
    
	
	public static void main(String[] args) throws IOException {
    
    
		
		int c;
		InputStream in = null;
		try {
    
    
			in = 
				new LowerCaseInputStream( 
					new BufferedInputStream(
						new FileInputStream("D:\\text.txt")));

			while((c = in.read()) >= 0) {
    
    
				System.out.print((char)c);
			}
		} catch (IOException e) {
    
    
			e.printStackTrace();
		} finally {
    
    
			if (in != null) {
    
     in.close(); }
		}
		
		
		System.out.println();
		System.out.println("---");
		
		//
		try (InputStream in2 = 
				new LowerCaseInputStream(
					new BufferedInputStream(
						new FileInputStream("D:\\text.txt")))) 
		{
    
    
			while((c = in2.read()) >= 0) {
    
    
				System.out.print((char)c);
			}
		} catch (IOException e) {
    
    
			e.printStackTrace();
		}
	}
}

演算結果:

i know the decorator pattern therefore i rule!
---
i know the decorator pattern therefore i rule!

参考文献

  1. 「ヘッドファーストデザインパターン」

おすすめ

転載: blog.csdn.net/u011863024/article/details/119843361