某公司准备开发一个杀毒软件,该软件既可以对某个文件夹(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进行杀毒