单一职责,从字面上就能看出,各自有各自的职责,可以这样理解:
CALayer:动画和视图的显示。
UIView:为CALaler提供显示的内容,负责事件传递、事件响应。
他们每个类都有各自的职责,负责相应的功能。
例如:在开发过程中,我们自定义对象,每个类/对象,只负责一个功能,不要一个对象处理很多逻辑。
开:开放出接口供外部使用
闭:不能修改,想要修改,采用继承的方式
我们要尽量通过扩展软件实体来解决需求变化,而不是通过修改已有的代码来完成变化。
例如:封装出一个公用类,提供可使用接口。如果不能满足需求,不要直接修改类代码,最好采用继承/组合的方式,来拓展以满足需求。就像我们OC中提供的基础类一样,想要对其功能进行扩展就只能使用分类扩展的方法。就是category(分类)。
在继承体系中,子类中可以增加自己特有的方法,也可以实现父类的抽象方法,但是不能重写父类的非抽象方法,否则该继承关系就不是一个正确的继承关系。
注:可以拓展父类方法,不要复写父类方法。
就是说,父类某些非抽象的,具体实现的方法,我们不能修改,就像父类中有一个只输出一句固定的消息的方法,你在子类中重写了这个方法,输出了另一个消息,那么这个方法在子类中的定义就和父类没有任何关系了,就不满足该原则了。
例如:
//父类
- (void)carName {
NSLog(@"五菱宏光");
}
//子类
//正确的
- (void)carName {
NSLog(@"五菱宏光");
}
- (void)carColor {
NSLog(@"白色的");
}
//错误的,他影响到了父类原有的功能,父类只是想使用这个输出车的名字而已,它却又添加了颜色
- (void)carName {
NSLog(@"白色的五菱宏光");
}
设计接口时,应当各个功能隔离开,应更具体,更详细。不要拥挤到一起,做很多用不到的功能。
就是说对每个对应的功能我们都要书写其相应的接口,不要在同一个接口中实现该接口非必要的功能代码。像我们之前学习的使用分类创建一个类不同功能类型的接口、使用多个专门的协议、而不是一个庞大臃肿的协议(tableview的delegate和datasource类似这样)。
例如:
//错误的
//行驶的时候没必要非要开空调
- (void)useCar {
NSLog(@"行驶");
NSLog(@"开空调");
}
//正确的
- (void)useCar {
NSLog(@"行驶");
}
- (void)openTheAircondition {
NSLog(@"开空调");
}
抽象不应该依赖于具体实现,实现应依赖于抽象。 如接口的封装,调用接口不关心内部的实现。
我们应该在代码书写的时候将其抽象化,就像我们使用网络请求一样的,大体的网络请求结构是相同的,其中不同的只是具体的某个数值,那么我们就可以定义一个属性来获取不同的数值,进而做到不同的网络请求,这么我们就只需要每次该这个属性的值就行了。而不是对于每个不同的网络请求都有与之对应的方法,那么要定义的方法就太多了,也不符合该原则。
例如:
//错误的
- (void)getPhotoNetFirst:(NSString *)photosType {
NSString *tempString = [[NSString alloc] initWithFormat:@"http://47.116.14.251:8888/info/First"];
}
- (void)getPhotoNetSecond:(NSString *)photosType {
NSString *tempString = [[NSString alloc] initWithFormat:@"http://47.116.14.251:8888/info/Second"];
}
//正确的
- (void)getPhotoNet:(NSString *)photosType {
NSString *tempString = [[NSString alloc] initWithFormat:@"http://47.116.14.251:8888/info/%@", self.transPhotosType];
}
迪米特法则也叫做最少知道原则(Least Know Principle),一个对象应当对其他对象尽可能少的了解,实现高聚合、低耦合。对象之间互相不要知道太多具体实现,知道的越多耦合性越强,修改一个对象对另一个影响越大。
类与类之间不应该知道对方太多的具体实现,况且有的类不一定会让你知道其实现,这时候我们就通过其已经给出的对象来进行判断就行了,就好比我们C++经常写代码的栈判空处理,我们只需要其是判空就行了,不用关心它是如何通过某些代码判断的。以及我们经常用的设计模式MVC,MVVM,MVP都是应用的迪米特法则。
低耦合,高内聚是我们软件编程的总的原则。
例如:
一句话概括六大原则: 单一职责原则告诉我们实现类要职责单一;里氏替换原则告诉我们不要破坏继承体系;依赖倒置原则告诉我们要面向接口编程;接口隔离原则告诉我们在设计接口的时候要精简单一;迪米特法则告诉我们要降低耦合。而开闭原则是总纲,他告诉我们要对扩展开放,对修改关闭。
对这六个原则的遵守并不是是和否的问题,而是多和少的问题,也就是说,我们一般不会说有没有遵守,而是说遵守程度的多少。任何事都是过犹不及,设计模式的六个设计原则也是一样,制定这六个原则的目的并不是要我们刻板的遵守他们,而需要根据实际情况灵活运用。对他们的遵守程度只要在一个合理的范围内,就算是良好的设计。我们用一幅图来说明一下。
图中的每一条维度各代表一项原则,我们依据对这项原则的遵守程度在维度上画一个点,则如果对这项原则遵守的合理的话,这个点应该落在红色的同心圆内部;如果遵守的差,点将会在小圆内部;如果过度遵守,点将会落在大圆外部。一个良好的设计体现在图中,应该是六个顶点都在同心圆中的六边形。
在上图中,设计1、设计2属于良好的设计,他们对六项原则的遵守程度都在合理的范围内;设计3、设计4设计虽然有些不足,但也基本可以接受;设计5则严重不足,对各项原则都没有很好的遵守;而设计6则遵守过渡了,设计5和设计6都是迫切需要重构的设计。
在基类中定义创建对象的一个接口,让子类决定实例化哪个类。工厂方法让一个类的实例化延迟到子类中进行。工厂方法要解决的问题是对象的创建时机,它提供了一种扩展的策略,很好地符合了开放封闭原则。工厂方法
也叫做虚构造器(Virtual Constructor)
。
专门定义一个类(工厂类)来负责创建其他类的实例。可以根据创建方法的参数来返回不同类的实例,被创建的实例通常具有共同的父类。就是通过你传入的参数来判断具体要实现的实例。
通俗的讲: 简单工厂模式就是一个工厂可以生产多种产品,只需要用户说明自己想生产的产品工厂就可以生产对应的产品。
设置两个button,控制不同的产品:
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
self.mouseButton = [UIButton buttonWithType:UIButtonTypeSystem];
[self.mouseButton setTitle:@"Mouse" forState:UIControlStateNormal];
[self.view addSubview:self.mouseButton];
[self.mouseButton setFrame:CGRectMake(50, 200, 100, 100)];
[self.mouseButton addTarget:self action:@selector(pressButton:) forControlEvents:UIControlEventTouchUpInside];
self.keyboardButton = [UIButton buttonWithType:UIButtonTypeSystem];
[self.keyboardButton setTitle:@"Keyboard" forState:UIControlStateNormal];
[self.view addSubview:self.keyboardButton];
[self.keyboardButton setFrame:CGRectMake(150, 200, 100, 100)];
[self.keyboardButton addTarget:self action:@selector(pressButton:) forControlEvents:UIControlEventTouchUpInside];
}
- (void)pressButton:(UIButton *)button {
id whichProduct = [SimpleFactory createProduct:button.titleLabel.text];
[whichProduct productType];
}
并且创建相应的产品:
#import "keyboardProduct.h"
@implementation keyboardProduct
- (void)productType {
NSLog(@"键盘");
}
@end
通过传递的信息来告诉工厂你想加工的产品:
+ (id)createProduct:(NSString *)productType {
NSArray *productArray = @[@"Mouse", @"Keyboard"];
switch ([productArray indexOfObject:productType]) {
case 0:
return [[MouseProduct alloc] init];
break;
case 1:
return [[keyboardProduct alloc] init];
break;
}
return NULL;
}
工厂方法模式(Factory Method Pattern)
又称为工厂模式
,工厂父类负责定义创建产品对象的公共接口,而工厂子类则负责生成具体的产品对象,即通过不同的工厂子类来创建不同的产品对象。就像Cocoa Touch中的NSNumber
的numberWithBool
和numberWithInt
方法,他们传入不同类型的参数,获得NSNumber
实例。
通俗的讲: 工厂方法模式就是在简单工厂模式的前提下,扩展了工厂,他不再是通过一个工厂来完成多个产品的生产,而是通过一个统一的抽象工厂,分配任务给底下相应生产专一产品的工厂让他们来生产产品。
工厂方法和简单工厂有一些区别,简单工厂是由一个代工厂生产不同的产品,而工厂方法是对工厂进行抽象化,不同产品都由专门的具体工厂来生产。
即:多个工厂对应多个品牌厂商进行一对一生产。
创建两个button,控制不同工厂生产产品:
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
self.mouseButton = [UIButton buttonWithType:UIButtonTypeSystem];
[self.mouseButton setTitle:@"Mouse" forState:UIControlStateNormal];
[self.view addSubview:self.mouseButton];
[self.mouseButton setFrame:CGRectMake(50, 200, 100, 100)];
[self.mouseButton addTarget:self action:@selector(pressButton:) forControlEvents:UIControlEventTouchUpInside];
self.keyboardButton = [UIButton buttonWithType:UIButtonTypeSystem];
[self.keyboardButton setTitle:@"Keyboard" forState:UIControlStateNormal];
[self.view addSubview:self.keyboardButton];
[self.keyboardButton setFrame:CGRectMake(150, 200, 100, 100)];
[self.keyboardButton addTarget:self action:@selector(pressButton:) forControlEvents:UIControlEventTouchUpInside];
}
- (void)pressButton:(UIButton *)button {
id whichFactory = [MethodFactory allocatingTask:button.titleLabel.text];
id whichProduct = [whichFactory creatProduct];
[whichProduct productType];
}
定义工厂基类,其中包含子类工厂要实现的方法:
#import <Foundation/Foundation.h>
#import "ProductProtocol.h"
@interface BaseFactory : NSObject<ProductProtocol>
- (id)creatProduct;
@end
子类去实现该基类的方法:
//.h文件
#import "BaseFactory.h"
NS_ASSUME_NONNULL_BEGIN
@interface KeyboardFactory : BaseFactory
@end
NS_ASSUME_NONNULL_END
//.m文件
#import "KeyboardFactory.h"
#import "KeyboardProduct.h"
@implementation KeyboardFactory
- (id)creatProduct {
NSLog(@"生产键盘");
return [[KeyboardProduct alloc] init];
}
@end
同时实现工厂产品类:
#import "KeyboardProduct.h"
@implementation KeyboardProduct
- (void)productType {
NSLog(@"开始生产键盘");
}
@end
提供一个创建一系列相关或相互依赖对象的接口,而无须指定他们具体的类。一个工厂等级结构可以创建出分属于不同产品等级结构的一个产品族中的所有对象。
通俗的讲: 抽象工厂就是在工厂方法模式的基础上,扩展了产品,之前通过抽象工厂给各实例工厂说明自己要生产的产品,但是一个工厂就只能生产一种产品,但是抽象工厂就是它的升级版,现在的一个实例工厂就可以生产多个产品,相当于一个工厂就是一个品牌,他可以生产手机、键盘、电脑等等。
定义两个button,控制不同的工厂厂家:
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
self.hpButton = [UIButton buttonWithType:UIButtonTypeSystem];
[self.hpButton setTitle:@"Hp" forState:UIControlStateNormal];
[self.view addSubview:self.hpButton];
[self.hpButton setFrame:CGRectMake(50, 200, 100, 100)];
[self.hpButton addTarget:self action:@selector(pressButton:) forControlEvents:UIControlEventTouchUpInside];
self.dellButton = [UIButton buttonWithType:UIButtonTypeSystem];
[self.dellButton setTitle:@"Dell" forState:UIControlStateNormal];
[self.view addSubview:self.dellButton];
[self.dellButton setFrame:CGRectMake(150, 200, 100, 100)];
[self.dellButton addTarget:self action:@selector(pressButton:) forControlEvents:UIControlEventTouchUpInside];
}
- (void)pressButton:(UIButton *)button {
id whichFactory = [AbstractFactory allocatingTask:button.titleLabel.text];
id whichProduct = [whichFactory createMouse];
[whichProduct productType];
}
再定义两个基类,一个工厂基类,一个产品基类:
//工厂基类
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface BaseFactory : NSObject
- (id)createKeyboard;
- (id)createMouse;
@end
NS_ASSUME_NONNULL_END
//产品基类
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface BaseProduct : NSObject
- (void)productType;
@end
NS_ASSUME_NONNULL_END
在定义上述两个基类的子类,并实现其方法:
//工厂子类,剩下的等同
//.h文件
#import "BaseFactory.h"
NS_ASSUME_NONNULL_BEGIN
@interface HpFactory : BaseFactory
@end
NS_ASSUME_NONNULL_END
//.m文件
#import "HpFactory.h"
#import "KeyboardHpProduct.h"
#import "MouseHpProduct.h"
@implementation HpFactory
- (id)createKeyboard {
NSLog(@"Hp Factory Create Keyboard");
return [[KeyboardHpProduct alloc] init];
}
- (id)createMouse {
NSLog(@"Hp Factory Create Mouse");
return [[MouseHpProduct alloc] init];
}
@end
//产品子类,剩下的等同
//.h文件
#import "BaseProduct.h"
NS_ASSUME_NONNULL_BEGIN
@interface KeyboardDellProduct : BaseProduct
@end
NS_ASSUME_NONNULL_END
//.m文件
#import "KeyboardDellProduct.h"
@implementation KeyboardDellProduct
- (void)productType {
NSLog(@"Dell keyboard");
}
@end
可能只这么看看没有什么体会,可以看看我的Demo:工厂模式