私はデザインパターンに関する本を何冊か読んでいますが、抽象化と実装の関係を構成として説明するものもあれば、それを集約として説明するものもあります。今、私は疑問に思います:これは実装に依存していますか? 言語で?それとも文脈?
3 に答える
「組成物」と「凝集体」という用語は、多かれ少なかれ同じことを意味し、交換可能に使用される場合があります。要素がすべて同じ型であるリスト、動的配列、マップ、およびキューなどのコンテナー クラスを記述する場合、集約がより頻繁に使用されることがあります。ただし、両方の用語は、それらのタイプが同種 (すべて同じタイプ) であるか異種 (異なるタイプのオブジェクト) であるかに関係なく、他のクラスの用語で定義されたクラスを記述するために使用される場合があります。
これを明確にするために:
class Car {
// ...
private:
Engine engine;
Hood hood;
};
// The car is *composed* of an engine and a hood. Hence, composition. You are
// also bringing together (i.e. *aggregating*) an engine and hood into a car.
抽象化と実装の間の関係は、通常、構成/集約ではなく、継承を意味します。通常、抽象化はインターフェイスまたは仮想基本クラスであり、実装は特定のインターフェイスを実装する完全に具体的なクラスです。ただし、混乱を招くように、構成/集約はインターフェイスの一部である可能性があり (たとえば、構成要素として使用されるオブジェクトを設定/取得する必要がある場合があるため)、それらは実装へのアプローチでもあります (委任を使用して、実装でメソッドの定義を提供する場合があります)。
これを明確にするために:
interface Car {
public Engine getEngine();
public Hood getHood();
public void drive();
}
// In the above, the fact that a car has these building blocks
// is a part of its interface (the abstraction).
class HondaCivic2010 implements Car {
public void drive(){ getEngine().drive(); }
// ...
}
// In the above, composition/delegation is an implementation
// strategy for providing the drive functionality.
質問に「ブリッジ」というタグを付けたので、ブリッジ パターンの定義は、継承ではなく構成を使用して、複数の異なるレベルでのバリエーションを可能にするパターンであることを指摘しておく必要があります。私が大学で学んだ例...継承を使用すると、次のようなものになる可能性があります。
class GoodCharacter;
class BadCharacter;
class Mage;
class Rogue;
class GoodMage : public GoodCharacter, Mage;
class BadMage : public BadCharacter, Mage;
class GoodRogue : public GoodCharacter, Rogue;
class BadRogue : public BadCharacter, Rogue;
ご覧のとおり、この種のことは非常にクレイジーで、途方もない数のクラスが得られます。同じことをブリッジ パターンで行うと、次のようになります。
class Personality;
class GoodPersonality : public Personality;
class BadPersonality : public Personality;
class CharacterClass;
class Mage : public CharacterClass;
class Rogue : public CharacterClass;
class Character {
public:
// ...
private:
CharacterClass character_class;
Personality personality;
};
// A character has both a character class and a personality.
// This is a perfect example of the bridge pattern, and we've
// reduced MxN classes into a mere M+N classes, and we've
// arguably made the system even more flexible than before.
ブリッジ パターンは委任を使用する必要があります (集約/構成であり、継承ではありません)。ギャング・オブ・フォーの本から:
ブリッジ パターンを使用する場合
* you want to avoid a permanent binding between an abstraction and its implementation. This might be the case, for example, when the implementation must be selected or switched at run-time.
* both the abstractions and their implementations should be extensible by subclassing. In this case, the Bridge pattern lets you combine the different abstractions and implementations and extend them independently.
* changes in the implementation of an abstraction should have no impact on clients; that is, their code should not have to be recompiled.
* (C++) you want to hide the implementation of an abstraction completely from clients. In C++ the representation of a class is visible in the class interface.
* you have a proliferation of classes as shown earlier in the first Motivation diagram. Such a class hierarchy indicates the need for splitting an object into two parts. Rumbaugh uses the term "nested generalizations" [RBP+91] to refer to such class hierarchies.
* you want to share an implementation among multiple objects (perhaps using reference counting), and this fact should be hidden from the client. A simple example is Coplien's String class [Cop92], in which multiple objects can share the same string representation (StringRep).
Bridge パターンの標準 UML は、混乱の周りのすべての空気を一掃します。以下は、この周りの空気をきれいにするための簡単な例での説明です。
この長いコードについてお詫び申し上げます。コードを簡単に理解できるように、このコードを Visual Studio にコピーすることをお勧めします。
コードの最後に書かれた説明を読む
interface ISpeak
{
void Speak();
}
class DogSpeak : ISpeak
{
public void Speak()
{
Console.WriteLine("Dog Barks");
}
}
class CatSpeak : ISpeak
{
public void Speak()
{
Console.WriteLine("Cat Meows");
}
}
abstract class AnimalBridge
{
protected ISpeak Speech;
protected AnimalBridge(ISpeak speech)
{
this.Speech = speech;
}
public abstract void Speak();
}
class Dog : AnimalBridge
{
public Dog(ISpeak dogSpeak)
: base(dogSpeak)
{
}
public override void Speak()
{
Speech.Speak();
}
}
class Cat : AnimalBridge
{
public Cat(ISpeak catSpeak)
: base(catSpeak)
{
}
public override void Speak()
{
Speech.Speak();
}
}
-- ISpeak は、ボットの Dog と Cat が実装しなければならない抽象化です -- ISpeak で構成されるブリッジ「アニマル」を導入することにより、Dog クラスと Cat クラスを分離しました -- Dog クラスと Cat クラスは Animal クラスを拡張し、ISpeak から分離されます。
これが明確になることを願っています