组合模式(Composite Pattern)

0.写在前面的话

我创建的Unity、C#交流群,有兴趣可加入大家一起学习(现在人还很少,现在加入就是元老🙀):952914223

1.组合模式

1.1 定义

将对象组合成树形结构来表现”部分-整体“的层次结构,使得客户以一致的方式处理单个对象以及对象的组合。
在这里插入图片描述

1.2 使用场景

在软件开发过程中,我们经常会遇到处理简单对象和复合对象的情况,例如对操作系统中目录的处理就是这样的一个例子,因为目录可以包括单独的文件,也可以包括文件夹,文件夹又是由文件组成的,由于简单对象和复合对象在功能上区别,导致在操作过程中必须区分简单对象和复合对象,这样就会导致客户调用带来不必要的麻烦,然而作为客户,它们希望能够始终一致地对待简单对象和复合对象。然而组合模式就是解决这样的问题。下面让我们看看组合模式是怎样解决这个问题的。

2.组合模式结构

在这里插入图片描述

  • 组件(Component):接口描述了树中简单项目和复杂项目所共有的操作。
  • 叶节点(Leaf):是树的基本结构, 它不包含子项目。一般情况下, 叶节点最终会完成大部分的实际工作, 因为它们无法将工作指派给其他部分。
  • 容器(Container):——又名 “组合 (Composite)”——是包含叶节点或其他容器等子项目的单位。 容器不知道其子项目所属的具体类, 它只通过通用的组件接口与其子项目交互。容器接收到请求后会将工作分配给自己的子项目, 处理中间结果, 然后将最终结果返回给客户端。
  • 客户端(Client) :通过组件接口与所有项目交互。 因此, 客户端能以相同方式与树状结构中的简单或复杂项目交互。

3.UML类图

3.1 透明式的组合模式类图

在这里插入图片描述

3.2 安全式组合模式的类图

在这里插入图片描述

4.具体实现

4.1 透明式的组合模式

组件(Component):

/// <summary>
/// 图形抽象类
/// </summary>
public abstract class Graphics
{
    public string Name {get; set;}
    public Graphics(string name)
    {
        Name = name;
    }

    public abstract void Draw();
    public abstract void Add(Graphics graphics);
    public abstract void Remove(Graphics graphics);
}

叶节点(Leaf):

/// <summary>
/// 简单图形类——线
/// </summary>
public class Line : Graphics
{
    public Line(string name) : base(name)
    { }

    // 重写父类抽象方法
    public override void Draw()
    {
        Console.WriteLine("画  " + Name);
    }

    // 因为简单图形在添加或移除其他图形,所以简单图形Add或Remove方法没有任何意义
    // 如果客户端调用了简单图形的Add或Remove方法将会在运行时抛出异常
    // 我们可以在客户端捕获该类移除并处理
    public override void Add(Graphics graphics)
    {
        throw new Exception("不能向简单图形Line添加其他图形");
    }

    public override void Remove(Graphics graphics)
    {
        throw new Exception("不能向简单图形Line移除其他图形");
    }
}

/// <summary>
/// 简单图形类——圆
/// </summary>
public class Circle : Graphics
{
    public Circle(string name)
        : base(name)
    { }

    // 重写父类抽象方法
    public override void Draw()
    {
        Console.WriteLine("画  " + Name);
    }

    public override void Add(Graphics g)
    {
        throw new Exception("不能向简单图形Circle添加其他图形");
    }
    public override void Remove(Graphics g)
    {
        throw new Exception("不能向简单图形Circle移除其他图形");
    }
}

容器(Container):

/// <summary>
/// 复杂图形,由一些简单图形组成,这里假设该复杂图形由一个圆两条线组成的复杂图形
/// </summary>
public class ComplexGraphics : Graphics
{
    private List<Graphics> complexGraphicsList = new List<Graphics>();

    public ComplexGraphics(string name)
        : base(name)
    { }

    /// <summary>
    /// 复杂图形的画法
    /// </summary>
    public override void Draw()
    {          
        foreach (Graphics g in complexGraphicsList)
        {
            g.Draw();
        }
    }

    public override void Add(Graphics g)
    {
        complexGraphicsList.Add(g);
    }
    public override void Remove(Graphics g)
    {
        complexGraphicsList.Remove(g);
    }
}

客户端(Client) :

ComplexGraphics complexGraphics = new ComplexGraphics("一个复杂图形和两条线段组成的复杂图形");
complexGraphics.Add(new Line("线段A"));
 ComplexGraphics CompositeCG = new ComplexGraphics("一个圆和一条线组成的复杂图形");
 CompositeCG.Add(new Circle("圆"));
 CompositeCG.Add(new Circle("线段B"));
 complexGraphics.Add(CompositeCG);
 Line l = new Line("线段C");
 complexGraphics.Add(l);

 // 显示复杂图形的画法
 Console.WriteLine("复杂图形的绘制如下:");
 complexGraphics.Draw();
 Console.WriteLine("复杂图形绘制完成");
 Console.WriteLine();

 // 移除一个组件再显示复杂图形的画法
 complexGraphics.Remove(l);
 Console.WriteLine("移除线段C后,复杂图形的绘制如下:");
 complexGraphics.Draw();
 Console.WriteLine("复杂图形绘制完成");

复杂图形的绘制如下:
画 线段A
画 圆
画 线段B
画 线段C
复杂图形绘制完成
移除线段C后,复杂图形的绘制如下:
画 线段A
画 圆
画 线段B
复杂图形绘制完成

4.2 安全式组合模式

此方式实现的组合模式把管理子对象的方法声明在树枝构件ComplexGraphics类中

