且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

【愚公系列】2021年12月 二十三种设计模式(八)-组合模式(Composite Pattern)

更新时间:2022-05-28 16:50:41

文章目录

前言

一、组合模式(Composite Pattern)

二、使用步骤

角色

示例

总结

优点

缺点

使用场景

前言

设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。 毫无疑问,设计模式于己于他人于系统都是多赢的,设计模式使代码编制真正工程化,设计模式是软件工程的基石,如同大厦的一块块砖石一样。项目中合理的运用设计模式可以完美的解决很多问题,每种模式在现在中都有相应的原理来与之对应,每一个模式描述了一个在我们周围不断重复发生的问题,以及该问题的核心解决方案,这也是它能被广泛应用的原因。


提示:以下是本篇文章正文内容,下面案例可供参考


一、组合模式(Composite Pattern)

组合模式属于结构型模式,将对象组合成树形结构以表示“部分-整体”的层次结构,组合模式使得用户对单个对象和组合对象的使用具有一致性。


组合模式有时候又叫做部分-整体模式,它使我们树型结构的问题中,模糊了简单元素和复杂元素的概念,客户程序可以像处理简单元素一样来处理复杂元素,从而使得客户程序与复杂元素的内部结构解耦。


二、使用步骤

角色

1、抽象构件(Component )


它是所有叶子构件和容器构件的共同父类,里面声明了叶子构件和容器构件的所有方法;


2、叶子构件(Leaf)


在组合中表示叶子结点对象,叶子结点没有子结点,对于从父类中继承过来的容器构件的方法,由于它不能实现,可以抛出异常;


3、容器构件(Composite)


定义有枝节点行为,用来存储子部件,在Component接口中实现与子部件有关操作,如增加(Attach)和删除(Detach)等。


示例

【愚公系列】2021年12月 二十三种设计模式(八)-组合模式(Composite Pattern)

命名空间CompositePattern包含文件系统FileSystem基类充当抽象构件类,文件File类充当叶子构件,文件夹Folder类充当容器构件,文件无效操作FileInvalidException 类进行异常处理。本案例尝试通过一个简单的文件系统来向大家阐述组合模式在调用方使用一致性方面的表现。

public abstract class FileSystem {

    protected string _name = null;

    protected const char SPLIT_CHAR_FILE = ' ';

    protected const char SPLIT_CHAR_DIR = '▼';

    public FileSystem(string name) {
        this._name = name;
    }

    public abstract FileSystem Attach(FileSystem component);

    public abstract FileSystem Detach(FileSystem component);

    public abstract void Print(int depth = 0);

}

抽象构件,文件系统FileSystem类。

public class File : FileSystem {

    public File(string name) : base(name) {

    }

    public override FileSystem Attach(FileSystem component) {
        throw new FileInvalidException(
            "You can not attach a component in a file!");
    }

    public override FileSystem Detach(FileSystem component) {
        throw new FileInvalidException(
            "You can not detach a component from a file!");
    }

    public override void Print(int depth = 0) {
        Console.WriteLine(new string(SPLIT_CHAR_FILE, depth) + _name);
    }

}

叶子构件,文件File类。

public class Folder : FileSystem {

    private List<FileSystem> _childrens = null;

    public Folder(string name) : base(name) {
        _childrens = new List<FileSystem>();
    }

    public override FileSystem Attach(FileSystem component) {
        _childrens.Add(component);
        return this;
    }

    public override FileSystem Detach(FileSystem component) {
        _childrens.Remove(component);
        return this;
    }

    public override void Print(int depth = 0) {
        Console.WriteLine(new string(SPLIT_CHAR_DIR, depth) + _name);
        foreach (var component in _childrens) {
            component.Print(depth + 1);
        }
    }

}

容器构件,文件夹Folder类。

public class FileInvalidException : Exception {

    public FileInvalidException(string message) : base(message) {

    }

    public FileInvalidException(string message, Exception innerException)
        : base(message, innerException) {

    }

}

无效的文件操作FileInvalidException类进行简单的异常处理。

public class Program {

    public static void Main(string[] args) {
        try {
            var root = new Folder("Root");

            var music = new Folder("My Music");
            music.Attach(new File("Heal the world.mp3"))
                    .Attach(new File("When You Say Nothing At All.mp3"))
                    .Attach(new File("Long Long Way to Go.mp3"))
                    .Attach(new File("Beautiful In White.mp3"));

            var video = new Folder("My Video");
            video.Attach(new File("The Shawshank Redemption.avi"))
                    .Attach(new File("Schindler's List.avi"))
                    .Attach(new File("Brave Heart.avi"));

            var png = new File("missing.png");
            root.Attach(png)
                .Detach(png)
                .Detach(png);

            root.Attach(new File("readme.txt"))
                .Attach(new File("index.html"))
                .Attach(new File("file.cs"))
                .Attach(music)
                .Attach(video)
                .Print();

            png.Attach(new File("error.json"));

        }
        catch (Exception ex) {
            Console.WriteLine(ex.Message);
        }

        Console.ReadKey();
    }

}

以上是调用方的代码,以下是这个案例的输出结果:

Root
 readme.txt
 index.html
 file.cs
▼My Music
  Heal the world.mp3
  When You Say Nothing At All.mp3
  Long Long Way to Go.mp3
  Beautiful In White.mp3
▼My Video
  The Shawshank Redemption.avi
  Schindler's List.avi
  Brave Heart.avi
You can not attach a component in a file!

总结

优点

1、可以清楚地定义分层次的复杂对象,表示对象的全部或部分层次,使得增加新构件也更容易;

2、客户端调用简单,客户端可以一致的使用组合结构或其中单个对象;

3、定义了包含叶子对象和容器对象的类层次结构,叶子对象可以被组合成更复杂的容器对象,而这个容器对象又可以被组合,这样不断递归下去,可以形成复杂的树形结构;

4、更容易在组合体内加入对象构件,客户端不必因为加入了新的对象构件而更改原有代码。


缺点

1、使设计变得更加抽象,对象的业务规则如果很复杂,则实现组合模式具有很大挑战性,而且不是所有的方法都与叶子对象子类都有关联;

2、增加新构件时可能会产生一些问题,很难对容器中的构件类型进行限制。


使用场景

1、你想表示对象的部分-整体层次结构;

2、你希望用户忽略组合对象与单个对象的不同,用户将统一地使用组合结构中的所有对象;

3、对象的结构是动态的并且复杂程度不一样,但客户需要一致地处理它们。