iOS 谈谈代码的封装

  最近做了下CoreData的增删改查,简单谈谈封装的过程。可结合demo的提交记录阅读。

iOS 谈谈代码的封装_第1张图片
CoreDataMainViewController

  主控制器,里面有7个点击事件,然后加上对一个表的简单增删改查,也就200多行。
coreData增删改成

#import "CoreDataMainViewController.h"
#import 
#import "User+CoreDataProperties.h"

#define db_user @"User"

@interface CoreDataMainViewController ()
@property (nonatomic,strong) NSManagedObjectContext * managedObjectContext;
@end

@implementation CoreDataMainViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
}

#pragma mark - action

- (IBAction)insertAction:(id)sender {
    [self insertWithUser:@{@"name":@"zhangsan",@"userId":@"1"}];
}

- (IBAction)insertBatchAction:(id)sender {
    NSArray * params = @[@{@"name":@"zhangsan",@"userId":@"1"},
                         @{@"name":@"lisi",@"userId":@"2"}
                         ];
    [self insertWithUserArr:params];
}

- (IBAction)delAction:(id)sender {
    [self deleteWithID:@"1"];
}

- (IBAction)delBatchAction:(id)sender {
    [self deleteAll];
}

- (IBAction)searchAction:(id)sender {
    [self searchWithID:@"2"];
}

- (IBAction)searchAllAction:(id)sender {
    [self searchAll];
}

- (IBAction)modityAction:(id)sender {
    [self updateWithID:@"1" params:@{@"name":@"wangwu"}];
}

#pragma mark - CRUD

- (void)insertWithUser:(NSDictionary *)param{
    User *user = [NSEntityDescription insertNewObjectForEntityForName:db_user inManagedObjectContext:self.managedObjectContext];
    user.userId = param[@"userId"];
    user.name = param[@"name"];
    [self saveContext:self.managedObjectContext];
}

- (void)insertWithUserArr:(NSArray *)arr{
    for (NSDictionary * param in arr) {
        User *user = [NSEntityDescription insertNewObjectForEntityForName:db_user inManagedObjectContext:self.managedObjectContext];
        user.userId = param[@"userId"];
        user.name = param[@"name"];
    }
    
    [self saveContext:self.managedObjectContext];
}

- (void)deleteWithID:(NSString *)idString{
    NSManagedObjectContext *context = self.managedObjectContext;
    
    // 2.创建查询请求
    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:db_user];
    NSPredicate *predicate = [NSPredicate predicateWithFormat:[NSString stringWithFormat:@"userId == %@", idString]];
    [fetchRequest setPredicate:predicate];
    
    // 3.执行查询方法,返回结果
    NSError *error = nil;
    NSArray *resultArray = [context executeFetchRequest:fetchRequest error:&error];
    if (error) {
        return;
    }
    
    // 4.将MO标记为删除
    for (User *user in resultArray) {
        [context deleteObject:user];
    }
    // 5.提交修改
    [self saveContext:self.managedObjectContext];
}

/// 删除所有记录
- (void)deleteAll {
    // 1.获取MOC
    NSManagedObjectContext *context = self.managedObjectContext;
    // 2.创建查询请求
    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:db_user];
    // 3.执行查询方法,返回结果
    NSError *error = nil;
    NSArray *resultArray = [context executeFetchRequest:fetchRequest error:&error];
    if (error) {
        return;
    }
    // 4.将MO标记为删除
    for (User *user in resultArray) {
        [context deleteObject:user];
    }
    // 5.提交修改
    [self saveContext:context];
    
}

/// 查询指定记录
- (NSArray *)searchWithID:(NSString *)idString {
    // 1.获取MOC
    NSManagedObjectContext *context = self.managedObjectContext;
    
    // 2.创建查询请求
    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:db_user];
    NSPredicate *predicate = [NSPredicate predicateWithFormat:[NSString stringWithFormat:@"userId == %@", idString]];
    [fetchRequest setPredicate:predicate];
    
    // 3.执行查询方法,返回结果
    NSError *error = nil;
    NSArray *resultArray = [context executeFetchRequest:fetchRequest error:&error];
    [self printSearchResult:resultArray];
    return resultArray;
}

/// 查询所有记录
- (NSArray *)searchAll{
    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:db_user];
    
    NSError *error = nil;
    NSArray *resultArray = [self.managedObjectContext executeFetchRequest:fetchRequest error:&error];
    [self printSearchResult:resultArray];
    return resultArray;
}

