下面我針對編程設計模式和蘋果App開發常用設計模式對設計模式進行簡單介紹,也算是自己設計模式的讀書筆記。
一、編程設計模式
代碼設計遵循以下原則:
a、找出應用中可能需要變化之處,把它們獨立出來,不要和那些不需要變化的代碼混在一起。
b、針對接口編程,而不是針對實現編程。
c、多用組合,少用繼承。
d、別調用我們,我們會調用你。
e、一個類應該只有一個引起變化的原因。
下面就介紹一下常用設計模式和其對應的生活中的模式應用:
1,觀察者模式(Subject、Observer):氣象台、報紙雜志
天氣->各種布告板;報紙雜志->訂閱人、讀者
2,裝飾者模式:星巴克咖啡
3,工廠模式:披薩工廠、紅團印、蓋章
4,抽象工廠模式:披薩店
5,單例模式:巧克力工廠鍋爐
6,命令模式:遙控器開關、訂單
7,適配器模式:插座轉接頭
8,模板方法模式:泡茶、咖啡
9,迭代器和組合模式:餐廳並購煎餅屋,菜單的遍歷
10,狀態模式:糖果機、飲料機
11,代理模式:代表輔助完成某功能
12,復合模式:結合兩個或多個模式,解決一般或重復發生的問題。
由於本篇主要講述ios設計模式,這裡不對編程模式展開敘述,後期會對編程模式寫一篇學習筆記。這裡就簡要介紹一下。
二、蘋果App開發常用設計模式及架構探索
1,設計模式的起源:MVC設計模式
1.1在模型對象中封裝數據和基本行為
1.2使用視圖對象向用戶展示信息
1.3用控制器對象聯系其模型和視圖
2,針對接口編程,而不是針對實現編程
2.1.類和類型的差別:一個對象可以具有多個類型,而不同的類的對象可以有相同的類型
2.2.類型的所有對象,包括其子類的對象,都可以針對接口(協議、抽象類的虛方法)請求作出應答。
2.3.協議@protocol與抽象基類
2.3.1協議並不定義任何實現,而只聲明方法,以確定符合協議的類的行為。
如我們最常用的UITableViewDataSource&&UITableViewDelegate。
@protocol UITableViewDataSource
@required
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section;
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath;
@optional
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView;
- (nullable NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section;
……
@end
@protocol UITableViewDelegate
@optional
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath;
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath;
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section;
- (CGFloat) tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section;
……
@end
3,對象組合與類繼承
3.1類繼承是白箱復用,對象組合是黑箱復用
3.2 優先使用組合而不是類繼承
這裡舉一個項目裡“發言”用的組合例子:
有多種發言,1、文字2、圖文+文字3、音頻(視頻)4、帶“@”的發言 5、帶地址的發言 6、帶發送范圍的發言等,這個時候我們很難抽象出一個CELL基類滿足各種“發言”的顯示需求,即便可以,每種發言有自己相應的操作,這樣就會導致耦合度也非常高,這個時候我們就需要用到組合,每個CELL實現自己的代理方法實現對應的操作。如下:
//
//YYHomeRecorderTableViewCell.h
//SuperCrm
//
//Created by bill on 15/7/30.
//Copyright (c) 2015年Yonyouup Information Technology Co., Ltd. All rights reserved.
//
#import
#import "YYHomeRecorderRowView.h"
#import "YYBaseViewController.h"
#import "YYHomeBusiDetailViewController.h"
#import "YYCustomAnalysisViewController.h"
#import "YYHomeBusinessViewController.h"
#import "YYHomeModel.h"
#import "WFTextView.h"
#import "WFHudView.h"
#pragma mark最有價值的生意
@interface YYHomeRecorderTableViewCell : UITableViewCell
@property (nonatomic, strong) UILabel *h_titleLable;
@property (nonatomic, strong) UILabel *h_contentLable;
@property (nonatomic, strong) UIButton *h_arrrowButton;
@property (nonatomic, strong) UIImageView *h_imgview;
- (void)hanleData:(NSString *)contentStr;
@end
#pragma mark具體生意
@protocol YYHomeRecorderSecondTableViewCellDelegate
- (void)secondTableIntoNextPage:(YYBaseViewController *)baseVC;
- (void)selectChangeScroll;//刪除選擇框
@end
@interface YYHomeRecorderSecondTableViewCell : UITableViewCell
@property (nonatomic, assign) id
@property (nonatomic, strong) NSArray *h_dataArray;
- (void)handleData:(NSArray *)opportunitySummary;
@end
#pragma mark客戶分類信息
@protocol YYHomeRecorderThirdTableViewCellDelegate
- (void)thirdTableIntoNextPage:(YYBaseViewController *)baseVC;
@end
@interface YYHomeRecorderThirdTableViewCell : UITableViewCell
@property (nonatomic, assign) id
@property (nonatomic, strong) UIScrollView *h_scrollView;
//加載數據
- (void)handleData:(NSArray *)accountSummary;
@end
#pragma mark客戶生命周期
@interface YYHomeRecorderThirdOneTableViewCell : UITableViewCell
@property (nonatomic, strong) UILabel *h_titleLable;
@property (nonatomic, strong) UIButton *h_arrrowButton;
@end
#pragma mark記錄
@interface YYHomeRecorderForthTableViewCell : UITableViewCell
@property (nonatomic, strong) UIImageView *h_leftImg;
@property (nonatomic, strong) UILabel *h_contentLable;
@property (nonatomic, strong) UILabel *h_timeLable;
@property (nonatomic, strong) NSMutableArray *h_dataArray;
/*
*是側滑出現的右視圖時,h_rightImg出現,h_timeLable隱藏
*
*/
@property (nonatomic, strong) UIButton *h_rightImgButton;
- (void)handelData:(YYDynamicResultListModel *)resultListModel;
@end
@interface YYHomeRecorderLeftViewTableViewCell : UITableViewCell
@property (nonatomic, strong) UILabel * H_leftLable;
@property (nonatomic, strong) UIButton *h_arrowButton;
@end
4,OC開發常用的其他設計模式
4.1單例:通常用於用戶數據、行為的管理
+ (id)shareUserManager{
//Singletoninstance
staticSCRMUserManager *userManager;
//Dispatchingit once.
staticdispatch_once_t onceToken;
dispatch_once(&onceToken,^{
//Initializingkeyboard manger.
userManager= [[self alloc] init];
userManager.clientDevice= [[YYClientDeviceModel alloc] init];
userManager.accountInfo= [[AccountInfoModel alloc] init];
});
//ReturningkbManager.
returnuserManager;
}
4.2 觀察者:類互相依賴,或一對多,接受響應的情況下使用。NSNotificationCenter && NSNotification同名配對使用,如:
[[NSNotificationCenter defaultCenter] postNotificationName:NOTIFICATION_LOGIN_LOGOUT object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(loginOrLogout:)
name:NOTIFICATION_LOGIN_LOGOUT object:nil];
4.3代理:為其他對象提供一種代理以控制對這個對象的訪問。
A’a類實現了b類裡定義的協議,那a類實際上也是b類型的代理。
B’代理和BLOCK的選擇(詳見:http://www.cocoachina.com/ios/20150927/13525.html)
a如果對象有超過一個以上不同的事件源,使用delegation。
b如果一個對象是單例,不要使用delegation”
c如果對象的請求帶有附加信息,更應該使用delegation
d如果你使用block去請求一個可能失敗的請求,你應當只使用一個block。如下:
[fetchermakeRequest:^(idresult){
//dosomethingwithresult
}error:^(NSError*err){
//Dosomethingwitherror
}];
三、設計模式與代碼重構
設計模式是封裝、繼承、與多態,是編寫代碼之前需要考慮的問題。在編寫代碼過程中,特別是明捷開發的過程中會有很多重復代碼出現,既增加了代碼導致可讀性減弱,再需求變更時往往由於編寫過程中代碼沒有注釋,越來越多的代碼在一個方法裡堆疊,導致最終只有編寫代碼的人才能讀懂代碼,並進行修改。
1,方法傳參
只有參數(特別是參數為類對象時)是變動的時候,才需傳參,否則便可放入方法裡,在調用時減少獲取參數進行傳遞。如:
UIBarButtonItem *item = [self.navigationItem.rightBarButtonItems objectAtIndex:0];
UIButton *buttonItem = item.customView;
[self rightViewHidden:buttonItem];
- (void)rightViewHidden:(UIButton *)sender {
…
}
這裡該這麼寫,
[self rightViewHidden]
- (void)rightViewHidden {
UIBarButtonItem *item = [self.navigationItem.rightBarButtonItems objectAtIndex:0];
UIButton*buttonItem = item.customView;
…
}
2,數據、視圖初始化
當控制器由一個controller 推到另一個controller的時,如果數據源來至網絡,那麼應該在viewDidLoad裡對數據、視圖進行初始化,這樣即便網絡請求出問題,視圖也會有默認的顯示,不至於顯示空白,如下:
- (void)viewDidLoad {
[superviewDidLoad];
[selfinitData];
[selfinitView];
}
3,減少沒有意義的參數,如正整形數據類型TAG等,定義有意義的名稱進行代替,如:
typedef NS_ENUM(NSUInteger, HeaderViewTag) {
HeaderViewTagBackImage= 400,// 背景圖
HeaderViewTagImportantImage,//重要生意圖標
HeaderViewTagBussinessName,// 生意名稱
HeaderViewTagBussinessAmount,//生意金額
HeaderViewTagBussinessAvatar,//客戶頭像
HeaderViewTagBussinessCustomer,//客戶名稱
HeaderViewTagDiscoverTime,// 發現日期
HeaderViewTagEstimateTime//預計成交日期
};
4,簡化中間過程增加公用類可擴展性
如YYTypeChooseViewController的tableView.frame計算:
- (UITableView *)tableView
{
if (!_tableView) {
_tableView = [[UITableView alloc] init];
if([self needObjNumber]){
if (_objType == YYObjTypeClue) {
_tableView.frame = CGRectMake(0,- (kTypeCellHeight*3)-64, self.view.width, kTypeCellHeight *3);
} else {
if (_isMaster) {
_tableView.frame = CGRectMake(0, - (kTypeCellHeight*kTypeCount)-64, self.view.width, kTypeCellHeight *kTypeCount);
}else{
_tableView.frame = CGRectMake(0, - (kTypeCellHeight*(kTypeCount-1))-64, self.view.width, kTypeCellHeight *(kTypeCount-1));
}
}
}else if(_objType == YYObjTypeBriefing){
_tableView.frame = CGRectMake(0, - (kTypeCellHeight*kBriefingTypeCount)-64, self.view.width, kTypeCellHeight *kBriefingTypeCount);
}else if (_objType == YYObjTypeRelationCustomer)
{
_tableView.frame = CGRectMake(0,- (kTypeCellHeight*kRelaitonCount), self.view.width, kTypeCellHeight *kRelaitonCount);
}else if(_objType == YYObjTypeBusinessDept || _objType==YYObjTypeLifeForm || _objType == YYObjTypeBusinessStage){
_tableView.frame = CGRectMake(0, -(kTypeCellHeight*kBusinessDeptTypeCount)-64, self.view.width, kTypeCellHeight *kBusinessDeptTypeCount);
}else if (_objType == YYObjTypeAttList || _objType == YYObjTypeAttListTa)
{
_tableView.frame = CGRectMake(0, - (kTypeCellHeight*kBriefingTypeCount)-64, self.view.width, kTypeCellHeight *kBriefingTypeCount);
}else if (_objType == YYObjTypeAttChartAll ||_objType == YYObjTypeAttChartExternal||
_objType == YYObjTypeAttChartInner)
{
_tableView.frame = CGRectMake(0, - (kTypeCellHeight*2)-64, self.view.width, kTypeCellHeight *2);
}
_tableView.tableFooterView = [UIView new];
_tableView.delegate = self;
_tableView.dataSource = self;
_tableView.backgroundColor = [UIColor colorWithRed:255.0 green:255.0 blue:255.0 alpha:0.5];
}
return _tableView;
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
self.navigationController.navigationBar.translucent = NO;
[UIView animateWithDuration:0.6 delay:0 usingSpringWithDamping:0.7 initialSpringVelocity:10 options:UIViewAnimationOptionCurveEaseInOut animations:^{
if([self needObjNumber]){
if (_objType == YYObjTypeClue) {
_tableView.frame = CGRectMake(0,- (kTypeCellHeight*3)-64, self.view.width, kTypeCellHeight *3);
} else {
if (_isMaster) {
_tableView.frame = CGRectMake(0, - (kTypeCellHeight*kTypeCount)-64, self.view.width, kTypeCellHeight *kTypeCount);
}else{
_tableView.frame = CGRectMake(0, - (kTypeCellHeight*(kTypeCount-1))-64, self.view.width, kTypeCellHeight *(kTypeCount-1));
}
}
}else if(_objType == YYObjTypeBriefing){
_tableView.frame = CGRectMake(0, - (kTypeCellHeight*kBriefingTypeCount)-64, self.view.width, kTypeCellHeight *kBriefingTypeCount);
}else if (_objType == YYObjTypeRelationCustomer)
{
_tableView.frame = CGRectMake(0,- (kTypeCellHeight*kRelaitonCount), self.view.width, kTypeCellHeight *kRelaitonCount);
}else if(_objType == YYObjTypeBusinessDept || _objType==YYObjTypeLifeForm || _objType == YYObjTypeBusinessStage){
_tableView.frame = CGRectMake(0, -(kTypeCellHeight*kBusinessDeptTypeCount)-64, self.view.width, kTypeCellHeight *kBusinessDeptTypeCount);
}else if (_objType == YYObjTypeAttList || _objType == YYObjTypeAttListTa)
{
_tableView.frame = CGRectMake(0, - (kTypeCellHeight*kBriefingTypeCount)-64, self.view.width, kTypeCellHeight *kBriefingTypeCount);
}else if (_objType == YYObjTypeAttChartAll ||_objType == YYObjTypeAttChartExternal||
_objType == YYObjTypeAttChartInner)
{
_tableView.frame = CGRectMake(0, - (kTypeCellHeight*2)-64, self.view.width, kTypeCellHeight *2);
}
} completion:^(BOOL finished) {
}];
}
此類起初只有兩三個顯示類型,後來隨著開發的深入,顯示類型越來越多,判斷開始變得越來越復雜。認真查看代碼,此處不過根據各種類型以及各種類型時用友顯示的controller的導航欄是否透明,來計算tableView高度,計算中心點位置,從而使tableView處於正確位置。於是重構為:
//導航欄是否透明
- (BOOL)navBarAlphaIsZero {
return(self.objType == YYObjTypeCustomer || self.objType == YYObjTypeBusiness || self.objType == YYObjTypeContact || self.objType == YYObjTypeBriefing || self.objType == YYObjTypeAttList ||self.objType == YYObjTypeAttListTa);
}
- (UITableView *)tableView
{
if(!_tableView) {
_tableView= [[UITableView alloc] init];
CGRectframe = self.view.frame;
[_tableViewsetFrame:CGRectMake(0, -frame.size.height, frame.size.width, frame.size.height)];
_tableView.tableFooterView= [UIView new];
_tableView.delegate= self;
_tableView.dataSource= self;
_tableView.backgroundColor= [UIColor clearColor];
}
return_tableView;
}
- (void)viewWillAppear:(BOOL)animated
{
[superviewWillAppear:animated];
self.navigationController.navigationBar.translucent= NO;
[UIView animateWithDuration:0.6 delay:0 usingSpringWithDamping:0.7 initialSpringVelocity:10
options:UIViewAnimationOptionCurveEaseInOut animations:^{
CGRecttableViewFrame = self.tableView.frame;
if([self navBarAlphaIsZero]){
tableViewFrame.origin.y= 64;
}
else{
tableViewFrame.origin.y= 0;
}
[self.tableViewsetFrame:tableViewFrame];
}completion:^(BOOL finished) {
}];
}
這裡簡單舉幾個重構的例子。“設計模式”是在編寫代碼前應該考慮的問題,而“重構”則是在代碼編寫過程中,功能實現完成後,去優化。如果說編程界只有一個真理,那一定是“改變”,我們應該計劃改變,迎接改變,同時挑戰改變增強代碼可讀性,這樣才能不斷提高自己作為一名程序員的職業素養的一件事。