设计模式 -- 组合模式

2019-08-05  本文已影响0人  皆为序幕_

场景

某公司准备开发一个杀毒软件,该软件既可以对某个文件夹(Folder)杀毒,也可以对某个指定的文件(File)进行杀毒。该杀毒软件还可以根据各类文件的特点,为不同类型的文件提供不同的杀毒方式,例如图像文件(ImageFile)和文本文件(TextFile)的杀毒方式就有所差异。现需要提供该杀毒软件的整体框架设计方案


示例图

如图所示,文件夹中可以包含文件,还可以继续包含子文件夹,但是在文件中不能再包含子文件或者子文件夹。在此,我们可以称文件夹为容器(Container),而不同类型的各种文件是其成员,也称为叶子(Leaf)。如果我们现在要对某一个文件夹进行操作,如查找文件,那么需要对指定的文件夹进行遍历,如果存在子文件夹则打开其子文件夹继续遍历,如果是文件则判断之后返回查找结果

class ImageFile {
    var name :String
    init(name:String) {
        self.name = name
    }
    func killVirus()  {
        print("图片文件:\(self.name)进行杀毒")
    }
}

class TextFile {
    var name :String
    init(name:String) {
        self.name = name
    }
    func killVirus()  {
        print("文本文件:\(self.name)进行杀毒")
    }
}

class Folder {
    var name : String
    var folderArr = [Folder]()
    var imageArr = [ImageFile]()
    var textArr = [TextFile]()
    
    init(name:String)  {
        self.name = name
    }
    
    
    //增加新的Folder类型的成员
    func addFolder(f:Folder)  {
        self.folderArr.append(f)
    }
    //增加新的ImageFile类型的成员
    func addImageFile(image:ImageFile) {
        self.imageArr.append(image)
    }
    //增加新的TextFile类型的成员
    func addTextFile(text:TextFile) {
        self.textArr.append(text)
    }
    
    func killVirus() {
        print("\(self.name)进行杀毒")
        
        for fodler in folderArr {
            fodler.killVirus()
        }
        for image in imageArr {
            image.killVirus()
        }
        for text in textArr {
            text.killVirus()
        }
    }
}

客户端调用

let folder = Folder.init(name: "文件夹")
let folderA = Folder.init(name: "图片文件夹")
let folderB = Folder.init(name: "文本文件夹")
folder.addFolder(f: folderA)
folder.addFolder(f: folderB)

let imageA = ImageFile.init(name: "图片A")
let imageB = ImageFile.init(name: "图片B")
folderA.addImageFile(image: imageA)
folderA.addImageFile(image: imageB)

let textA = TextFile.init(name: "文本A")
let textB = TextFile.init(name: "文本B")
folderB.addTextFile(text: textA)
folderB.addTextFile(text: textB)

folder.killVirus()

log:
// 文件夹进行杀毒
// 图片文件夹进行杀毒
// 图片文件:图片A进行杀毒
// 图片文件:图片B进行杀毒
// 文本文件夹进行杀毒
// 文本文件:文本A进行杀毒
// 文本文件:文本B进行杀毒

问题来了

问题改进
运用组合模式处理树形结构的问题,将容器和叶子进行递归组合,使得用户在使用时无须对它们进行区分,可以一致地处理容器和叶子

表述 (结构型模式)

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

组合模式的关键是定义了一个抽象构件类,它既可以代表叶子,又可以代表容器,而客户端针对该抽象构件类进行编程,无须知道它到底表示的是叶子还是容器,可以对其进行统一处理

组合模式类图

组合模式类图

优点

缺点

在增加新构件时很难对容器中的构件类型进行限制。有时候我们希望一个容器中只能有某些特定类型的对象,例如在某个文件夹中只能包含文本文件,使用组合模式时,不能依赖类型系统来施加这些约束,因为它们都来自于相同的抽象层,在这种情况下,必须通过在运行时进行类型检查来实现,这个实现过程较为复杂

使用场景

示例

需求V1:某公司准备开发一个杀毒软件,该软件既可以对某个文件夹(Folder)杀毒,也可以对某个指定的文件(File)进行杀毒。该杀毒软件还可以根据各类文件的特点,为不同类型的文件提供不同的杀毒方式,例如图像文件(ImageFile)和文本文件(TextFile)的杀毒方式就有所差异。现需要提供该杀毒软件的整体框架设计方案

