私は自分のデザインパターンを検討していますが、コーディングでまだ真剣に使用していないパターンの1つは、デコレータパターンです。
私はパターンを理解していますが、デコレータパターンが最良/最適/エレガントなソリューションであるという現実の世界の時間のいくつかの良い具体例が知りたいです。デコレータパターンの必要性が本当に便利な特定の状況。
ありがとう。
デコレータパターンは、ストリームで頻繁に使用されます。ストリームをストリームでラップして、機能を追加できます。 .Netフレームワークでこれを見てきました-私が知る限り、これは他の場所で発生します。私のお気に入りは、圧縮を追加するためにFileStreamの周りでGZipStreamを使用することです。
オブジェクトのクラスではなく、特定のオブジェクトに機能を追加するために、デコレータパターンが使用されます。オブジェクトをサブクラス化することにより、オブジェクトのクラス全体に機能を追加するのは簡単ですが、この方法で単一のオブジェクトを拡張することは不可能です。デコレータパターンを使用すると、1つのオブジェクトに機能を追加し、他のオブジェクトを変更しないでおくことができます。
Javaでのデコレータパターンの典型的な例は、Java I/O Streams実装です。
FileReader frdr = new FileReader(filename);
LineNumberReader lrdr = new LineNumberReader(frdr);
上記のコードは、ファイルから読み取り、行番号を追跡するリーダー(lrdr
)を作成します。 1行目はファイルリーダー(frdr
)を作成し、2行目は行番号の追跡を追加します。
実際、Java I/OクラスのJavaソースコードを確認することを強くお勧めします。
最近、次のCommandProcessorインターフェースを使用するWebサービスでデコレーターパターンを使用しました。
public Command receive(Request request);
public Response execute(Command command);
public void respond(Response response);
基本的に、CommandProcessorはリクエストを受信して適切なコマンドを作成し、コマンドを実行して適切なレスポンスを作成し、レスポンスを送信します。タイミングを追加してログに記録する場合、既存のCommandProcessorをコンポーネントとして使用するTimerDecoratorを作成しました。 TimerDecoratorはCommandProcessorインターフェイスを実装しますが、タイミングを追加するだけで、実際のCommandProcessorであるターゲットを呼び出します。このようなもの:
public class TimerDecorator implements CommandProcessor {
private CommandProcessor target;
private Timer timer;
public TimerDecorator(CommandProcessor processor) {
this.target = processor;
this.timer = new Timer();
}
public Command receive(Request request) {
this.timer.start();
return this.target.receive(request);
}
public Response execute(Command command) {
return this.target.execute(command);
}
public void respond(Response response) {
this.target.response(response);
this.timer.stop();
// log timer
}
}
したがって、実際のCommandProcessorはTimerDecorator内にラップされ、TimerDecoratorをターゲットCommandProcessorと同じように扱うことができますが、タイミングロジックが追加されました。
Decorator patternは、オブジェクトの既存の機能に影響を与えることなく、実行時にオブジェクトの機能を動的に変更します。
主なユースケース:
欠点:
実世界の例:飲料の価格を計算します。複数のフレーバーが含まれている場合があります。
abstract class Beverage {
protected String name;
protected int price;
public Beverage(){
}
public Beverage(String name){
this.name = name;
}
public void setName(String name){
this.name = name;
}
public String getName(){
return name;
}
protected void setPrice(int price){
this.price = price;
}
protected int getPrice(){
return price;
}
protected abstract void decorateBeverage();
}
class Tea extends Beverage{
public Tea(String name){
super(name);
setPrice(10);
}
public void decorateBeverage(){
System.out.println("Cost of:"+ name +":"+ price);
// You can add some more functionality
}
}
class Coffee extends Beverage{
public Coffee(String name){
super(name);
setPrice(15);
}
public void decorateBeverage(){
System.out.println("Cost of:"+ name +":"+ price);
// You can add some more functionality
}
}
abstract class BeverageDecorator extends Beverage {
protected Beverage beverage;
public BeverageDecorator(Beverage beverage){
this.beverage = beverage;
setName(beverage.getName()+"+"+getDecoratedName());
setPrice(beverage.getPrice()+getIncrementPrice());
}
public void decorateBeverage(){
beverage.decorateBeverage();
System.out.println("Cost of:"+getName()+":"+getPrice());
}
public abstract int getIncrementPrice();
public abstract String getDecoratedName();
}
class SugarDecorator extends BeverageDecorator{
public SugarDecorator(Beverage beverage){
super(beverage);
}
public void decorateBeverage(){
super.decorateBeverage();
decorateSugar();
}
public void decorateSugar(){
System.out.println("Added Sugar to:"+beverage.getName());
}
public int getIncrementPrice(){
return 5;
}
public String getDecoratedName(){
return "Sugar";
}
}
class LemonDecorator extends BeverageDecorator{
public LemonDecorator(Beverage beverage){
super(beverage);
}
public void decorateBeverage(){
super.decorateBeverage();
decorateLemon();
}
public void decorateLemon(){
System.out.println("Added Lemon to:"+beverage.getName());
}
public int getIncrementPrice(){
return 3;
}
public String getDecoratedName(){
return "Lemon";
}
}
public class VendingMachineDecorator {
public static void main(String args[]){
Beverage beverage = new SugarDecorator(new LemonDecorator(new Tea("Assam Tea")));
beverage.decorateBeverage();
beverage = new SugarDecorator(new LemonDecorator(new Coffee("Cappuccino")));
beverage.decorateBeverage();
}
}
出力:
Cost of:Assam Tea:10
Cost of:Assam Tea+Lemon:13
Added Lemon to:Assam Tea
Cost of:Assam Tea+Lemon+Sugar:18
Added Sugar to:Assam Tea+Lemon
Cost of:Cappuccino:15
Cost of:Cappuccino+Lemon:18
Added Lemon to:Cappuccino
Cost of:Cappuccino+Lemon+Sugar:23
Added Sugar to:Cappuccino+Lemon
この例では、飲料に多くのフレーバーを追加した後、Vending Machineで飲料のコストを計算します。
上記の例:
茶のコスト= 10、レモン= 3、砂糖=5。砂糖+レモン+茶を作る場合、18がかかります。
コーヒーのコスト= 15、レモン= 3、砂糖=5。砂糖+レモン+コーヒーを作る場合、コストは23です。
両方の飲料(Tea and Coffee)に同じデコレーターを使用することにより、サブクラスの数が削減されました。 Decoratorパターンがない場合は、異なる組み合わせに対して異なるサブクラスが必要です。
組み合わせは次のようになります。
SugarLemonTea
SugarTea
LemonTea
SugarLemonCapaccuino
SugarCapaccuino
LemonCapaccuino
等.
両方の飲料に同じDecoratorを使用することにより、サブクラスの数が削減されました。このパターンで使用されているinheritanceコンセプトではなく、compositionが原因である可能性があります。
関連するSEの質問:
便利なリンク:
design-patterns-decorator by dzone
装飾 ソースメイキングによる
oodesign 記事
デコレータはシンプルでありながら非常に強力です。懸念の分離を達成するための鍵であり、オープンクローズドプリンシプルにとって不可欠なツールです。製品を注文する一般的な例を見てみましょう。
IOrderGateway
{
void PlaceOrder(Order order);
{
主な実装:AmazonAffiliateOrderGateway
可能なデコレータは次のとおりです。
IncrementPerformanceCounterOrderGateway
QueueOrderForLaterOnTimeoutOrderGateway
EmailOnExceptionOrderGateway
InterceptTestOrderAndLogOrderGateway
here のより詳細な例は、注文の完了時にギフトカードを使用して作成された注文の連絡先を保存するデコレータを示しています。
class OrderWithGiftCardGateway extends OrderGatewayDecorator { ... public function createOrder(CreateOrderRequest $order) { if ($this->containsGiftCard($order)) { $this->addContactToFolder($order); } return parent::createOrder($order); } }
Zend Frameworkはフォーム要素にデコレーターを使用します
詳細: http://framework.zend.com/manual/en/zend.form.decorators.html
デコレータパターンは、C#言語自体によって使用されます。 C#のストリームI/Oクラスを装飾するために使用されます。装飾されたバージョンは、BufferedStream、FileStrem、MemoryStrem、NetworkStream、およびCryptoStreamクラスです。
これらのサブクラスは、Streamクラスを継承し、Streamクラスのインスタンスも含みます。
続きを読む こちら