GOFに記載されているように、Decoratorパターンを研究していました。
デコレータパターンを理解してください。誰かがこれが現実の世界で便利なユースケースの例を教えてくれませんか?
GOFに記載されているように、Decoratorパターンを研究していました。
デコレータパターンを理解してください。誰かがこれが現実の世界で便利なユースケースの例を教えてくれませんか?
回答:
デコレータパターンは、オブジェクトに責任を動的に追加するという単一の目的を達成します。
ピザ屋の場合を考えてみましょう。ピザショップではピザの種類をほとんど販売せず、メニューにトッピングも提供します。次に、ピザ屋がピザとトッピングの各組み合わせの価格を提供しなければならない状況を想像してみてください。4つの基本的なピザと8つの異なるトッピングがある場合でも、アプリケーションはピザとトッピングのこれらすべての具体的な組み合わせを維持することに夢中になります。
ここにデコレータパターンがあります。
デコレータパターンに従って、デコレータとしてトッピングを実装し、ピザはそれらのトッピングのデコレータによって装飾されます。実際、各顧客は自分の欲望のトッピングを望み、最終的な請求額はベースのピザと追加で注文したトッピングで構成されます。それぞれのトッピングのデコレータは、それが飾っているピザとその価格を知っています。ToppingオブジェクトのGetPrice()メソッドは、ピザとトッピングの両方の累積価格を返します。
上記の説明のコード例を以下に示します。
public abstract class BasePizza
{
protected double myPrice;
public virtual double GetPrice()
{
return this.myPrice;
}
}
public abstract class ToppingsDecorator : BasePizza
{
protected BasePizza pizza;
public ToppingsDecorator(BasePizza pizzaToDecorate)
{
this.pizza = pizzaToDecorate;
}
public override double GetPrice()
{
return (this.pizza.GetPrice() + this.myPrice);
}
}
class Program
{
[STAThread]
static void Main()
{
//Client-code
Margherita pizza = new Margherita();
Console.WriteLine("Plain Margherita: " + pizza.GetPrice().ToString());
ExtraCheeseTopping moreCheese = new ExtraCheeseTopping(pizza);
ExtraCheeseTopping someMoreCheese = new ExtraCheeseTopping(moreCheese);
Console.WriteLine("Plain Margherita with double extra cheese: " + someMoreCheese.GetPrice().ToString());
MushroomTopping moreMushroom = new MushroomTopping(someMoreCheese);
Console.WriteLine("Plain Margherita with double extra cheese with mushroom: " + moreMushroom.GetPrice().ToString());
JalapenoTopping moreJalapeno = new JalapenoTopping(moreMushroom);
Console.WriteLine("Plain Margherita with double extra cheese with mushroom with Jalapeno: " + moreJalapeno.GetPrice().ToString());
Console.ReadLine();
}
}
public class Margherita : BasePizza
{
public Margherita()
{
this.myPrice = 6.99;
}
}
public class Gourmet : BasePizza
{
public Gourmet()
{
this.myPrice = 7.49;
}
}
public class ExtraCheeseTopping : ToppingsDecorator
{
public ExtraCheeseTopping(BasePizza pizzaToDecorate)
: base(pizzaToDecorate)
{
this.myPrice = 0.99;
}
}
public class MushroomTopping : ToppingsDecorator
{
public MushroomTopping(BasePizza pizzaToDecorate)
: base(pizzaToDecorate)
{
this.myPrice = 1.49;
}
}
public class JalapenoTopping : ToppingsDecorator
{
public JalapenoTopping(BasePizza pizzaToDecorate)
: base(pizzaToDecorate)
{
this.myPrice = 1.49;
}
}
これは、既存のオブジェクトに新しい動作を動的に追加する簡単な例、またはデコレータパターンです。JavaScriptなどの動的言語の性質上、このパターンは言語自体の一部になります。
// Person object that we will be decorating with logging capability
var person = {
name: "Foo",
city: "Bar"
};
// Function that serves as a decorator and dynamically adds the log method to a given object
function MakeLoggable(object) {
object.log = function(property) {
console.log(this[property]);
}
}
// Person is given the dynamic responsibility here
MakeLoggable(person);
// Using the newly added functionality
person.log('name');
switch
または内で、if
これはクラスに動的に動作を追加する素晴らしい例であると主張することができます。しかし、このパターンでデコレータと装飾されたオブジェクトを定義するには、少なくとも2つのクラスが必要です。
Java i / oモデルがデコレーターパターンに基づいていることは注目に値します。このリーダーをそのリーダーの上に重ねることは、デコレータの実際の例です。
例-シナリオ-暗号化モジュールを作成しているとしましょう。この暗号化では、DES-データ暗号化標準を使用してクリアファイルを暗号化できます。同様に、システムでは、AES-Advance暗号化標準として暗号化を持つことができます。また、暗号化を組み合わせることができます-最初にDES、次にAES。または、最初にAES、次にDESを使用することもできます。
ディスカッション-この状況にどのように対応しますか?このような組み合わせのオブジェクト(AESとDESなど)を作成し続けることはできません。合計4つの組み合わせです。したがって、4つの個別のオブジェクトが必要です。これは、暗号化の種類が増えるにつれて複雑になります。
解決策-実行時にスタック-必要に応じて組み合わせ-を構築し続けます。このスタックアプローチのもう1つの利点は、簡単に解くことができることです。
ここに解決策があります-C ++で。
まず、スタックの基本単位である基本クラスが必要です。スタックのベースとして考えることができます。この例では、クリアファイルです。常にポリモーフィズムに従いましょう。まず、この基本ユニットのインターフェースクラスを作成します。このように、あなたはあなたが望むようにそれを実装することができます。また、この基本的な単位を含めながら、依存関係について考える必要はありません。
これがインターフェースクラスです-
class IclearData
{
public:
virtual std::string getData() = 0;
virtual ~IclearData() = 0;
};
IclearData::~IclearData()
{
std::cout<<"Destructor called of IclearData"<<std::endl;
}
次に、このインターフェースクラスを実装します。
class clearData:public IclearData
{
private:
std::string m_data;
clearData();
void setData(std::string data)
{
m_data = data;
}
public:
std::string getData()
{
return m_data;
}
clearData(std::string data)
{
setData(data);
}
~clearData()
{
std::cout<<"Destructor of clear Data Invoked"<<std::endl;
}
};
ここで、デコレータ抽象クラスを作成しましょう。これを拡張して、あらゆる種類のフレーバーを作成できます。ここで、フレーバーは暗号化タイプです。このデコレータ抽象クラスは、基本クラスに関連しています。したがって、デコレータは一種のインターフェイスクラスです。したがって、継承を使用する必要があります。
class encryptionDecorator: public IclearData
{
protected:
IclearData *p_mclearData;
encryptionDecorator()
{
std::cout<<"Encryption Decorator Abstract class called"<<std::endl;
}
public:
std::string getData()
{
return p_mclearData->getData();
}
encryptionDecorator(IclearData *clearData)
{
p_mclearData = clearData;
}
virtual std::string showDecryptedData() = 0;
virtual ~encryptionDecorator() = 0;
};
encryptionDecorator::~encryptionDecorator()
{
std::cout<<"Encryption Decorator Destructor called"<<std::endl;
}
それでは、具体的なデコレータクラスを作成しましょう-暗号化タイプ-AES-
const std::string aesEncrypt = "AES Encrypted ";
class aes: public encryptionDecorator
{
private:
std::string m_aesData;
aes();
public:
aes(IclearData *pClearData): m_aesData(aesEncrypt)
{
p_mclearData = pClearData;
m_aesData.append(p_mclearData->getData());
}
std::string getData()
{
return m_aesData;
}
std::string showDecryptedData(void)
{
m_aesData.erase(0,m_aesData.length());
return m_aesData;
}
};
ここで、デコレータのタイプがDESであるとします。
const std :: string desEncrypt = "DES Encrypted";
class des: public encryptionDecorator
{
private:
std::string m_desData;
des();
public:
des(IclearData *pClearData): m_desData(desEncrypt)
{
p_mclearData = pClearData;
m_desData.append(p_mclearData->getData());
}
std::string getData(void)
{
return m_desData;
}
std::string showDecryptedData(void)
{
m_desData.erase(0,desEncrypt.length());
return m_desData;
}
};
このデコレータクラスを使用するクライアントコードを作成しましょう-
int main()
{
IclearData *pData = new clearData("HELLO_CLEAR_DATA");
std::cout<<pData->getData()<<std::endl;
encryptionDecorator *pAesData = new aes(pData);
std::cout<<pAesData->getData()<<std::endl;
encryptionDecorator *pDesData = new des(pAesData);
std::cout<<pDesData->getData()<<std::endl;
/** unwind the decorator stack ***/
std::cout<<pDesData->showDecryptedData()<<std::endl;
delete pDesData;
delete pAesData;
delete pData;
return 0;
}
次の結果が表示されます-
HELLO_CLEAR_DATA
Encryption Decorator Abstract class called
AES Encrypted HELLO_CLEAR_DATA
Encryption Decorator Abstract class called
DES Encrypted AES Encrypted HELLO_CLEAR_DATA
AES Encrypted HELLO_CLEAR_DATA
Encryption Decorator Destructor called
Destructor called of IclearData
Encryption Decorator Destructor called
Destructor called of IclearData
Destructor of clear Data Invoked
Destructor called of IclearData
これがUMLダイアグラムです-それのクラス表現。場合によっては、コードをスキップして、デザインの側面に集中する必要があります。
strategy pattern
ますか?
デコレータパターンは、このオブジェクトの他の同様のサブクラスとチェーンすることにより、オブジェクトの機能を変更または構成するのに役立ちます。
最良の例は、java.ioパッケージのInputStreamおよびOutputStreamクラスです。
File file=new File("target","test.txt");
FileOutputStream fos=new FileOutputStream(file);
BufferedOutputStream bos=new BufferedOutputStream(fos);
ObjectOutputStream oos=new ObjectOutputStream(bos);
oos.write(5);
oos.writeBoolean(true);
oos.writeBytes("decorator pattern was here.");
//... then close the streams of course.
Javaのデコレーターデザインパターンとは
GoFブック(Design Patterns:Elements of Reusable Object-Oriented Software、1995、Pearson Education、Inc. Publishing as Pearson Addison Wesley)のデコレーターパターンの正式な定義では、次のことができます。
「オブジェクトに追加の責任を動的に付加します。デコレーターは、機能を拡張するためのサブクラス化の柔軟な代替手段を提供します。」
ピザがあり、チキンマサラ、オニオン、モッツァレラチーズなどのトッピングで飾りたいとしましょう。それをJavaで実装する方法を見てみましょう...
Javaでデコレーターデザインパターンを実装する方法を示すプログラム。
Pizza.java:
<!-- language-all: lang-html -->
package com.hubberspot.designpattern.structural.decorator;
public class Pizza {
public Pizza() {
}
public String description(){
return "Pizza";
}
}
package com.hubberspot.designpattern.structural.decorator;
public abstract class PizzaToppings extends Pizza {
public abstract String description();
}
package com.hubberspot.designpattern.structural.decorator;
public class ChickenMasala extends PizzaToppings {
private Pizza pizza;
public ChickenMasala(Pizza pizza) {
this.pizza = pizza;
}
@Override
public String description() {
return pizza.description() + " with chicken masala, ";
}
}
package com.hubberspot.designpattern.structural.decorator;
public class MozzarellaCheese extends PizzaToppings {
private Pizza pizza;
public MozzarellaCheese(Pizza pizza) {
this.pizza = pizza;
}
@Override
public String description() {
return pizza.description() + "and mozzarella cheese.";
}
}
package com.hubberspot.designpattern.structural.decorator;
public class Onion extends PizzaToppings {
private Pizza pizza;
public Onion(Pizza pizza) {
this.pizza = pizza;
}
@Override
public String description() {
return pizza.description() + "onions, ";
}
}
package com.hubberspot.designpattern.structural.decorator;
public class TestDecorator {
public static void main(String[] args) {
Pizza pizza = new Pizza();
pizza = new ChickenMasala(pizza);
pizza = new Onion(pizza);
pizza = new MozzarellaCheese(pizza);
System.out.println("You're getting " + pizza.description());
}
}
私は仕事で広範囲にDecoratorパターンを使用しました。私が作っ自分のブログに記事をログでそれを使用する方法について。
デコレータパターンを使用すると、オブジェクトに動作を動的に追加できます。
さまざまな種類のハンバーガーの価格を計算するアプリを作成する必要がある例を考えてみましょう。「ラージ」や「チーズ付き」など、さまざまなバリエーションのハンバーガーを処理する必要があります。それぞれのバーガーは、基本的なハンバーガーと比較して価格が設定されています。たとえば、バーガーにチーズを10ドル、大きなバーガーに15ドルを追加します。
この場合、これらを処理するサブクラスを作成したくなるかもしれません。これをRubyで次のように表現します。
class Burger
def price
50
end
end
class BurgerWithCheese < Burger
def price
super + 15
end
end
上記の例では、BurgerWithCheeseクラスはBurgerを継承し、priceメソッドをオーバーライドして、スーパークラスで定義された価格に$ 15を追加します。また、LargeBurgerクラスを作成し、Burgerを基準とした価格を定義します。ただし、「大」と「チーズあり」の組み合わせの新しいクラスを定義する必要もあります。
「フライドポテト」を提供する必要がある場合はどうなるでしょうか。これらの組み合わせを処理するクラスはすでに4つあります。「大」、「チーズ付き」、「フライドポテト」の3つのプロパティのすべての組み合わせを処理するには、さらに4つのクラスを追加する必要があります。現在、8つのクラスが必要です。別のプロパティを追加すると、16が必要になります。これは2 ^ nに増加します。
代わりに、Burgerオブジェクトを取り込むBurgerDecoratorを定義してみましょう。
class BurgerDecorator
def initialize(burger)
self.burger = burger
end
end
class BurgerWithCheese < BurgerDecorator
def price
self.burger.price + 15
end
end
burger = Burger.new
cheese_burger = BurgerWithCheese.new(burger)
cheese_burger.price # => 65
上記の例では、BurgerWithCheeseクラスが継承するBurgerDecoratorクラスを作成しました。LargeBurgerクラスを作成することで、「大きな」バリエーションを表すこともできます。これで、実行時のチーズ入りの大きなハンバーガーを次のように定義できます。
b = LargeBurger.new(cheese_burger)
b.price # => 50 + 15 + 20 = 85
継承を使用して "with fries"バリエーションを追加するには、さらに4つのサブクラスを追加する必要があることを覚えていますか?デコレータを使用すると、新しいバリエーションを処理し、実行時にこれを処理するために、BurgerWithFriesという1つの新しいクラスを作成するだけです。新しいプロパティごとに、すべての順列をカバーするためのデコレータが必要になります。
PS。これは、Rubyでのデコレーターパターンの使用について書いた記事の短縮版です。詳細な例を知りたい場合は、この記事を読むことができます。
デコレータ:
ソースメイキングを参照に関する記事をてください。
デコレーター(抽象):コンポーネントインターフェースを実装する抽象クラス/インターフェースです。これにはComponentインターフェースが含まれています。このクラスがない場合、さまざまな組み合わせのためにConcreteDecoratorsの多くのサブクラスが必要です。コンポーネントの構成により、不要なサブクラスが削減されます。
JDKの例:
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(new File("a.txt")));
while(bis.available()>0)
{
char c = (char)bis.read();
System.out.println("Char: "+c);;
}
UML図とコード例については、以下のSEの質問をご覧ください。
役立つ記事:
デコレータパターンの実際の単語例:VendingMachineDecoratorは@で説明されています
Beverage beverage = new SugarDecorator(new LemonDecorator(new Tea("Assam Tea")));
beverage.decorateBeverage();
beverage = new SugarDecorator(new LemonDecorator(new Coffee("Cappuccino")));
beverage.decorateBeverage();
上記の例では、紅茶またはコーヒー(飲料)は砂糖とレモンで装飾されています。
ウィキペディアに、スクロールバーでウィンドウを装飾する例があります:
http://en.wikipedia.org/wiki/Decorator_pattern
以下は、「チームメンバー、チームリーダー、マネージャー」の別の非常に「現実的な」例です。これは、デコレーターパターンが単純な継承では置き換えられないことを示しています。
https://zishanbilal.wordpress.com/2011/04/28/design-patterns-by-examples-decorator-pattern/
昔、コードベースをリファクタリングしてDecoratorパターンを使用していたので、ユースケースを説明しようと思います。
一連のサービスがあり、ユーザーが特定のサービスのライセンスを取得したかどうかに基づいて、サービスを開始する必要があるとします。
すべてのサービスに共通のインターフェースがあります
interface Service {
String serviceId();
void init() throws Exception;
void start() throws Exception;
void stop() throws Exception;
}
abstract class ServiceSupport implements Service {
public ServiceSupport(String serviceId, LicenseManager licenseManager) {
// assign instance variables
}
@Override
public void init() throws Exception {
if (!licenseManager.isLicenseValid(serviceId)) {
throw new Exception("License not valid for service");
}
// Service initialization logic
}
}
注意深く観察すると、ServiceSupport
に依存しLicenseManager
ます。しかし、なぜそれに依存する必要がありますLicenseManager
か?ライセンス情報を確認する必要のないバックグラウンドサービスが必要な場合はどうなりますか。現在の状況では、何とかLicenseManager
して戻るために訓練する必要がありますtrue
でバックグラウンドサービスためのます。このアプローチは私にはよく思われませんでした。私によると、ライセンスチェックと他のロジックは互いに直交していた。
そこで、Decoratorパターンが助けとなり、TDDによるリファクタリングが始まります。
class LicensedService implements Service {
private Service service;
public LicensedService(LicenseManager licenseManager, Service service) {
this.service = service;
}
@Override
public void init() {
if (!licenseManager.isLicenseValid(service.serviceId())) {
throw new Exception("License is invalid for service " + service.serviceId());
}
// Delegate init to decorated service
service.init();
}
// override other methods according to requirement
}
// Not concerned with licensing any more :)
abstract class ServiceSupport implements Service {
public ServiceSupport(String serviceId) {
// assign variables
}
@Override
public void init() {
// Service initialization logic
}
}
// The services which need license protection can be decorated with a Licensed service
Service aLicensedService = new LicensedService(new Service1("Service1"), licenseManager);
// Services which don't need license can be created without one and there is no need to pass license related information
Service aBackgroundService = new BackgroundService1("BG-1");
PubGの例を見てみましょう。アサルトライフルは4倍ズームで最適に機能します。その間、補正器と抑制器も必要になります。それは反動を減らし、発砲音とエコーを減らします。この機能を実装して、プレイヤーがお気に入りの銃とその付属品を購入できるようにする必要があります。プレーヤーは、銃またはアクセサリの一部またはアクセサリのすべてを購入でき、それに応じて課金されます。
ここでデコレータパターンがどのように適用されるかを見てみましょう。
上記の3つのアクセサリがすべて付いたSCAR-Lを購入したいとします。
これにより、次のようなクラス図が作成されます。
これで、次のようなクラスを作成できます。
public abstract class Gun {
private Double cost;
public Double getCost() {
return cost;
}
}
public abstract class GunAccessories extends Gun { }
public class Scarl extends Gun {
public Scarl() {
cost = 100;
}
}
public class Suppressor extends GunAccessories {
Gun gun;
public Suppressor(Gun gun) {
cost = 5;
this.gun = gun;
}
public double getCost(){
return cost + gun.getCost();
}
}
public class GunShop{
public static void main(String args[]){
Gun scarl = new Scarl();
scarl = new Supressor(scarl);
System.out.println("Price is "+scarl.getCost());
}
}
同様に他のアクセサリーを追加して、ガンを装飾することもできます。
参照:
https://nulpointerexception.com/2019/05/05/a-beginner-guide-to-decorator-pattern/
デコレータデザインパターン:このパターンは、実行時にオブジェクトの特性を変更するのに役立ちます。オブジェクトにさまざまなフレーバーを提供し、そのフレーバーで使用したい成分を柔軟に選択できます。
実際の例:機内にメインキャビンシートがあるとします。これで、シートで複数のアメニティを選択できるようになりました。各アメニティには、それに関連する独自のコストがあります。ユーザーがWifiとプレミアムフードを選択すると、座席+ wifi +プレミアムフードの料金が請求されます。
この場合、デコレータのデザインパターンが非常に役立ちます。上記のリンクにアクセスして、デコレータパターンと実際の1つの例の実装について詳しく理解してください。