前言
刚毕业就做iOS开发,到现在也有些年头了,感觉自己在工作中慢慢摸索、总结出了一套自己的程序设计思路,而且这套思路也不错的经受住了很多个不同类型项目、不同团队的实际考验,一直都有着不错的表现,*近不是很忙,借着博客记录一下在程序设计上自己的收获与心得。

个人总结,app客户端程序可以划分为两个部分,下层部分与数据打交道,包含网络请求与本地数据操作(存取);上层部分就是应用层部分,负责UI交互与业务逻辑的串联,如下图:

%title插图%num
一般在项目实施时,我们*步都会先商量、定义好底层的数据模型、本地数据存储接口以及网络层接口(本地的+服务器的)。然后,两组人同时起头并进进行开发,负责上层的同事完全不用关心下层(数据相关)的具体实现(数据是怎么从网上请求回来的;请求返回后如何处理、存储数据;本地数据是如何存储获取的;底层的存储是怎么实现的,等等),他们只需要知道之前定义好的本地网络层及数据层接口就行了,在合适的时机去调用即可。而下层开发的同事,也是不用去考虑上层的具体实现,两拨人的工作是不会产生任何冲突的。一般情况下,下层的开发工作会相对更快的完成,然后下层的开发同事就可以返过头来去参与上层同事的开发工作,*终完成项目。

当然,要达到上面所说的效果,就离不开前期一定的设计工作,下面就从数据层、网络层和应用层三个层次讲一下我一直以来采用的设计方法。

总的来说,明确职责,各司其职,尽力而为,是我在程序设计时的基本指导思想;

点到为止、量力而行,不要过度设计、过度模式是我的设计原则,够用就行。其实设计的目的主要就是为了让代码有更高的独立性、纯粹性(不臃肿、职责明确)、通用性和扩展性,手段一般也就是不断的换着花样分层或解藕。个人感觉,这部分工作在大型互联网企业、大团队、特大项目中还是很有必要的,是可以不断追求的,因为大团队人多,业务繁杂,如果设计层面上不做好,不花精力去实现、改进,都揉杂在一起,团队间相互牵制,基本无法配合,而且还会带来后续扩展性差、可能会重复实现、重复造轮子等问题;但是,设计都是需要花费精力去思考和实现的,同时往往也会在某种程度上增加代码的”复杂性”与代码量,像中小型团队(非特大型项目),人不多,项目相对独立、业务也不是太过庞杂等,沟通与后续开发修改成本本身不会太大,所以权衡设计、把握设计的尺度就显的尤为重要。

数据层设计
1.基本设计
数据层负责模型定义+本地数据存取(数据操作),其实看看realm或者coredata这类框架的设计,当你定义好模型后,数据的存取操作也随之完成,也印证了我们对这个层次在职责划分上的合理性。这个层次可以说是**单纯的一层,在代码层面上与其他层次完全没有任何缠绕,在实现上可以做到完全的独立,有着很好的复用与迁移性。

这个层次位于整个架构的*底层,是其它所有层次的根基与”工具”。

在数据层里定义好项目中所有可能用到的模型类,同时还要实现好每种模型对应的所有数据操作

%title插图%num

[objc]  view plain  copy
//模型定义
@interface JHSMS : NSObject

@property (nonatomic,copy) NSString *uniqueId;
@property (nonatomic,copy) NSString *phoneNum;
@property (nonatomic,copy) NSString *content;
@property (nonatomic,copy) NSString *relatedWorkNum;
@property (nonatomic,strong) NSDate *msgDate;
@property (nonatomic,assign) BOOL isInComing;
@property (nonatomic,assign) SMS_SENDSTATUS sendStatus;
……..

