- 模板方法模式
模板方法模式:定义一个操作中的算法骨架,而将一些步骤延迟到子类中。模板方法使得子类可以在不改变一个算法的结构即可重定义该算法的某些特定步骤。
比如说,小时候数学老师的随堂检测,都是在黑板上抄题目,要我们先抄题目,然后再做答案,有时候没看清就会把题目抄错,这就意味着,即使做得再好,也不会正确了。
题目抄错了,那就不是考试题目了,而考试试卷最大的好处就是大家题目都一样,比如说选择题或者判断题,大家都是ABCD或者打钩打叉,非对即错的结果。这其实可以引申出来一个设计模式。
抄题目的代码,版本一:
#import <Foundation/Foundation.h> @interface ZYTestPaperA : NSObject @property (nonatomic, copy) NSString *name; - (void)testQuestion1; - (void)testQuestion2; @end #import "ZYTestPaperA.h" @implementation ZYTestPaperA - (void)testQuestion1 { NSLog(@"%@: “大煮干丝”是哪个菜系的代表菜之一( )。 A四川菜系 B山东菜系 C广东菜系 D淮扬菜系",self.name); } - (void)testQuestion2 { NSLog(@"%@: 红茶属于( )茶。 A半发酵 B发酵 C不发酵 D微发酵",self.name); } @end
#import <Foundation/Foundation.h> @interface ZYTestPaperB : NSObject @property (nonatomic, copy) NSString *name; - (void)testQuestion1; - (void)testQuestion2; @end #import "ZYTestPaperB.h" @implementation ZYTestPaperB - (void)testQuestion1 { NSLog(@"%@: “大煮干丝”是哪个菜系的代表菜之一( )。 A四川菜系 B山东菜系 C广东菜系 D淮扬菜系",self.name); } - (void)testQuestion2 { NSLog(@"%@: 红茶属于( )茶。 A半发酵 B发酵 C不发酵 D微发酵",self.name); } @end
viewController里面的代码:
#import "ViewController.h" #import "ZYTestPaperA.h" #import "ZYTestPaperB.h" @interface ViewController () @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; ZYTestPaperA *paperA = [[ZYTestPaperA alloc] init]; paperA.name = @"Joke"; [paperA testQuestion1]; [paperA testQuestion2]; ZYTestPaperB *paperB = [[ZYTestPaperB alloc] init]; paperB.name = @"Jom"; [paperB testQuestion1]; [paperB testQuestion2]; } @end
上面的代码,很容易就可以发现问题,两张试卷,除了答案不同,其他几乎都相似,像上面那样写,既麻烦、又容易出错,也不易维护。
如果老师突然要改题目,那么两个人就都需要改代码,如果某人抄写错误,那就是很糟糕的一种体验了。
老师出一份题目,打印多份,让学生填写答案就可以了,在这里应该就是试题和答案分享,抽出一个父类,让两个子类继承它,公共的试题代码写到父类中就可以了。
如此,有下面的代码:
#import <Foundation/Foundation.h> @interface ZYTestPaper : NSObject @property (nonatomic, copy) NSString *name; - (void)testQuestion1; - (void)testQuestion2; @end #import "ZYTestPaper.h" @implementation ZYTestPaper - (void)testQuestion1 { NSLog(@"%@: “大煮干丝”是哪个菜系的代表菜之一( )。 A四川菜系 B山东菜系 C广东菜系 D淮扬菜系",self.name); } - (void)testQuestion2 { NSLog(@"%@: 红茶属于( )茶。 A半发酵 B发酵 C不发酵 D微发酵",self.name); } @end
#import <Foundation/Foundation.h> #import "ZYTestPaper.h" @interface ZYTestPaperA : ZYTestPaper @end #import "ZYTestPaperA.h" @implementation ZYTestPaperA - (void)testQuestion1 { [super testQuestion1]; NSLog(@"答案是B"); } - (void)testQuestion2 { [super testQuestion2]; NSLog(@"答案是C"); } @end
#import <Foundation/Foundation.h> #import "ZYTestPaper.h" @interface ZYTestPaperB : ZYTestPaper @end #import "ZYTestPaperB.h" @implementation ZYTestPaperB - (void)testQuestion1 { [super testQuestion1]; NSLog(@"答案是B"); } - (void)testQuestion2 { [super testQuestion2]; NSLog(@"答案是C"); } @end
我们既然用了类的继承,并且肯定这个继承有意义,就应该要成为子类的模板,所有重复的代码都应该要上升到父类里面去,而不是让每个子类都去重复。
当我们要完成在某一细节层次一致的某一个过程或者一系列的步骤,但其个别步骤在更详细的实现上不同时,我们通常考虑用模板方法模式来处理。
简单的说,模板方法模式,是通过将不变的行为移动到父类中去,然后去除子类中的重复代码来体现它的优势,它提供了一个很好的代码复用平台。
当不变的和可变的行为在方法的子类实现中混合到一起的时候,不变的行为就会在子类中重复出现。我们通过模板方法模式将这些行为搬移到单一的地方,这样就帮助子类摆脱了重复的不变行为的纠缠。
2. 迪米特法则
迪米特法则,也叫最少知识原则:如果两个类不必彼此直接通信,那么这两个类就不应当发生直接的相互作用,如果其中一个类需要调用另一个类的某一个方法的话,可以通过第三者转发这个调用。
迪米特法则首先强调的前提是,在类的结构设计上,每一个类都应当尽量降低成员的访问权限。也就是说,一个类包装好自己的private状态,不需要让别的类知道的字段或者行为就不要公开,需要公开的字段,就用属性来体现。
迪米特法则根本思想,是强调了类的松耦合。类之间的耦合越弱,越有利于复用,一个处在弱耦合的类被修改,不会对有关系的类造成波及。