- (void)printSearchResult:(NSArray *)resultArray{
    NSLog(@"查询结果%lu条",(unsigned long)resultArray.count);
    for (User *user in resultArray) {
        NSLog(@"userId=%@,name=%@", user.userId, user.name);
    }
}

- (void)saveContext:(NSManagedObjectContext *)context {
    if (!context) {
        return;
    }
    NSError *error = nil;
    // 判断MOC监听的MO对象是否有改变,如果有则提交保存
    if ([context hasChanges] && ![context save:&error]) {
        // log error
        NSAssert(YES, @"save error!!!");
    }
    
    if (context.parentContext) {
        // 递归保存
        [self saveContext:context.parentContext];
    }
}

/// 更新指定记录
- (void)updateWithID:(NSString *)idString params:(NSDictionary *)params{
    // 1.获取MOC
    NSManagedObjectContext *context = self.managedObjectContext;
    
    // 2.创建查询请求
    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:db_user];
    NSPredicate *predicate = [NSPredicate predicateWithFormat:[NSString stringWithFormat:@"userId == %@", idString]];
    [fetchRequest setPredicate:predicate];
    
    // 3.执行查询方法,返回结果
    NSError *error = nil;
    NSArray *resultArray = [context executeFetchRequest:fetchRequest error:&error];
    if (error) {
        return;
    }
    
    // 4.更新MO
    for (User *user in resultArray) {
        user.name = params[@"name"];
    }
    
    // 5.提交修改
    [self saveContext:context];
}

#pragma mark - context

- (NSManagedObjectContext *)managedObjectContext{
    if (!_managedObjectContext) {
        _managedObjectContext = [self getContext];
    }
    return _managedObjectContext;
}

- (NSManagedObjectContext *)getContext {
    NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"Tesl" withExtension:@"momd"];
    //根据模型文件创建模型对象
    NSManagedObjectModel *model = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
    
    
    //2、创建持久化助理
    //利用模型对象创建助理对象
    NSPersistentStoreCoordinator *store = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model];
    
    //数据库的名称和路径
    NSString *docStr = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
    NSString *sqlPath = [docStr stringByAppendingPathComponent:@"Tesl.sqlite"];
    NSLog(@"path = %@", sqlPath);
    NSURL *sqlUrl = [NSURL fileURLWithPath:sqlPath];
    
    //设置数据库相关信息
    [store addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:sqlUrl options:nil error:nil];
    
    //3、创建上下文
    NSManagedObjectContext *context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
    
    //关联持久化助理
    [context setPersistentStoreCoordinator:store];
    return context;
}

@end

  由于CoreData的增删改查需要手动管理多线程,然后考虑引用MagicalRecordCRUD来管理。再在CoreDataMainViewController增加MR的增删改查代码,明显整个类都臃肿了起来,而且同样功能的放在一起,你肯定也屡不清,我把CoreData的增删改查提取出来了,我做了以下步骤:

第一步:新建了协议类CRUDProtocol.h

@protocol CRUDProtocol 

- (void)insertWithUser:(NSDictionary *)param;
- (void)insertWithUserArr:(NSArray *)arr;
- (void)deleteWithID:(NSString *)idString;
- (void)deleteAll;
- (NSArray *)searchWithID:(NSString *)idString;
- (NSArray *)searchAll;
- (void)updateWithID:(NSString *)idString params:(NSDictionary *)params;

@end

第二步:新建CRUD类,实现CRUDProtocol协议

@interface CRUD : NSObject

@end

#import "CRUD.h"
#import 
#import "User+CoreDataProperties.h"

@interface CRUD()

@property (nonatomic,strong) NSManagedObjectContext * managedObjectContext;

@end

@implementation CRUD

- (void)insertWithUser:(NSDictionary *)param{
    NSString * userId = param[db_user_userId];
    
    NSArray * searchResultArr = [self searchWithID:userId];
    if (searchResultArr.count > 0) {
        [self updateWithID:userId params:param];
        return;
    }else{
        User *user = [NSEntityDescription insertNewObjectForEntityForName:db_user inManagedObjectContext:self.managedObjectContext];
        user.userId = userId;
        user.name = param[db_user_name];
        [self saveContext:self.managedObjectContext];
    }
}