/// 此方式实现的组合模式把管理子对象的方法声明在树枝构件ComplexGraphics类中
/// 这样如果叶子节点Line、Circle使用了Add或Remove方法时,就能在编译期间出现错误
/// 但这种方式虽然解决了透明式组合模式的问题,但是它使得叶子节点和树枝构件具有不一样的接口。
/// 所以这两种方式实现的组合模式各有优缺点,具体使用哪个,可以根据问题的实际情况而定
class Client
{
    static void Main(string[] args)
    {
        ComplexGraphics complexGraphics = new ComplexGraphics("一个复杂图形和两条线段组成的复杂图形");
        complexGraphics.Add(new Line("线段A"));
        ComplexGraphics CompositeCG = new ComplexGraphics("一个圆和一条线组成的复杂图形");
        CompositeCG.Add(new Circle("圆"));
        CompositeCG.Add(new Circle("线段B"));
        complexGraphics.Add(CompositeCG);
        Line l = new Line("线段C");
        complexGraphics.Add(l);

        // 显示复杂图形的画法
        Console.WriteLine("复杂图形的绘制如下:");
        Console.WriteLine("---------------------");
        complexGraphics.Draw();
        Console.WriteLine("复杂图形绘制完成");
        Console.WriteLine("---------------------");
        Console.WriteLine();

        // 移除一个组件再显示复杂图形的画法
        complexGraphics.Remove(l);
        Console.WriteLine("移除线段C后,复杂图形的绘制如下:");
        Console.WriteLine("---------------------");
        complexGraphics.Draw();
        Console.WriteLine("复杂图形绘制完成");
        Console.WriteLine("---------------------");
        Console.Read();
    }
}

/// <summary>
/// 图形抽象类,
/// </summary>
public abstract class Graphics
{
    public string Name { get; set; }
    public Graphics(string name)
    {
        this.Name = name;
    }

    public abstract void Draw();
    // 移除了Add和Remove方法
    // 把管理子对象的方法放到了ComplexGraphics类中进行管理
    // 因为这些方法只在复杂图形中才有意义
}

/// <summary>
/// 简单图形类——线
/// </summary>
public class Line : Graphics
{
    public Line(string name)
        : base(name)
    { }

    // 重写父类抽象方法
    public override void Draw()
    {
        Console.WriteLine("画  " + Name);
    }
}

/// <summary>
/// 简单图形类——圆
/// </summary>
public class Circle : Graphics
{
    public Circle(string name)
        : base(name)
    { }

    // 重写父类抽象方法
    public override void Draw()
    {
        Console.WriteLine("画  " + Name);
    }
}

/// <summary>
/// 复杂图形,由一些简单图形组成,这里假设该复杂图形由一个圆两条线组成的复杂图形
/// </summary>
public class ComplexGraphics : Graphics
{
    private List<Graphics> complexGraphicsList = new List<Graphics>();
    public ComplexGraphics(string name)
        : base(name)
    { }

    /// <summary>
    /// 复杂图形的画法
    /// </summary>
    public override void Draw()
    {
        foreach (Graphics g in complexGraphicsList)
        {
            g.Draw();
        }
    }

    public void Add(Graphics g)
    {
        complexGraphicsList.Add(g);
    }
    public void Remove(Graphics g)
    {
        complexGraphicsList.Remove(g);
    }
}
### 设计模式中的奶茶小料组合 在面向对象编程中,通过应用装饰器模式可以灵活地给奶茶添加不同的小料而不改变其原有代码结构。基于此需求,下面展示了一个简化版的UML来表示如何利用装饰器模式实现奶茶及其附加的小料组件。 #### UML 描述 ```plaintext +-----+ ^ | +-------------------+ | ConcreteMilkTea |----> 实现基础奶茶逻辑 +-------------------+ | -process():void | +-------------------+ +-------------------+ | Decorator | -----> 抽象装饰者角色, 继承自MilkTea +-------------------+ | -milktea:MilkTea| | -process() | +-------------------+ ^ | +-------------------+ | CondimentDecorator| ---> 小料的具体实现,比如珍珠、椰果等 +-------------------+ | -specificProcess()| | -process():void | +-------------------+ ``` 在这个表里: - `MilkTea` 是一个抽象基,定义了所有具体奶茶以及它们可能拥有的任何装饰者的公共接口。 - `ConcreteMilkTea` 表示具体的奶茶产品,实现了由父声明的方法[^1]。 - `Decorator` 定义了一种动态增加职责的方式;它维持着对实际奶茶实例(`MilkTea`) 的引用,并提供与被修饰的对象相同的接口以便于透明地使用这些对象。 - `CondimentDecorator` 扩展了基本的装饰者行为,允许向特定型的奶茶上追加额外的功能特性——即各种各样的小料选项[^2]。 为了更直观理解上述关系,在Java语言环境下可参照如下代码片段模拟这一过程: ```java // 基础奶茶 abstract class MilkTea { public abstract void process(); } class OriginalMilkTea extends MilkTea{ @Override public void process(){ System.out.println("准备一杯原味奶茶"); } } // 装饰者基 abstract class CondimentDecorator extends MilkTea { protected MilkTea milkTea; public CondimentDecorator(MilkTea mt){ this.milkTea = mt; } // 子需重写此方法以加入各自特色加工流程 public abstract void specificProcess(); @Override public final void process(){ milkTea.process(); // 首先执行原始奶茶制备步骤 specificProcess(); // 接着进行特有处理(如添加配料) } } // 特定小料实现之一:珍珠 class PearlyBeads extends CondimentDecorator { public PearlyBeads(MilkTea mt){ super(mt); } @Override public void specificProcess(){ System.out.println("...再加入Q弹珍珠..."); } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值