解決すべき問題
コーヒーショップの課金問題を考えてみましょう。柔軟なコーヒー価格計算を実現する方法です。コーヒーショップは主にコーヒーを販売していますが、後に顧客の好みの違いに対応するために、純粋なコーヒーは単調すぎたので、組み合わせを変えることを考えました。材料を変えて別の飲み物を作ることで、バラエティに富んでいます。しかし、その後、さまざまな新しい品種の新しく正確な価格をどのように計算するかという問題も生じました。2つのオプション:
第一种
:継承メソッドを使用して、純粋なコーヒーを基本クラスとして使用できます。次に、バラエティが必要な場合は、サブクラスを生成して、価格計算メソッドを書き直すためのバラエティとして使用したり、このバラエティに他の関数を追加したりできます。ただし、継承の大きな問題は、このようなスキームを実現するには、さまざまなサブカテゴリを導出する前に、利用可能な品種を最初に知っておく必要があることです。ただし、将来、既存の品種の一部を削除する場合は、または、コンテンツを追加したり、この品種を直接削除したりすると、対応するカテゴリを常に変更するのは非常に面倒であり、欠点があります。品種が多く、品種間の差が小さいと、種子が大量に生成されます。時間になると、クラスとして分けるのはとても面倒です。
第二种
:本日お話しする本記事のテーマデコレーターパターンを採用します。人生の例を比較してみましょう:紙の写真。この写真を長く保ちたい場合は、最初にこの写真を可塑化できます。プラスチックのパッケージングの後、十分ではないと思われる場合は、この写真をもう一度提供することがあります。フォトフレームを取り付けます。フォトフレームを追加しても写真を保護できない場合は、ガラスカバーを追加します。この例では、写真自体が装飾対象であり、プラスチック、フォトフレーム、ガラスカバーがすべてデコレータとして使用されていることがわかります。各レイヤーのデコレータは、装飾されている最も内側のオブジェクトを変更しません。ここでは、特定のコーヒードリンクをデコレータと見なし、追加する食べ物や飲み物をデコレータと見なすことができます。各コーヒードリンクは、さまざまなデコレータで装飾できます。
さまざまなコーヒードリンクの価格を計算する上記の問題は、プログラミングの概念に基づいています。プログラミングの概念は、オブジェクトに機能を透過的に追加し、機能の動的な組み合わせを実現する方法です。これはデコレータモードの機能です。
モード定義
デコレータモードでは、オブジェクトに機能を動的に追加したり、外部から透過的にオブジェクトに機能を追加したりできます。透明地给一个对象增加功能,就是说要给一个对象增加功能,但是不能让这个对象知道,也就是不能去修改这个对象
装飾された各人は、複数の装飾者によって装飾することができます。例:ブラックコーヒー(装飾された人)は、牛乳(装飾者)、果物(装飾者)で飾ることができ、異なる装飾者の間で順序の制限はありません。
実装
デコレータは、デコレーションされたオブジェクトと同じクラスを継承するか、同じインターフェイスを実装する必要があり(iOSでは同じプロトコルに準拠すると言われています)、特定のデコレータの実装で、デコレータオブジェクトを転送します[これにはデコレータが必要ですオブジェクトは装飾されたオブジェクトを保持します]
以下は、実装されたUMLダイアグラムと、異なるクラス間の呼び出し階層ダイアグラムです。
CoffeeComponent
:コーヒーの基本クラス(インターフェイス/プロトコルにすることもできます)BlackCoffee
:特定のコーヒーは装飾されるオブジェクトです。CondimentDecorator
:成分の基本クラス(デコレータの基本クラス)であり、デコレートの基本クラスから継承する必要があり、属性のタイプもCoffeeComponent
保持する必要がCoffeeComponent
あります。MilkDecorator
:ミルクデコレータ、特定のデコレータオブジェクト。
上記の階層図からわかるように、多層デコレータは装飾されたオブジェクトの外側でレイヤーごとにラップされ、関数メソッドの呼び出しも装飾されたオブジェクトへのレイヤーごとの再帰呼び出しです。図からわかるように、ブラックコーヒーを牛乳で飾ると、牛乳のデコレーターが新しいデコレーターになり、将来的には他のデコレーターでも飾ることができ、デコレーター間の注文要件はありません。シーケンスはあなた自身の希望に応じて行うことができます。
//********************************咖啡组件(基类)*********************
@interface CoffeeComponent : NSObject
- (double)getPrice;
@end
@implementation CoffeeComponent//抽象组件,可以写默认实现的方法,也可以用协议实现
- (double)getPrice
{
return 0.f;
}
@end
//********************************黑咖啡(具体咖啡类)*********************
@interface BlackCoffee : CoffeeComponent//继承于抽象组件的具体组件
- (double)getPrice;
@end
@implementation BlackCoffee
- (double)getPrice
{
return 5;
}
@end
//********************************装饰者基类*********************
@interface CondimentDecorator : CoffeeComponent//继承于组件的装饰者抽象类
- (instancetype)initWithComponent:(CoffeeComponent *)component;
@property (nonatomic,strong)CoffeeComponent *component;
@end
@implementation CondimentDecorator
- (instancetype)initWithComponent:(CoffeeComponent *)component
{
if (self = [super init]) {
_component = component;
}
return self;
}
@end
//********************************牛奶装饰者(具体装饰者)*********************
@interface MilkDecorator : CondimentDecorator//继承于抽象佐料装饰者的具体装饰者
@end
@implementation MilkDecorator
- (double)getPrice
{
NSLog(@"牛奶加了2元");
return 2 + [self.component getPrice];
}
@end
//在此省略其它装饰者的代码,与牛奶装饰者的代码一样。代码可以查看demo。
//=========================外部调用=====================
//纯咖啡
BlackCoffee *blackCoffee = [[BlackCoffee alloc]init];
//加奶
MilkDecorator *milkDecorator = [[MilkDecorator alloc]initWithComponent:blackCoffee];
//加豆浆
SoyDecorator *soyDecorator = [[SoyDecorator alloc]initWithComponent:milkDecorator];
//加水果
FruitDecorator *fruitDecorator = [[FruitDecorator alloc]initWithComponent:soyDecorator];
NSLog(@"一共多少钱%f",[fruitDecorator getPrice]);
総括する
- デコレータパターンは継承よりも柔軟性があります。継承は静的であり、継承されたサブクラスは基本クラスと同じ機能を持ちますが、デコレータパターンは関数を異なるデコレータに分割し、必要な関数を動的に選択できます。
- デコレータモードの本質は動的な組み合わせです。デコレータの組み合わせにより、装飾されたオブジェクトに透過的に機能を追加できます。
- デコレータモードは、機能を追加するだけでなく、新しい機能や制御機能へのアクセスを完全に実現します。デコレータでデコレーションされたオブジェクトの機能が呼び出されたときに制御できます。
- デコレータパターンのデメリット:きめの細かいオブジェクトが生成されます。一連の複雑な関数がさまざまな機能をさまざまなデコレータに細分化する場合、多くのきめの細かいオブジェクトが生成されます。