- (void)insertWithUserArr:(NSArray *)arr{
    for (NSDictionary * param in arr) {
        [self insertWithUser:param];
    }
    
    [self saveContext:self.managedObjectContext];
}

- (void)deleteWithID:(NSString *)idString{
    NSManagedObjectContext *context = self.managedObjectContext;
    
    // 2.创建查询请求
    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:db_user];
    NSPredicate *predicate = [NSPredicate predicateWithFormat:[NSString stringWithFormat:@"%@ == %@",db_user_userId ,idString]];
    [fetchRequest setPredicate:predicate];
    
    // 3.执行查询方法,返回结果
    NSError *error = nil;
    NSArray *resultArray = [context executeFetchRequest:fetchRequest error:&error];
    if (error) {
        return;
    }
    
    // 4.将MO标记为删除
    for (User *user in resultArray) {
        [context deleteObject:user];
    }
    // 5.提交修改
    [self saveContext:self.managedObjectContext];
}

/// 删除所有记录

- (void)deleteAll {
    // 1.获取MOC
    NSManagedObjectContext *context = self.managedObjectContext;
    // 2.创建查询请求
    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:db_user];
    // 3.执行查询方法,返回结果
    NSError *error = nil;
    NSArray *resultArray = [context executeFetchRequest:fetchRequest error:&error];
    if (error) {
        return;
    }
    // 4.将MO标记为删除
    for (User *user in resultArray) {
        [context deleteObject:user];
    }
    // 5.提交修改
    [self saveContext:context];
    
}

/// 查询指定记录

- (NSArray *)searchWithID:(NSString *)idString {
    // 1.获取MOC
    NSManagedObjectContext *context = self.managedObjectContext;
    
    // 2.创建查询请求
    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:db_user];
    NSPredicate *predicate = [NSPredicate predicateWithFormat:[NSString stringWithFormat:@"%@ == %@",db_user_userId ,idString]];
    [fetchRequest setPredicate:predicate];
    
    // 3.执行查询方法,返回结果
    NSError *error = nil;
    NSArray *resultArray = [context executeFetchRequest:fetchRequest error:&error];
    [self printSearchResult:resultArray];
    return resultArray;
}

/// 查询所有记录

- (NSArray *)searchAll{
    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:db_user];
    
    NSError *error = nil;
    NSArray *resultArray = [self.managedObjectContext executeFetchRequest:fetchRequest error:&error];
    [self printSearchResult:resultArray];
    return resultArray;
}

- (void)printSearchResult:(NSArray *)resultArray{
    NSLog(@"查询结果%lu条",(unsigned long)resultArray.count);
    for (User *user in resultArray) {
        NSLog(@"userId=%@,name=%@", user.userId, user.name);
    }
}

- (void)saveContext:(NSManagedObjectContext *)context {
    if (!context) {
        return;
    }
    NSError *error = nil;
    // 判断MOC监听的MO对象是否有改变,如果有则提交保存
    if ([context hasChanges] && ![context save:&error]) {
        // log error
        NSAssert(YES, @"save error!!!");
    }
    
    if (context.parentContext) {
        // 递归保存
        [self saveContext:context.parentContext];
    }
}

/// 更新指定记录

- (void)updateWithID:(NSString *)idString params:(NSDictionary *)params{
    // 1.获取MOC
    NSManagedObjectContext *context = self.managedObjectContext;
    
    // 2.创建查询请求
    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:db_user];
    NSPredicate *predicate = [NSPredicate predicateWithFormat:[NSString stringWithFormat:@"%@ == %@",db_user_userId ,idString]];
    [fetchRequest setPredicate:predicate];
    
    // 3.执行查询方法,返回结果
    NSError *error = nil;
    NSArray *resultArray = [context executeFetchRequest:fetchRequest error:&error];
    if (error) {
        return;
    }
    
    // 4.更新MO
    for (User *user in resultArray) {
        user.name = params[db_user_name];
    }
    
    // 5.提交修改
    [self saveContext:context];
}

#pragma mark - getter

- (NSManagedObjectContext *)managedObjectContext{
    if (!_managedObjectContext) {
        _managedObjectContext = [self getContext];
    }
    return _managedObjectContext;
}

