访问者模式
电脑上有很多资源文件,它们的格式有三种: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
网友评论