项目Demo
本文代码语言为Objective-C
规划形式是一种被广泛应用于软件工程的解决问题的办法。
它们能够协助开发人员进步代码的可复用性、可保护性和可扩展性。规划形式的运用能够让开发人员愈加专心于解决实践的问题而不是去考虑怎么完成它们。
在1994年出书的《规划形式:可复用面向目标软件的根底》(Design Patterns: Elements of Reusable Object-Oriented Software)一书中阐述了这些形式。 这本书中共涵盖了23种规划形式,后来又有一种新的规划形式被参加,使得总数达到24种,这些规划形式能够分为三种类型,分别是:
1. 创立型形式(Creational Patterns)
2. 结构型形式(Structural Patterns)
3. 行为型形式(Behavioral Patterns)
在学习这些规划形式前,本文将会介绍规划形式的七大准则。
1 总准则:开闭准则(Open/Closed Principle,OCP)
这个准则非常易于了解,相同,越是简略的准则,越是会成为规划形式中“基石”一样的存在。
该准则要求软件实体(类、模块、函数等)应该对扩展敞开,对修正关闭。
开闭准则的思维是:当新的需求出现时,应该尽可能地经过增加新的代码来满意这些需求,而不是直接修正现有代码。
经过增加新的代码,能够确保现有代码的稳定性,一起也能够进步代码的可保护性和可扩展性。
比如
当面对一个需求改变时,假如没有采用开闭准则,咱们通常会直接修正原有代码来适应新的需求,这可能会破坏原有代码的结构和稳定性。下面是一个不符合开闭准则的Objective-C 代码示例:
// 原有代码:轿车类
@interface Car : NSObject
@property (nonatomic, copy) NSString *brand;
@property (nonatomic, assign) NSInteger price;
- (void)startEngine;
@end
@implementation Car
- (void)startEngine {
NSLog(@"Start Engine!");
}
@end
// 需求改变:增加自动驾驶功用
@implementation Car
- (void)startEngine {
NSLog(@"Start Engine!");
// 新增代码:自动驾驶功用
NSLog(@"Auto Driving!");
}
@end
在上述代码中,原有的Car
类有一个startEngine
办法用于发动发动机,可是当需求增加自动驾驶功用时,咱们直接在startEngine
办法中增加了新的代码。这尽管能够满意新的需求,可是也破坏了原有代码的结构,违反了开闭准则的要求。
相反,假如咱们采用开闭准则,就能够经过扩展现有代码来满意新的需求,而不需求修正现有的代码。下面是一个符合开闭准则的Objective-C代码示例:
// 原有代码:轿车类
@interface Car : NSObject
@property (nonatomic, copy) NSString *brand;
@property (nonatomic, assign) NSInteger price;
- (void)startEngine;
@end
@implementation Car
- (void)startEngine {
NSLog(@"Start Engine!");
}
@end
// 新需求:增加自动驾驶功用
// 笼统类:轿车装修器
@interface CarDecorator : Car
@property (nonatomic, strong) Car *car;
- (instancetype)initWithCar:(Car *)car;
@end
@implementation CarDecorator
- (instancetype)initWithCar:(Car *)car {
if (self = [super init]) {
self.car = car;
}
return self;
}
- (void)startEngine {
[self.car startEngine];
}
@end
// 详细装修器:自动驾驶装修器
@interface AutoDriveDecorator : CarDecorator
- (void)startAutoDriving;
@end
@implementation AutoDriveDecorator
- (void)startAutoDriving {
NSLog(@"Auto Driving!");
}
- (void)startEngine {
[super startEngine];
[self startAutoDriving];
}
@end
在上述代码中,咱们采用了装修器形式来完成开闭准则。首要,咱们界说了一个笼统类CarDecorator
,用于表示所有的轿车装修器。然后,咱们界说了一个详细装修器AutoDriveDecorator
,用于增加自动驾驶功用。在AutoDriveDecorator
中,咱们重写了startEngine
办法,并在其中增加了自动驾驶功用。最终,咱们能够经过创立一个AutoDriveDecorator
目标来给轿车增加自动驾驶功用,而不需求修正原有的Car
类。详细地,咱们能够经过以下代码来完成:
// 创立一辆轿车
Car *car = [[Car alloc] init];
car.brand = @"Ford";
car.price = 100000;
// 运用装修器来增加自动驾驶功用
AutoDriveDecorator *decorator = [[AutoDriveDecorator alloc] initWithCar:car];
[decorator startEngine];
经过扩展现有代码来满意新的需求,而不需求修正原有的代码。这种办法能够确保原有代码的结构和稳定性,一起也使得代码愈加易于扩展和保护。
2 单一责任准则(Single responsibility principle, SRP)
不要存在多于一个导致类改变的原因,也便是说每个类应该完成单一的责任,否则就应该把类拆分.
单一责任准则能够协助咱们更好地组织代码,使得代码易于保护和扩展。经过遵从单一责任准则,咱们能够使代码愈加模块化,每个模块只担任一个责任,易于测验和重用。一起,当需求改变时,咱们也能够更容易地对代码进行修正,而不会影响到其他模块。
比如
不遵从单一责任准则的代码示例:
// 不遵从单一责任准则的代码示例
@interface Employee : NSObject
@property (nonatomic, strong) NSString *name;
@property (nonatomic, assign) NSInteger age;
@property (nonatomic, strong) NSString *department;
- (void)calculateSalary;
- (void)performDailyTasks;
@end
@implementation Employee
- (void)calculateSalary {
// 核算职工薪水的代码
}
- (void)performDailyTasks {
// 履行职工日常使命的代码
}
@end
上面这个示例中,Employee
类完成了两个责任:核算职工薪水和履行职工日常使命。这违反了单一责任准则,由于一个类不该该有多个导致其改变的原因。
下面是一个遵从单一责任准则的示例代码,将 Employee
类的两个责任拆分红两个类:
// 遵从单一责任准则的代码示例
@interface Employee : NSObject
@property (nonatomic, strong) NSString *name;
@property (nonatomic, assign) NSInteger age;
@property (nonatomic, strong) NSString *department;
@end
@implementation Employee
// Employee 类的代码只包括职工的根本信息,不再包括核算薪水和履行日常使命的代码
@end
@interface SalaryCalculator : NSObject
- (void)calculateSalaryForEmployee:(Employee *)employee;
@end
@implementation SalaryCalculator
- (void)calculateSalaryForEmployee:(Employee *)employee {
// 核算职工薪水的代码
NSLog(@"calculateSalaryForEmployee: name: %@, age: %ld, department: %@", employee.name, employee.age, employee.department);
}
@end
@interface TaskPerformer : NSObject
- (void)performDailyTasksForEmployee:(Employee *)employee;
@end
@implementation TaskPerformer
- (void)performDailyTasksForEmployee:(Employee *)employee {
// 履行职工日常使命的代码
NSLog(@"performDailyTasksForEmployee: name: %@, age: %ld, department: %@", employee.name, employee.age, employee.department);
}
@end
上面这个示例中,咱们将 Employee
类的两个责任分别拆分为 SalaryCalculator
类和 TaskPerformer
类。这样每个类只担任单一责任,易于保护和扩展,符合单一责任准则。
运用:
Employee *employee = [[Employee alloc] init];
employee.name = @"Layton Pike";
employee.age = 20;
employee.department = @"iOS development department";
SalaryCalculator *salaryCalculator = [[SalaryCalculator alloc] init];
[salaryCalculator calculateSalaryForEmployee:employee];
TaskPerformer *taskPerformer = [[TaskPerformer alloc] init];
[taskPerformer performDailyTasksForEmployee:employee];
在实践开发中,咱们能够经过以下几个方面来遵从单一责任准则:
- 分离责任:将一个类中的不同责任拆分红独立的类或模块,使每个类或模块只担任单一责任。
- 笼统接口:运用笼统接口或协议来界说类或模块之间的交互办法,防止直接依靠详细的完成。
- 约束类的大小:尽可能约束类的大小,防止过于杂乱的类,能够经过拆分、承继、组合等办法来完成。
- 定时重构:定时地对代码进行重构,去除重复代码,将代码依照责任进行组织,使得代码更易于保护和扩展。
总归,遵从单一责任准则是一个重要的规划准则,能够协助咱们写出愈加模块化、可保护和可扩展的代码。
3 里氏替换准则(Liskov Substitution Principle, LSP)
中心:子类目标能够替换父类目标出现在程序中,而不影响程序的正确性。
这个准则能够进步程序的灵活性和可保护性,使程序更容易扩展和修正。
比如
假定有一个Animal 类和一个Dog类,Dog 类是Animal 类的子类。Animal 类有一个run 办法,它能够让动物奔驰;Dog 类承继了Animal 类,并重写了run 办法。
@interface Animal : NSObject
- (void)run;
@end
@implementation Animal
- (void)run {
NSLog(@"Animal is running");
}
@end
@interface Dog : Animal
@end
@implementation Dog
- (void)run {
NSLog(@"Dog is running");
}
@end
依据里氏替换准则,咱们能够将Dog 目标赋值给Animal 目标,而程序仍然能够正常运转。例如:
Animal *animal = [[Dog alloc] init];
[animal run];
4 依靠倒转准则(Dependency Inversion Principle)
官方解说是:高层模块不该该依靠低层模块,二者都应该依靠其笼统,笼统不该该依靠细节,细节应该依靠笼统。
其实中心便是面向接口编程,而不是面向完成编程。
这样的优点便是经过笼统接口,能够削减模块间的依靠联系,进步体系的灵活性和可保护性。
咱们能够经过代码的比如更好的看出他们之间的不同:
比如
现在假定咱们有一个Logger
类,它担任将日志信息输出到控制台,还有一个MyClass
类,它需求运用Logger
类来输出日志信息。
没有恪守依靠倒转准则的代码:
Logger
类:
@interface Logger : NSObject
- (void)log:(NSString *)message;
@end
@implementation Logger
- (void)log:(NSString *)message {
NSLog(@"%@", message);
}
@end
MyClass
类,它需求运用Logger
类来输出日志信息:
@interface MyClass : NSObject
@property (nonatomic, strong) Logger *logger;
- (void)log;
@end
@implementation MyClass
- (void)doSomething {
[self.logger log:@"Log something"];
}
@end
这儿MyClass
类依靠于Logger
类,直接创立Logger
目标,并在doSomething
办法中调用Logger
目标的log
办法输出日志信息。这样做会形成MyClass
类和Logger
类之间的紧耦合,MyClass
类无法独立于Logger
类进行测验或扩展。
面向接口编程
为了恪守依靠倒转准则,咱们需求对MyClass
类进行改造。首要,咱们需求界说一个Logger
接口,包括一个log
办法:
@protocol LoggerProtocol <NSObject>
- (void)log:(NSString *)message;
@end
然后,咱们修正Logger
类,使其恪守接口办法:
@interface Logger : NSObject <LoggerProtocol>
@end
@implementation Logger
- (void)log:(NSString *)message {
NSLog(@"%@", message);
}
@end
然后,咱们修正MyClass
类,使其依靠于Logger
接口而非详细的Logger
类:
@interface MyClass : NSObject
@property (nonatomic, strong) Logger *logger;
- (void)log;
@end
@implementation MyClass
- (void)doSomething {
[self.logger log:@"Log something"];
}
@end
现在MyClass
类不再依靠于详细的Logger
类,而是依靠于一个笼统的Logger
接口。这样做能够将Logger
类的完成细节与MyClass
类解耦,使得MyClass
类愈加灵活和可保护。
完成:
MyClass *logClass = [[MyClass alloc] init];
Logger *logger = [[Logger alloc] init];
logClass.logger = logger;
[logClass log];
5 接口隔离准则(Interface Segregation Principle)
一个类不该该强迫其它类依靠它们不需求运用的办法,也便是说,一个类对另一个类的依靠应该建立在最小的接口上。
中心思维是:一个类应该只供给其它类需求运用的办法,而不该该强迫其它类依靠于它们不需求运用的办法。
经过恪守接口隔离准则,能够进步体系的灵活性、可保护性和可扩展性。
比如
假定咱们有一个Database
类,它供给了数据库的一些根本操作,包括衔接数据库、履行SQL句子等:
没有恪守接口隔离准则的代码:
@interface Database : NSObject
- (void)connect;
- (void)executeSQL:(NSString *)sql;
- (void)disconnect;
@end
@implementation Database
- (void)connect {
NSLog(@"Connect to database");
}
- (void)executeSQL:(NSString *)sql {
NSLog(@"Execute SQL: %@", sql);
}
- (void)disconnect {
NSLog(@"Disconnect from database");
}
@end
现在咱们有一个UserService
类,它依靠于Database
类来完成用户信息的存储:
@interface UserService : NSObject、
@property (nonatomic, strong) Database *database;
- (void)saveUser:(NSString *)name withEmail:(NSString *)email;
@end
@implementation UserService
- (void)saveUser:(NSString *)name withEmail:(NSString *)email {
[self.database connect];
NSString *sql = [NSString stringWithFormat:@"INSERT INTO users(name, email) VALUES('%@', '%@')", name, email];
[self.database executeSQL:sql];
[self.database disconnect];
}
@end
这儿UserService
类依靠于Database
类,并运用它的connect
、executeSQL
和disconnect
办法。但实践上,UserService
类只需求运用Database
类中的executeSQL
办法来完成用户信息的存储和查询,而不需求运用connect
和disconnect
办法。因而,UserService
类应该只依靠于一个更小的接口,即只需求一个executeSQL
办法。
为了恪守接口隔离准则,咱们需求对Database
类进行改造。首要,咱们界说一个DatabaseProtocol
接口,包括一个executeSQL
办法:
@protocol DatabaseProtocol <NSObject>
- (void)executeSQL:(NSString *)sql;
@end
然后,咱们修正Database
类,使其完成DatabaseProtocol
接口:
@interface Database : NSObject <DatabaseProtocol>
- (void)connect;
- (void)disconnect;
@end
@implementation Database
#pragma mark - Method
- (void)connect {
NSLog(@"Connect to database");
}
- (void)disConnect {
NSLog(@"Disconnect from database");
}
#pragma mark - DatabaseProtocol
- (void)executeSQL:(NSString *)sql {
NSLog(@"Execute SQL: %@", sql);
}
修正UserService
类,使其依靠于DatabaseProtocol
接口而不是详细的Database
类:
@interface UserService : NSObject
@property (nonatomic, strong) id<DatabaseProtocol> database;
- (instancetype)initWithDatabase:(id<DatabaseProtocol>)database;
- (void)saveUser:(NSString *)name withEmail:(NSString *)email;
@end
@implementation UserService
- (instancetype)initWithDatabase:(id<DatabaseProtocol>)database {
self = [super init];
if (self) {
_database = database;
}
return self;
}
- (void)saveUser:(NSString *)name withEmail:(NSString *)email {
[self.database executeSQL:[NSString stringWithFormat:@"INSERT INTO users(name, email) VALUES('%@', '%@')", name, email]];
}
@end
完成:
Database *database = [[Database alloc] init];
UserService *userService = [[UserService alloc] initWithDatabase:database];
[database connect];
[userService saveUser:@"SungKaikai" withEmail:@"aaaa.com"];
[database disConnect];
接口隔离准则的中心是为了让接口具有高内聚性和低耦合性。
假如一个接口过于臃肿,就需求将其拆分红多个小的接口,使得每个接口中只包括必要的办法。这样的优点是:
- 接口愈加具有内聚性:每个接口只需求重视自己的功用,而不需求重视其他接口中的办法,因而能够使接口愈加专心和具有内聚性。
- 接口之间的耦合度更低:每个接口只依靠于必要的办法,而不依靠于其他不必要的办法,因而能够使接口之间的耦合度更低。
- 代码的复用性更高:每个接口只包括必要的办法,因而能够使得代码的复用性更高,也能够进步代码的可读性和可保护性。
6 迪米特规律(最少知道准则)(The Law of Demeter, LoD)
一个类对自己依靠的类知道的越少越好。无论被依靠的类多么杂乱,都应该将逻辑封装在办法的内部,经过public
办法供给给外部。
最少知道准则的另一个表达办法是:只与直接的朋友通讯。
类之间只需有耦合联系,就叫朋友联系。耦合分为依靠、相关、聚合、组合等。咱们称出现为成员变量、办法参数、办法返回值中的类为直接朋友。局部变量、临时变量则不是直接的朋友。
比如
假定有一个购物车类ShoppingCart
,它有一个办法 addGoodsInfo
,用于增加产品到购物车中。这个办法需求从产品类Goods
中获取产品信息。最初的ShoppingCart
类可能会直接和这个类进行耦合,代码如下:
// 产品类
@interface Goods : NSObject
@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) CGFloat price;
@end
@implementation Goods
@end
// 购物车类
@interface ShoppingCart : NSObject
@property (nonatomic, strong) NSMutableArray<Goods *> *goodsArray;
- (void)addGoodsInfo;
- (void)printShoppingList;
@end
@implementation ShoppingCart
- (instancetype)init {
self = [super init];
if (self) {
_goodsArray = [NSMutableArray array];
}
return self;
}
- (void)addGoodsInfo {
// apple
Goods *apple = [[Goods alloc] init];
apple.name = @"apple";
apple.price = 5.0;
// orange
Goods *orange = [[Goods alloc] init];
orange.name = @"orange";
orange.price = 5.0;
[self.goodsArray addObject:apple];
NSLog(@"增加产品 %@ 到购物车", apple.name);
[self.goodsArray addObject:orange];
NSLog(@"增加产品 %@ 到购物车", orange.name);
}
- (void)printShoppingList {
NSLog(@"========== 购物车清单 ==========");
for (Goods *info in self.goodsArray) {
NSLog(@"%@ , %f", info.name, info.price);
}
NSLog(@"================================");
}
@end
在上面的代码中,ShoppingCart
类直接依靠了Goods
类,这样会导致ShoppingCart
类的修正会影响到这两个类,而且假如Goods
类的完成发生了改变,ShoppingCart
类也有必要相应地进行修正。
为了恪守最少知道准则,咱们能够修正ShoppingCart
类的完成,将获取产品信息逻辑放到办法外部,这样 ShoppingCart
类只需求调用这个类的办法即可。修正后的代码如下:
// 产品类
@interface Goods : NSObject
@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) CGFloat price;
@end
@implementation Goods
@end
// 购物车类
@interface ShoppingCart : NSObject
@property (nonatomic, strong) NSMutableArray<Goods *> *goodsArray;
- (void)addGoodsInfo:(Goods *)goods;
- (void)printShoppingList;
@end
@implementation ShoppingCart
- (instancetype)init {
self = [super init];
if (self) {
_goodsArray = [NSMutableArray array];
}
return self;
}
- (void)addGoodsInfo:(Goods *)goods {
[self.goodsArray addObject:goods];
NSLog(@"增加产品 %@ 到购物车", goods.name);
}
- (void)printShoppingList {
NSLog(@"========== 购物车清单 ==========");
for (Goods *info in self.goodsArray) {
NSLog(@"%@ , %f", info.name, info.price);
}
NSLog(@"================================");
}
@end
@interface User : NSObject
@property (nonatomic, copy) NSString *name;
@property (nonatomic, strong) ShoppingCart *shoppingCart;
- (void)addGoodsToCart:(Goods *)goods;
- (void)checkOut;
@end
@implementation User
- (instancetype)init {
self = [super init];
if (self) {
_shoppingCart = [[ShoppingCart alloc] init];
}
return self;
}
- (void)addGoodsToCart:(Goods *)goods {
[self.shoppingCart addGoodsInfo:goods];
}
- (void)checkOut {
NSLog(@"用户 %@ 结账,购物清单如下:", self.name);
[self.shoppingCart printShoppingList];
}
@end
在上面的代码示例中,每个类都只和自己直接的朋友进行通讯,遵从了最少知道准则。User
类只依靠于 ShoppingCart
类,而不直接依靠于 Goods
类。ShoppingCart
类只依靠于Goods
类。这样,当 Goods
类发生改变时,只会对 ShoppingCart
类产生影响,而不会对 User
类产生影响。
以下为完成代码:
User *user = [[User alloc] init];
user.name = @"Kaikai Pike";
// apple
Goods *apple = [[Goods alloc] init];
apple.name = @"apple";
apple.price = 5.0;
// orange
Goods *orange = [[Goods alloc] init];
orange.name = @"orange";
orange.price = 5.0;
// 用户增加产品到购物车
[user addGoodsToCart:apple];
[user addGoodsToCart:orange];
// 结账
[user checkOut];
7 组成复用准则(Composite Reuse Principle, CRP)
当咱们需求在一个类中运用另一个类的功用时,有两种办法:承继和组成/聚合。
承继是指子类承继父类的特点和办法,能够重写父类的办法,但也存在耦合性较高的问题,当父类修正时,子类也需求相应地修正。
而组成/聚合是指一个类作为另一个类的成员变量,经过调用该成员变量的办法来完成该类的功用。
比如
假定咱们有一个社交媒体应用程序,其中有多个视图控制器用于显现不同的内容。每个视图控制器都需求显现一个头像图像,并且需求处理用户点击头像时的事情。咱们能够运用承继来完成这个功用,如下所示:
@interface AvatarViewController : UIViewController
@property (nonatomic, strong) UIImageView *imageView;
- (void)handleAvatarTap;
@end
@implementation AvatarViewController
- (instancetype)init {
self = [super init];
if (self) {
_imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"default_avatar"]];
_imageView.userInteractionEnabled = YES;
UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleAvatarTap)];
[_imageView addGestureRecognizer:tapGesture];
[self.view addSubview:_imageView];
}
return self;
}
- (void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
self.imageView.frame = CGRectMake(0, 0, 100, 100);
}
- (void)handleAvatarTap {
NSLog(@"Avatar tapped in %@", NSStringFromClass([self class]));
}
@end
@interface ProfileViewController : AvatarViewController
@end
@implementation ProfileViewController
@end
@interface FeedViewController : AvatarViewController
@end
@implementation FeedViewController
@end
在这个比如中,咱们创立了一个AvatarViewController
基类,它包括了一个imageView
特点和handleAvatarTap
办法。然后,咱们创立了两个子类ProfileViewController
和FeedViewController
,它们都承继自AvatarViewController
,重写了handleAvatarTap
办法以处理自己的逻辑。在每个子类中,咱们都能够运用imageView
特点来显现头像,并处理用户点击头像时的事情。
尽管这种办法能够复用一部分代码,但它存在一些问题。例如,假如咱们需求增加新的视图控制器来显现头像,咱们需求创立一个新的子类,并重复完成大部分相同的代码。
现在,让咱们运用组成复用准则来改进这个比如。咱们将创立一个单独的头像视图组件,它担任显现头像和处理头像点击事情。每个视图控制器都将包括一个头像视图组件的实例,并将其增加到自己的视图中。这样,咱们能够轻松地创立新的视图控制器来显现头像,而无需创立新的子类。
下面是运用组合完成的比如:
@interface AvatarView : UIView
@property (nonatomic, strong) UIImageView *imageView;
@property (nonatomic, copy) void (^avatarTappedBlock)(void);
@end
@implementation AvatarView
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
_imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"0"]];
_imageView.userInteractionEnabled = YES;
UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleAvatarTap)];
[_imageView addGestureRecognizer:tapGesture];
[self addSubview:_imageView];
}
return self;
}
- (void)layoutSubviews {
[super layoutSubviews];
self.imageView.frame = CGRectMake(0, 0, 100, 100);
}
- (void)handleAvatarTap {
if (self.avatarTappedBlock) {
self.avatarTappedBlock();
}
}
@interface ProfileViewController : UIViewController
@property (nonatomic, strong) AvatarView *avatarView;
@end
@implementation ProfileViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.avatarView = [[AvatarView alloc] initWithFrame:CGRectMake(0, 0, self.view.bounds.size.width, self.view.bounds.size.height)];
self.avatarView.avatarTappedBlock = ^{
NSLog(@"Avatar tapped in ProfileViewController");
};
[self.view addSubview:self.avatarView];
}
@end
@interface FeedViewController : UIViewController
@property (nonatomic, strong) AvatarView *avatarView;
@end
@implementation FeedViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.avatarView = [[AvatarView alloc] initWithFrame:CGRectMake(0, 0, self.view.bounds.size.width, self.view.bounds.size.height)];
self.avatarView.avatarTappedBlock = ^{
NSLog(@"Avatar tapped in FeedViewController");
};
[self.view addSubview:self.avatarView];
}
@end
在这个比如中,咱们创立了两个视图控制器ProfileViewController
和FeedViewController
。这两个视图控制器都包括了一个AvatarView
的实例,并且为avatarTappedBlock
特点赋值了一个块,用于处理头像的点击事情。这两个视图控制器都运用了AvatarView
来显现头像,而不是承继 AvatarViewController
类。这种办法使得咱们能够轻松地创立新的视图控制器来显现头像,而无需创立新的子类。
总归,组合和承继都能够用来完成代码的复用,可是它们之间有着不同的优缺点。在运用承继时,咱们需求留意代码的耦合度,防止代码的重复和臃肿。在运用组合时,咱们需求将代码分解为更小的组件,并留意组件之间的接口规划。
项目Demo
24-Design-Patterns
下一篇
下一篇:创立型形式