- (NSManagedObjectContext *)getContext {
    NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"Tesl" withExtension:@"momd"];
    //根据模型文件创建模型对象
    NSManagedObjectModel *model = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
    
    
    //2、创建持久化助理
    //利用模型对象创建助理对象
    NSPersistentStoreCoordinator *store = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model];
    
    //数据库的名称和路径
    NSString *docStr = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
    NSString *sqlPath = [docStr stringByAppendingPathComponent:@"Tesl.sqlite"];
    NSLog(@"path = %@", sqlPath);
    NSURL *sqlUrl = [NSURL fileURLWithPath:sqlPath];
    
    //设置数据库相关信息
    [store addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:sqlUrl options:nil error:nil];
    
    //3、创建上下文
    NSManagedObjectContext *context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
    
    //关联持久化助理
    [context setPersistentStoreCoordinator:store];
    return context;
}
@end

第三步:在CoreDataMainViewController中调用

#import "CoreDataMainViewController.h"
#import "CRUD.h"

@interface CoreDataMainViewController ()
@property (nonatomic,strong) CRUD * crud;
@end

@implementation CoreDataMainViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
}

#pragma mark - action

- (IBAction)insertAction:(id)sender {
    [self.crud insertWithUser:@{db_user_name:@"zhangsan",db_user_userId:@"1"}];
}

- (IBAction)insertBatchAction:(id)sender {
    NSArray * params = @[@{db_user_name:@"zhangsan",db_user_userId:@"1"},
                         @{db_user_name:@"lisi",db_user_userId:@"2"}
                         ];
    [self.crud insertWithUserArr:params];
}

- (IBAction)delAction:(id)sender {
    [self.crud deleteWithID:@"1"];
}

- (IBAction)delBatchAction:(id)sender {
    [self.crud deleteAll];
}

- (IBAction)searchAction:(id)sender {
    [self.crud searchWithID:@"2"];
}

- (IBAction)searchAllAction:(id)sender {
    [self.crud searchAll];
}

- (IBAction)modityAction:(id)sender {
    [self.crud updateWithID:@"1" params:@{db_user_name:@"wangwu"}];
}

@end

  在CoreDataMainViewController只有几个点击事件和几个方法的调用,加起来也就50几行。回到刚才的问题,我需要把CoreData的增删改查改成MR的增删改查。

我做了以下步骤:
1.引入第三方库pod 'MagicalRecord', '~> 2.3.2'
2.新建MagicalRecordCRUD,然后实现CRUDProtocol协议

#import 
#import "CRUDProtocol.h"
#import "CRUDHeader.h"
NS_ASSUME_NONNULL_BEGIN

@interface MagicalRecordCRUD : NSObject

@end

NS_ASSUME_NONNULL_END

#import "MagicalRecordCRUD.h"
#import 
#import "User+CoreDataClass.h"

@implementation MagicalRecordCRUD

- (void)insertWithUser:(NSDictionary *)param{
    NSString *userID    = param[db_user_userId];
    NSString *userName  = param[db_user_name];
    
    // localContext -> rootSavingContext
    [MagicalRecord saveWithBlock:^(NSManagedObjectContext * _Nonnull localContext) {
        User *user = [NSEntityDescription insertNewObjectForEntityForName:db_user inManagedObjectContext:localContext];
        user.userId = userID;
        user.name = userName;
    } completion:^(BOOL contextDidSave, NSError * _Nullable error) {
        
    }];
}

- (void)insertWithUserArr:(NSArray *)arr{
    
}

- (NSArray *)searchAll{
    NSArray *resultArray = [User MR_findAll];
    return resultArray;
}

- (NSArray *)searchWithID:(NSString *)idString{
    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"%@ == %@",db_user_userId, idString];
    NSArray *resultArray = [User MR_findAllWithPredicate:predicate];
    return resultArray;

}

- (void)updateWithID:(NSString *)idString params:(NSDictionary *)params{
    [MagicalRecord saveWithBlock:^(NSManagedObjectContext * _Nonnull localContext) {
        NSPredicate *predicate  = [NSPredicate predicateWithFormat:[NSString stringWithFormat:@"%@ == %@",db_user_userId, idString]];
        NSArray *resultArray    = [User MR_findAllWithPredicate:predicate inContext:localContext];
        for (User *user in resultArray) {
            user.name = params[db_user_name];
        }
    } completion:^(BOOL contextDidSave, NSError * _Nullable error) {
        
    }];
}