//数据操作
+(void)saveSmsAry:(NSMutableArray *)smsAry;
+(BOOL)saveSingleSms:(JHSMS *)sms;
+(void)updateMsgStatus:(SMS_SENDSTATUS)sendStatus uniqueId:(NSString *)uniqueId;
+(NSMutableArray *)getRecentlySMSs:(NSString *)phoneNum;
+(NSMutableArray *)getSMSs:(NSString *)phoneNum beforeSms:(JHSMS *)sms;
+(void)resendSms:(JHSMS *)sms;
…….
@end
具体数据操作实现
[objc]  view plain  copy
+(BOOL)saveSingleSms:(JHSMS *)sms
{
BOOL __block result = YES;
[_shareQueue inTransaction:^(FMDatabase *db, BOOLBOOL *rollback) {

@try {
NSString *insertStr = @”insert into ‘SMS'(‘uniqueID’,’appUserWorkId’,’phoneNum’,’content’,’msgDate’,’contactType’,’friendContactId’,’relatedWorkNum’,’isInComing’,’sendStatus’) values (?,?,?,?,?,?,?,?,?,?)”;
CONATACT_TYPE contactType;

FMResultSet *rs = [db executeQuery:@”select relatedWorkId from PhonesInCorpContacts where phoneNum = ?”,sms.phoneNum];
if ([rs next])
{
sms.friendContactId = [rs stringForColumn:@”relatedWorkId”];
contactType = CORP_CONTACT;
}
else
{
FMResultSet *rs2 = [db executeQuery:@”select relatedRecordId from PhonesInLocalContacts where appUserWorkId = ? and phoneNum = ?”,appUserWorkId,sms.phoneNum];
…………..
[rs2 close];
}
…………
}
}];
return result;
}
2.数据层的设计改进
数据层的设计改进主要针对的是数据操作部分,上面的设计中存在两个问题,导致了模型类的过于臃肿和耦合性过大

(1)模型定义与数据操作耦合性过大

数据的本地存取操作可能会有很多不同方式(nsuerdefault、数据库、文件等等)及不同的业务需求,如果按照上面的实现,我们没有一个单独的层次去分离出数据操作的具体实现,那么如果以后要更换底层的存储方式,或是修改、添加数据操作实现时,那么就会对模型类造成影响,导致模型类的暴露与修改。所以我们还可以在数据层里单独划分出模型层+数据操作层.

%title插图%num

%title插图%num

在另一个项目中,我加入saver层,在这个层次里完成具体的数据存储操作,可以是数据库操作,也可以文件操作或是别的,甚至我还完成过同时支持多种存储形式,然后交由调用者选择的需求,无论怎样,这些不同的底层实现都封装可以在这个层次中单独的处理,对使用者透明,修改、实现时不会对模型类产生任何影响,同时大大减少了模型类的代码体量与功能承载压力

(2)数据操作与业务有缠绕

第二个问题是,针对每种模型(数据),其实**纯粹根本的操作就是对一个(一组)数据的增、删、改、查,而在我上面的代码示例中

[objc]  view plain  copy
+(void)updateMsgStatus:(SMS_SENDSTATUS)sendStatus uniqueId:(NSString *)uniqueId;
+(void)resendSms:(JHSMS *)sms;
+(NSMutableArray *)getRecentlySMSs:(NSString *)phoneNum;
这些操作明显是依据业务而定义(衍生)的,不具备普遍的通用性,没有复用的价值,同时造成了数据操作层与业务的耦合,于是我们可以对数据操作层再细分

%title插图%num

划分后,基础操作层可能包含下列这类函数实现,分离后它们具有很好的”原子性”、通用性与复用性

[objc]  view plain  copy
+(BOOL)saveSmsAry:(NSMutableArray *)smsAry;
+(BOOL)saveSingleSms:(JHSMS *)sms;
+(BOOL)deleteSms:(JHSMS *)sms;
+(NSMutableArray *)getSMSsWithCondition:(NSString *)condition;
+(BOOL)updateSmsForProperty:(NSString *)property value:(NSString *)value;
而业务操作层则是根据你具体的项目业务需求,对基础操作层进行不同的组合、调用

在数据层的实际设计开发中,我大部分情况下都是按基础设计去做的,除非有特殊需要,比较少的对数据操作层进行进一步细分.数据层比较单纯、独立、基础,其实修改、实现成本本身就不大,除非有特殊需求,一般我也就模型、数据操作写一起了,这样开发起来速度*快,复用、修改起来的工作量与难度也并不会很大.