//一般将抽象构件类设计为接口或抽象类,将所有子类共有方法的声明和实现放在抽象构件类中
class File {
    var name : String;
    init(name:String) {
        self.name = name
    }
    
    func add(f:File) {
        
    }
    func remove(f:File) {
        
    }
    func getChild(i:Int) -> File {
        return File.init(name: "")
    }
    func killVirus() {
        
    }
}

//图像文件类:叶子构件
class ImageFile : File {    
    override init(name: String) {
        super.name = name
    }
    
    override func add(f: File) {
        print("不支持该方法")
    }
    override func remove(f: File) {
        print("不支持该方法")
    }
    override func getChild(i: Int) -> File {
        print("不支持该方法")
        return File.init(name: "")
    }
    override func killVirus() {
        print("图片文件:\(self.name)进行杀毒")
    }
}

//文本文件类:叶子构件
class TextFile : File {
    override init(name: String) {
        super.name = name
    }
    
    override func add(f: File) {
        print("不支持该方法")
    }
    override func remove(f: File) {
        print("不支持该方法")
    }
    override func getChild(i: Int) -> File {
        print("不支持该方法")
        return File.init(name: "")
    }
    override func killVirus() {
        print("文本文件:\(self.name)进行杀毒")
    }
}

//文件夹类:容器构件
class Folder : File {
    var fileArray = [File]()
    override init(name: String) {
        super.init(name: name)
    }
    
    override func add(f: File) {
        fileArray.append(f)
    }
    override func remove(f: File) {
        for i in 0..<fileArray.count {
            if fileArray[i].name == f.name {
                fileArray.remove(at: i)
                break
            }
        }
    }
    override func getChild(i: Int) -> File {
        for i in 0..<fileArray.count {
            return fileArray[i]
        }
    }
    
    override func killVirus() {
        print("\(self.name)进行杀毒")
        for obj in fileArray {
            obj.killVirus()
        }
    }
}

客户端:

let folder = Folder.init(name: "文件夹")
let folderA = Folder.init(name: "图片文件夹")
let folderB = Folder.init(name: "文本文件夹")
folder.addFile(f: folderA)
folder.addFile(f: folderB)

let folder = Folder.init(name: "文件夹")
let imageFolder = Folder.init(name: "图片文件夹")
let textFolder = Folder.init(name: "文本文件夹")
folder.addFile(f: imageFolder)
folder.addFile(f: textFolder)

let imageFileA = ImageFile.init(name: "图片A")
let imageFileB = ImageFile.init(name: "图片B")
imageFolder.addFile(f: imageFileA)
imageFolder.addFile(f: imageFileB)

let textFileA = TextFile.init(name: "文本A")
let textFileB = TextFile.init(name: "文本B")
textFolder.addFile(f: textFileA)
textFolder.addFile(f: textFileB)

folder.killVirus()
log:
//文件夹进行杀毒
//图片文件夹进行杀毒
//图片文件:图片A进行杀毒
//图片文件:图片B进行杀毒
//文本文件夹进行杀毒
//文本文件:文本A进行杀毒
//文本文件:文本B进行杀毒

需求V2:在系统中增加一种新类型的视频文件VideoFile

只需要新建VideoFile继承自File即可

class VideoFile : File {
    override init(name: String) {
        super.init(name: name)
    }

    override func addFile(f: File) {
        print("不支持该方法")
    }
    override func removeFile(f: File) {
        print("不支持该方法")
    }
    override func getChild(i: Int) -> File {
        print("不支持该方法")
        return File.init(name: "")
    }
    override func killVirus() {
        print("视频文件:\(self.name)进行杀毒")
    }
}

客户端

let videoFolder = Folder.init(name: "视频文件夹")
folder.addFile(f: videoFolder)

let videoFileA = VideoFile.init(name: "视频A")
let videoFileB = VideoFile.init(name: "视频B")
videoFolder.addFile(f: videoFileA)
videoFolder.addFile(f: videoFileB)

folder.killVirus()

log
//视频文件夹进行杀毒
//视频文件:视频A进行杀毒
//视频文件:视频B进行杀毒
上一篇 下一篇

猜你喜欢

热点阅读