導入
Compositeは「個と全体を同じように扱う」——フォルダとファイルを再帰的に同一視するようなツリー構造に。Decoratorは「既存オブジェクトを包んで機能を継ぎ足す」——継承せずに振る舞いを積み重ねます。
図解
flowchart TB
subgraph Decorator
BASE["Coffee 300円"] --> M["+Milk 50円"] --> S["+Sugar 20円 = 370円"]
end
サンプル
// Composite: ファイルもフォルダも同じ INode として扱い、合計サイズを再帰計算
INode tree = new Folder("root", new INode[]
{
new FileNode("a.txt", 100),
new Folder("sub", new INode[] { new FileNode("b.txt", 200) }),
});
Console.WriteLine(tree.Size()); // 300
// Decorator: コーヒーを包んで機能(トッピング)を継ぎ足す
ICoffee coffee = new Sugar(new Milk(new PlainCoffee()));
Console.WriteLine($"{coffee.Description()}: {coffee.Cost()}円"); // コーヒー+ミルク+砂糖: 370円
interface INode { int Size(); }
class FileNode : INode
{
private readonly int _size;
public FileNode(string name, int size) => _size = size;
public int Size() => _size;
}
class Folder : INode
{
private readonly INode[] _children;
public Folder(string name, INode[] children) => _children = children;
public int Size() => _children.Sum(c => c.Size()); // 子を再帰集計
}
interface ICoffee { string Description(); int Cost(); }
class PlainCoffee : ICoffee { public string Description() => "コーヒー"; public int Cost() => 300; }
class Milk : ICoffee
{
private readonly ICoffee _inner;
public Milk(ICoffee inner) => _inner = inner;
public string Description() => _inner.Description() + "+ミルク";
public int Cost() => _inner.Cost() + 50;
}
class Sugar : ICoffee
{
private readonly ICoffee _inner;
public Sugar(ICoffee inner) => _inner = inner;
public string Description() => _inner.Description() + "+砂糖";
public int Cost() => _inner.Cost() + 20;
}
- Composite: 個(ファイル)と全体(フォルダ)を同じインターフェースで扱い、再帰処理する
- Decorator: 対象を包む同じインターフェースのクラスで、機能を積み重ねる
- どちらも「同じインターフェースで包む/まとめる」のが鍵
演習
IMessage m = new Encrypted(new Plain("hello"));
Console.WriteLine(m.Content()); // [暗号化]hello
// TODO: Plain を包んで内容の先頭に "[暗号化]" を付ける Decorator の Encrypted を定義してください
interface IMessage { string Content(); }
class Plain : IMessage
{
private readonly string _text;
public Plain(string text) => _text = text;
public string Content() => _text;
}
___
- 期待される出力:
[暗号化]hello
ヒント1を見る
class Encrypted : IMessage { private readonly IMessage _inner; public Encrypted(IMessage inner) => _inner = inner; public string Content() => "[暗号化]" + _inner.Content(); }
ヒント2を見る
内側のオブジェクトを保持し、その結果を加工して返します
まとめ
- Compositeは個と全体を同一視してツリーを扱う
- Decoratorは包んで機能を継ぎ足す(継承の代替)
- 共通インターフェースが両者の要
次回: 窓口・共有・代理のFacade/Flyweight/Proxyです。