- (void)deleteWithID:(NSString *)idString{
    [MagicalRecord saveWithBlock:^(NSManagedObjectContext * _Nonnull localContext) {
        NSPredicate *predicate = [NSPredicate predicateWithFormat:@"%@ == %@",db_user_userId, idString];
        NSArray *resultArray = [User MR_findAllWithPredicate:predicate inContext:localContext];
        [localContext MR_deleteObjects:resultArray];
    } completion:^(BOOL contextDidSave, NSError * _Nullable error) {
        
    }];
}

- (void)deleteAll{
    [MagicalRecord saveWithBlock:^(NSManagedObjectContext * _Nonnull localContext) {
        NSArray *resultArray = [User MR_findAllInContext:localContext];
        [localContext MR_deleteObjects:resultArray];
    }];
}

@end

3.在CoreDataMainViewController中的调用
  我仅需要导入MagicalRecordCRUD并且替换CRUD,就实现了数据库的替换。

#import "CoreDataMainViewController.h"
#import "MagicalRecordCRUD.h"

@interface CoreDataMainViewController ()
@property (nonatomic,strong) MagicalRecordCRUD * crud;
@end

@implementation CoreDataMainViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
}

#pragma mark - action

- (IBAction)insertAction:(id)sender {
    [self.crud insertWithUser:@{db_user_name:@"zhangsan",db_user_userId:@"1"}];
}

- (IBAction)insertBatchAction:(id)sender {
    NSArray * params = @[@{db_user_name:@"zhangsan",db_user_userId:@"1"},
                         @{db_user_name:@"lisi",db_user_userId:@"2"}
                         ];
    [self.crud insertWithUserArr:params];
}

- (IBAction)delAction:(id)sender {
    [self.crud deleteWithID:@"1"];
}

- (IBAction)delBatchAction:(id)sender {
    [self.crud deleteAll];
}

- (IBAction)searchAction:(id)sender {
    [self.crud searchWithID:@"2"];
}

- (IBAction)searchAllAction:(id)sender {
    [self.crud searchAll];
}

- (IBAction)modityAction:(id)sender {
    [self.crud updateWithID:@"1" params:@{db_user_name:@"wangwu"}];
}

- (MagicalRecordCRUD *)crud{
    if (!_crud) {
        _crud = [MagicalRecordCRUD new];
    }
    return _crud;
}

@end

  @property (nonatomic,strong) MagicalRecordCRUD * crud;
这个地方我是用类实现的,其实MagicalRecordCRUD和CRUD都实现了相关协议,可以改成@property (nonatomic,strong) id crud;这也是所谓的面向接口编程。
  这样的话,我仅需要替换初始化就能实现数据库的替换:

- (id)crud{
    if (!_crud) {
        _crud = [MagicalRecordCRUD new];
    }
    return _crud;
}

总结:
1.我这篇文章属于抛砖引玉,真正想实现好的封装,经验和阅历少不了。最好能熟练使用各种设计模式,我这里推荐<<设计模式之禅>>。通俗易懂,书中每种设计模式都有例子说明。
2.如果是增加多表查询,又会怎么封装呢?其实我的代码后面有实现一个一对多的,如果两个类有共性,是不是考虑继承一个父类,共用的部分提取到父类。如果子类在共用的方法上有差异,是不是可以考虑模板模式来实现子类的刷新。
3.对于多个类似的功能,如阅读解析时可能有txt,html,epub等多种格式,可考虑策略模式替换。我这只是一个引入,封装是有目的性的,是变化的,不是一开始就考虑项目有多大,封装有多好,扩展性有多强。随着需求的变化,代码相应的也要产生变化。
4.在一个小类中,不需要任何封装;但类大了,任何一个变量都是灾难性的。例如一个阅读模块已有5000多行,让你加个弹窗,你能把变量直接写在阅读类里吗?可想阅读是多么的难易维护。这个时候对弹窗的封装是多么的有必要。
5.我经常听到身边的同事说,少用继承。如果你对代码封装很了解,也许说出这话来,理解的很深刻;如果你只是听别人说或网上看到的,知其然不知其所以然,这话给你的误解太深了。既然语言支持继承,为什么我们不用呢?当然要恰当运用,不要乱用。如果一个工程里都继承自BaseViewController,里面有很多代码,那么这是灾难性的,BaseViewController有谁敢去修改呢。
6.有任何问题欢迎留言交流。

其他封装文章:
iOS 模板模式封装shell 解压、拷贝、删除命令
iOS责任链设计模式实现逐个弹窗

你可能感兴趣的:(iOS 谈谈代码的封装)