美文网首页
iOS设计模式之访问者模式

iOS设计模式之访问者模式

作者: 点滴86 | 来源:发表于2024-07-01 23:34 被阅读0次

    访问者模式

    电脑上有很多资源文件,它们的格式有三种:PDF、PPT、Word。现在要开发一个工具来处理这批资源文件。这个工具的其中一个功能是,把这些资源文件中的文本内容抽取出来放到txt文件中。

    @protocol DMResourceFileDelegate <NSObject>
    
    - (void)extractTxt;
    
    @end
    
    @interface DMPDFFile : NSObject <DMResourceFileDelegate>
    
    @end
    
    @implementation DMPDFFile
    
    - (void)extractTxt
    {
        NSLog(@"%@:%@", NSStringFromClass([self class]), NSStringFromSelector(_cmd));
    }
    
    @end
    
    @interface DMPPTFile : NSObject <DMResourceFileDelegate>
    
    @end
    
    @implementation DMPPTFile
    
    - (void)extractTxt
    {
        NSLog(@"%@:%@", NSStringFromClass([self class]), NSStringFromSelector(_cmd));
    }
    
    @end
    
    @interface DMWordFile : NSObject <DMResourceFileDelegate>
    
    @end
    
    @implementation DMWordFile
    
    - (void)extractTxt
    {
        NSLog(@"%@:%@", NSStringFromClass([self class]), NSStringFromSelector(_cmd));
    }
    
    @end
    
    @interface DMToolApplication : NSObject
    
    @end
    
    @implementation DMToolApplication
    
    - (void)test
    {
        NSMutableArray<DMResourceFileDelegate> *tmpArray = [[NSMutableArray<DMResourceFileDelegate> alloc] init];
        DMPDFFile *pdfFile = [[DMPDFFile alloc] init];
        [tmpArray addObject:pdfFile];
        
        DMPPTFile *pptFile = [[DMPPTFile alloc] init];
        [tmpArray addObject:pptFile];
        
        DMWordFile *wordFile = [[DMWordFile alloc] init];
        [tmpArray addObject:wordFile];
        
        for (id<DMResourceFileDelegate> resourceFile in tmpArray) {
            [resourceFile extractTxt];
        }
    }
    
    @end
    

    如果工具的功能不停地扩展,不仅要能抽取文本内容,还要支持压缩、提取元文件信息(文件名、大小、更新时间等等)构建索引等一系列的功能,继续按照上面代码的实现思路,就会存在几个问题:

    • 违背开闭原则,添加一个新的功能,所有类的代码都要修改
    • 虽然功能增多,每个类的代码都不断膨胀,可读性和可维护性都变差了
    • 把所有比较上层的业务逻辑都耦合到DMPDFFile、DMPPTFile、DMWordFile类中,导致这些类的职责不够单一,变成了大杂烩

    针对上面的问题,常用的解决方法就是拆分解耦,把业务操作跟具体的数据结构解耦,设计成独立的类。重构之后的代码如下:

    @interface DMPDFFile : NSObject
    
    @end
    
    @implementation DMPDFFile
    
    @end
    
    @interface DMPPTFile : NSObject
    
    @end
    
    @implementation DMPPTFile
    
    @end
    
    @interface DMWordFile : NSObject
    
    @end
    
    @implementation DMWordFile
    
    @end
    
    @interface DMExtractor : NSObject
    
    - (void)extractPDF:(DMPDFFile *)pdfFile;
    
    - (void)extractPPT:(DMPPTFile *)pptFile;
    
    - (void)extractWord:(DMWordFile *)wordFile;
    
    @end
    
    @implementation DMExtractor
    
    - (void)extractPDF:(DMPDFFile *)pdfFile
    {
        
    }
    
    - (void)extractPPT:(DMPPTFile *)pptFile
    {
        
    }
    
    - (void)extractWord:(DMWordFile *)wordFile
    {
        
    }
    
    @end
    
    @interface DMToolApplication : NSObject
    
    @end
    
    @implementation DMToolApplication
    
    - (void)test
    {
        DMExtractor *extractor = [[DMExtractor alloc] init];
        
        NSMutableArray *tmpArray = [[NSMutableArray alloc] init];
        DMPDFFile *pdfFile = [[DMPDFFile alloc] init];
        [tmpArray addObject:pdfFile];
        
        DMPPTFile *pptFile = [[DMPPTFile alloc] init];
        [tmpArray addObject:pptFile];
        
        DMWordFile *wordFile = [[DMWordFile alloc] init];
        [tmpArray addObject:wordFile];
        
        for (id resourceFile in tmpArray) {
            if ([resourceFile isKindOfClass:[DMPDFFile class]]) {
                DMPDFFile *pdfFile = (DMPDFFile *)resourceFile;
                [extractor extractPDF:pdfFile];
            } else if ([resourceFile isKindOfClass:[DMPPTFile class]]) {
                DMPPTFile *pptFile = (DMPPTFile *)resourceFile;
                [extractor extractPPT:pptFile];
            } else if ([resourceFile isKindOfClass:[DMWordFile class]]) {
                DMWordFile *wordFile = (DMWordFile *)resourceFile;
                [extractor extractWord:wordFile];
            }
        }
    }
    
    @end
    

    遍历tmpArray获取resourceFile,需要手动转换类型,调用对应的方法,继续优化这部分

    @class DMExtractor;
    
    @protocol DMResourceFileDelegate <NSObject>
    
    - (void)accept:(DMExtractor *)extractor;
    
    @end
    
    @interface DMPDFFile : NSObject <DMResourceFileDelegate>
    
    @end
    
    @implementation DMPDFFile
    
    - (void)accept:(DMExtractor *)extractor
    {
        [extractor extractPDF:self];
    }
    
    @end
    
    @interface DMPPTFile : NSObject <DMResourceFileDelegate>
    
    @end
    
    @implementation DMPPTFile
    
    - (void)accept:(DMExtractor *)extractor
    {
        [extractor extractPPT:self];
    }
    
    @end
    
    @interface DMWordFile : NSObject <DMResourceFileDelegate>
    
    @end
    
    @implementation DMWordFile
    
    - (void)accept:(DMExtractor *)extractor
    {
        [extractor extractWord:self];
    }
    
    @end
    
    @interface DMExtractor : NSObject
    
    - (void)extractPDF:(DMPDFFile *)pdfFile;
    
    - (void)extractPPT:(DMPPTFile *)pptFile;
    
    - (void)extractWord:(DMWordFile *)wordFile;
    
    @end
    
    @implementation DMExtractor
    
    - (void)extractPDF:(DMPDFFile *)pdfFile
    {
        
    }
    
    - (void)extractPPT:(DMPPTFile *)pptFile
    {
        
    }
    
    - (void)extractWord:(DMWordFile *)wordFile
    {
        
    }
    
    @end
    
    @interface DMToolApplication : NSObject
    
    @end
    
    @implementation DMToolApplication
    
    - (void)test
    {
        DMExtractor *extractor = [[DMExtractor alloc] init];
        
        NSMutableArray<DMResourceFileDelegate> *tmpArray = [[NSMutableArray<DMResourceFileDelegate> alloc] init];
        DMPDFFile *pdfFile = [[DMPDFFile alloc] init];
        [tmpArray addObject:pdfFile];
        
        DMPPTFile *pptFile = [[DMPPTFile alloc] init];
        [tmpArray addObject:pptFile];
        
        DMWordFile *wordFile = [[DMWordFile alloc] init];
        [tmpArray addObject:wordFile];
        
        for (id<DMResourceFileDelegate> resourceFile in tmpArray) {
            [resourceFile accept: extractor];
        }
    }
    
    @end
    

    现在,如果要继续添加新的功能,比如前面提到的压缩功能,根据不同的文件类型,使用不同的压缩算法来压缩资源文件。代码实现如下:

    @class DMExtractor;
    @class DMCompressor;
    
    @protocol DMResourceFileDelegate <NSObject>
    
    - (void)accept:(DMExtractor *)extractor;
    
    - (void)acceptCompressor:(DMCompressor *)compressor;
    
    @end
    
    @interface DMPDFFile : NSObject <DMResourceFileDelegate>
    
    @end
    
    @implementation DMPDFFile
    
    - (void)accept:(DMExtractor *)extractor
    {
        [extractor extractPDF:self];
    }
    
    - (void)acceptCompressor:(DMCompressor *)compressor
    {
        [compressor compressPDF:self];
    }
    
    @end
    
    @interface DMPPTFile : NSObject <DMResourceFileDelegate>
    
    @end
    
    @implementation DMPPTFile
    
    - (void)accept:(DMExtractor *)extractor
    {
        [extractor extractPPT:self];
    }
    
    - (void)acceptCompressor:(DMCompressor *)compressor
    {
        [compressor compressPPT:self];
    }
    
    @end
    
    @interface DMWordFile : NSObject <DMResourceFileDelegate>
    
    @end
    
    @implementation DMWordFile
    
    - (void)accept:(DMExtractor *)extractor
    {
        [extractor extractWord:self];
    }
    
    - (void)acceptCompressor:(DMCompressor *)compressor
    {
        [compressor compressWord:self];
    }
    
    @end
    
    @interface DMExtractor : NSObject
    
    - (void)extractPDF:(DMPDFFile *)pdfFile;
    
    - (void)extractPPT:(DMPPTFile *)pptFile;
    
    - (void)extractWord:(DMWordFile *)wordFile;
    
    @end
    
    @implementation DMExtractor
    
    - (void)extractPDF:(DMPDFFile *)pdfFile
    {
        
    }
    
    - (void)extractPPT:(DMPPTFile *)pptFile
    {
        
    }
    
    - (void)extractWord:(DMWordFile *)wordFile
    {
        
    }
    
    @end
    
    @interface DMCompressor : NSObject
    
    - (void)compressPDF:(DMPDFFile *)pdfFile;
    
    - (void)compressPPT:(DMPPTFile *)pptFile;
    
    - (void)compressWord:(DMWordFile *)wordFile;
    
    @end
    
    @implementation DMCompressor
    
    - (void)compressPDF:(DMPDFFile *)pdfFile
    {
        NSLog(@"%@:%@", NSStringFromClass([self class]), NSStringFromSelector(_cmd));
    }
    
    - (void)compressPPT:(DMPPTFile *)pptFile
    {
        NSLog(@"%@:%@", NSStringFromClass([self class]), NSStringFromSelector(_cmd));
    }
    
    - (void)compressWord:(DMWordFile *)wordFile
    {
        NSLog(@"%@:%@", NSStringFromClass([self class]), NSStringFromSelector(_cmd));
    }
    
    @end
    
    @interface DMToolApplication : NSObject
    
    @end
    
    @implementation DMToolApplication
    
    - (void)test
    {
        NSLog(@"重构三");
        DMExtractor *extractor = [[DMExtractor alloc] init];
        DMCompressor *compressor = [[DMCompressor alloc] init];
        
        NSMutableArray<DMResourceFileDelegate> *tmpArray = [[NSMutableArray<DMResourceFileDelegate> alloc] init];
        DMPDFFile *pdfFile = [[DMPDFFile alloc] init];
        [tmpArray addObject:pdfFile];
        
        DMPPTFile *pptFile = [[DMPPTFile alloc] init];
        [tmpArray addObject:pptFile];
        
        DMWordFile *wordFile = [[DMWordFile alloc] init];
        [tmpArray addObject:wordFile];
        
        for (id<DMResourceFileDelegate> resourceFile in tmpArray) {
            [resourceFile accept:extractor];
        }
        
        for (id<DMResourceFileDelegate> resourceFile in tmpArray) {
            [resourceFile acceptCompressor:compressor];
        }
    }
    @end
    

    上面的代码还存在一些问题,新添加一个新的业务,还是需要修改每个资源文件,违反了开闭原则。针对这个问题,做如下修改,
    协议DMVisitor,三个方法- (void)visitPDF:(DMPDFFile *)pdfFile、- (void)visitPPT:(DMPPTFile *)pptFile、- (void)visitWord:(DMWordFile *)wordFile 分别处理三种不同类型的资源文件。具体做什么业务,由遵守这个协议的具体类来决定,比如DMExtractor负责抽取文本内容、DMCompressor负责压缩。当添加一个新的业务功能的时候,资源文件类不需要做任务修改,只需要新的业务功能遵守DMVisitor协议,实现对应的功能,就可以了。然后再修改下DMToolApplication的使用代码,如下:

    
    @class DMPDFFile;
    @class DMPPTFile;
    @class DMWordFile;
    
    @protocol DMVisitor <NSObject>
    
    - (void)visitPDF:(DMPDFFile *)pdfFile;
    
    - (void)visitPPT:(DMPPTFile *)pptFile;
    
    - (void)visitWord:(DMWordFile *)wordFile;
    
    @end
    
    @interface DMExtractor : NSObject <DMVisitor>
    
    @end
    
    @implementation DMExtractor
    
    - (void)visitPDF:(DMPDFFile *)pdfFile
    {
        NSLog(@"%@:%@", NSStringFromClass([self class]), NSStringFromSelector(_cmd));
    }
    
    - (void)visitPPT:(DMPPTFile *)pptFile
    {
        NSLog(@"%@:%@", NSStringFromClass([self class]), NSStringFromSelector(_cmd));
    }
    
    - (void)visitWord:(DMWordFile *)wordFile
    {
        NSLog(@"%@:%@", NSStringFromClass([self class]), NSStringFromSelector(_cmd));
    }
    
    @end
    
    @interface DMCompressor : NSObject <DMVisitor>
    
    @end
    
    @implementation DMCompressor
    
    - (void)visitPDF:(DMPDFFile *)pdfFile
    {
        NSLog(@"%@:%@", NSStringFromClass([self class]), NSStringFromSelector(_cmd));
    }
    
    - (void)visitPPT:(DMPPTFile *)pptFile
    {
        NSLog(@"%@:%@", NSStringFromClass([self class]), NSStringFromSelector(_cmd));
    }
    
    - (void)visitWord:(DMWordFile *)wordFile
    {
        NSLog(@"%@:%@", NSStringFromClass([self class]), NSStringFromSelector(_cmd));
    }
    
    @end
    
    @protocol DMResourceFileDelegate <NSObject>
    
    - (void)acceptVisitor:(id<DMVisitor>)visitor;
    
    @end
    
    @interface DMPDFFile : NSObject <DMResourceFileDelegate>
    
    @end
    
    @implementation DMPDFFile
    
    - (void)acceptVisitor:(id<DMVisitor>)visitor
    {
        [visitor visitPDF:self];
    }
    
    @end
    
    @interface DMPPTFile : NSObject <DMResourceFileDelegate>
    
    @end
    
    @implementation DMPPTFile
    
    - (void)acceptVisitor:(id<DMVisitor>)visitor
    {
        [visitor visitPPT:self];
    }
    
    @end
    
    @interface DMWordFile : NSObject <DMResourceFileDelegate>
    
    @end
    
    @implementation DMWordFile
    
    - (void)acceptVisitor:(id<DMVisitor>)visitor
    {
        [visitor visitWord:self];
    }
    
    @end
    
    @interface DMToolApplication : NSObject
    
    @end
    
    @implementation DMToolApplication
    
    - (void)test
    {
        DMExtractor *extractor = [[DMExtractor alloc] init];
        DMCompressor *compressor = [[DMCompressor alloc] init];
        
        NSMutableArray<DMResourceFileDelegate> *tmpArray = [[NSMutableArray<DMResourceFileDelegate> alloc] init];
        DMPDFFile *pdfFile = [[DMPDFFile alloc] init];
        [tmpArray addObject:pdfFile];
        
        DMPPTFile *pptFile = [[DMPPTFile alloc] init];
        [tmpArray addObject:pptFile];
        
        DMWordFile *wordFile = [[DMWordFile alloc] init];
        [tmpArray addObject:wordFile];
        
        for (id<DMResourceFileDelegate> resourceFile in tmpArray) {
            [resourceFile acceptVisitor:extractor];
        }
        
        for (id<DMResourceFileDelegate> resourceFile in tmpArray) {
            [resourceFile acceptVisitor:compressor];
        }
    }
    
    @end
    

    相关文章

      网友评论

          本文标题:iOS设计模式之访问者模式

          本文链接:https://www.haomeiwen.com/subject/nptvcjtx.html