刚刚学习完SpriteKit的第一章, 项目的需求就压过来了, 诶, 时间都去哪啦, 接着之前的热修复架构, 这次为了更加好的进行代码规范, 我们将之间的架构模式进行模板代码生成, 这里会用到一些简单的python, 没接触过的同学们可以先去了解下, 今天我就将零耦合代码生成工具分享与你.

参考链接:
- Hybird 搭建零耦合架构从MVC开始
 - Hybird 搭建后端Koa.js并过度到MVVM
 - Hybird 搭建前端Vue.js并升级至MVP
 - Hybird 搭建路由Router实现组件化
 - Hybird 搭建客户端实时降级架构
 
其实代码生成工具好久之前就想试试学着开发, 一直没有头绪和所谓的契机, 最近在学习算法的时候顺带学了下python, 发现python的写文件的代码之简洁, 再看看OC, 诶… 刚学iOS的时候还觉得OC是最好的语言, 说实话, 现在好久不敲OC, 都有些手生了.
先按之前的架构写出对应OC模板 没看过Swift版的点击 –> Hybird 搭建客户端实时降级架构
生成器目录结构
├── Template //模板文件
│   ├── ControllerTemplate.h
│   ├── ControllerTemplate.m
│   ├── InterfaceTemplate.h
│   ├── ModelTemplate.h
│   ├── ModelTemplate.m
│   ├── PresenterTemplate.h
│   ├── PresenterTemplate.m
│   ├── ViewModelTemplate.h
│   ├── ViewModelTemplate.m
│   ├── ViewTemplate.h
│   └── ViewTemplate.m
├── builder.py //生成器执行文件
└── config.json //生成器配置文件
InterfaceTemplate.h
#import <UIKit/UIKit.h>
@protocol <#Unit#>ViewOperation <NSObject>
<#ViewOperation#>
@end
@protocol <#Unit#>ModelInterface <NSObject>
<#ModelInterface#>
@end
@protocol <#Unit#>ViewModelInterface <NSObject>
@property (nonatomic,strong) id<<#Unit#>ModelInterface> model;
- (void)initializeWithParameter:(NSDictionary *)parameter finishedCallBack:(void(^)())finishCallBack;
<#ViewModelInterface#>
@end
@protocol <#Unit#>ViewInterface <NSObject>
@property (nonatomic,strong) id<<#Unit#>ViewModelInterface> <#unit#>ViewModel;
@property (nonatomic,strong) id<<#Unit#>ViewOperation> <#unit#>Operation;
@end
- InterfaceTemplate 是根据规则指定的一套协议, 用来进行解耦
 - ViewOperation view的用户交互方法规则
 - ModelInterface model的数据结构的规则
 - ViewModelInterface viewModel的数据获取规则
 - ViewInterface view的适配器模式
 
ModelTemplate.h
#import <Foundation/Foundation.h>
#import "<#Unit#>Interface.h"
@interface <#Unit#>Model : NSObject <<#Unit#>ModelInterface>
<#ModelInterface#>
- (instancetype)initWithDictionary:(NSDictionary *)dictionary;
+ (<#Unit#>Model *)modelWithDictionary:(NSDictionary *)dictionary;
@end
ModelTemplate.m
#import "<#Unit#>Model.h"
@implementation <#Unit#>Model
- (instancetype)initWithDictionary:(NSDictionary *)dictionary {
    
    self = [super init];
    if (self) {
        [self setValuesForKeysWithDictionary:dictionary];
    }
    return self;
}
+ (<#Unit#>Model *)modelWithDictionary:(NSDictionary *)dictionary {
    return [[<#Unit#>Model alloc]initWithDictionary:dictionary];
}
@end
- 简单的使用KVC进行映射, 也可以使用Runtime或第三方库
 
ViewModelTemplate.h
#import <Foundation/Foundation.h>
#import "<#Unit#>Interface.h"
@interface <#Unit#>ViewModel : NSObject <<#Unit#>ViewModelInterface>
@property (nonatomic,strong) id<<#Unit#>ModelInterface> model;
- (void)initializeWithParameter:(NSDictionary *)parameter finishedCallBack:(void(^)())finishCallBack;
<#ViewModelInterface#>
@end
ViewModelTemplate.m
#import "<#Unit#>ViewModel.h"
#import "<#Unit#>Model.h"
@implementation <#Unit#>ViewModel
- (void)initializeWithParameter:(NSDictionary *)parameter finishedCallBack:(void(^)())finishCallBack {
}
<#ViewModelImplementation#>
@end
- 仅新增初始化获取数据的方法, 其他的获取数据方法根据规则进行累加
 
PresenterTemplate.h
#import <UIKit/UIKit.h>
#import "<#Unit#>Interface.h"
@interface <#Unit#>Presenter : NSObject<<#Unit#>ViewOperation>
@property (nonatomic,weak) id<<#Unit#>ViewInterface> <#unit#>View;
@property (nonatomic,weak) id<<#Unit#>ViewModelInterface> <#unit#>ViewModel;
- (void)adapterWith<#Unit#>View:(id<<#Unit#>ViewInterface>)<#unit#>View <#unit#>ViewModel:(id<<#Unit#>ViewModelInterface>)<#unit#>ViewModel;
@end
PresenterTemplate.m
#import "<#Unit#>Presenter.h"
@implementation <#Unit#>Presenter
- (void)adapterWith<#Unit#>View:(id<<#Unit#>ViewInterface>)<#unit#>View <#unit#>ViewModel:(id<<#Unit#>ViewModelInterface>)<#unit#>ViewModel {
    _<#unit#>View = <#unit#>View;
    _<#unit#>ViewModel = <#unit#>ViewModel;
    [self dynamicBinding];
}
- (void)dynamicBinding {
    
    __weak typeof(self) _self = self;
    __weak id<<#Unit#>ViewModelInterface> __<#unit#>ViewModel = _<#unit#>ViewModel;
    [_<#unit#>ViewModel initializeWithParameter:nil finishedCallBack:^{
        _self.<#unit#>View.<#unit#>ViewModel = __<#unit#>ViewModel;
        _self.<#unit#>View.<#unit#>Operation = _self;
    }];
}
<#ViewOperation_m#>
@end
- 仅实现了viewModel和view之间初始化的解耦传输
 
ViewTemplate.h
#import <UIKit/UIKit.h>
#import "<#Unit#>Interface.h"
@interface <#Unit#>View : UIView <<#Unit#>ViewInterface>
@property (nonatomic,strong) id<<#Unit#>ViewOperation> <#unit#>Operation;
@property (nonatomic,strong) id<<#Unit#>ViewModelInterface> <#unit#>ViewModel;
@end
ViewTemplate.m
#import "<#Unit#>View.h"
@interface <#Unit#>View () <UITableViewDataSource, UITableViewDelegate>
@property (nonatomic,strong) UITableView * tableView;
@end
@implementation <#Unit#>View
- (void)dealloc {
    NSLog(@"%@ - execute %s",NSStringFromClass([self class]),__func__);
}
- (instancetype)initWithFrame:(CGRect)frame {
    
    self = [super initWithFrame:frame];
    if (self) {
        [self setupSubviews];
    }
    return self;
}
- (instancetype)initWithCoder:(NSCoder *)coder  {
    
    self = [super initWithCoder:coder];
    if (self) {
        [self setupSubviews];
    }
    return self;
}
- (UITableView *)tableView {
    
    if (!_tableView) {
        _tableView = [UITableView new];
        _tableView.dataSource = self;
        _tableView.delegate = self;
    }
    return _tableView;
}
- (void)setupSubviews {
    [self addSubview:self.tableView];
}
- (void)set<#Unit#>ViewModel:(id<<#Unit#>ViewModelInterface>)<#unit#>ViewModel {
    _<#unit#>ViewModel = <#unit#>ViewModel;
    [_tableView reloadData];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return 0;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    
    static NSString * identifier = @"identifier";
    UITableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:identifier];
    if (!cell) {
        cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:identifier];
    }
    return cell;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
    return 44;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    [tableView deselectRowAtIndexPath:indexPath animated:YES];
}
- (void)layoutSubviews {
    [super layoutSubviews];
    _tableView.frame = self.bounds;
}
@end
- 仅实现了一个基本的TableView, 如需更改可替换.
 
ControllerTemplate.h
#import <UIKit/UIKit.h>
#import "<#Root#>ViewController.h"
@interface <#Unit#>ViewController : <#Root#>ViewController
@end
ControllerTemplate.m
#import "<#Unit#>ViewController.h"
#import "<#Unit#>Presenter.h"
#import "<#Unit#>ViewModel.h"
#import "<#Unit#>View.h"
@interface <#Unit#>ViewController ()
@property (nonatomic,strong) <#Unit#>Presenter * <#unit#>Presenter;
@property (nonatomic,strong) <#Unit#>ViewModel * <#unit#>ViewModel;
@property (nonatomic,strong) <#Unit#>View * <#unit#>View;
@end
@implementation <#Unit#>ViewController
- (void)loadView {
    [super loadView];
}
- (void)viewDidLoad {
    [super viewDidLoad];
    [self setupView];
    [self adapterView];
}
- (<#Unit#>Presenter *)<#unit#>Presenter {
    
    if (!_<#unit#>Presenter) {
        _<#unit#>Presenter = [<#Unit#>Presenter new];
    }
    return _<#unit#>Presenter;
}
- (<#Unit#>ViewModel *)<#unit#>ViewModel {
    
    if (!_<#unit#>ViewModel) {
        _<#unit#>ViewModel = [<#Unit#>ViewModel new];
    }
    return _<#unit#>ViewModel;
}
- (<#Unit#>View *)<#unit#>View {
    
    if (!_<#unit#>View) {
        _<#unit#>View = [<#Unit#>View new];
        _<#unit#>View.frame = self.view.bounds;
    }
    return _<#unit#>View;
}
- (void)setupView {
    [self.view addSubview:self.<#unit#>View];
}
- (void)adapterView {
    [self.<#unit#>Presenter adapterWith<#Unit#>View:self.<#unit#>View <#unit#>ViewModel:self.<#unit#>ViewModel];
}
@end
- 进行所有层的配置
 
以上的内容在热修复架构的时候分别分了五篇来进行讲解, 这里仅是把Swift替换成了OC的版本.
config.json
{
	"super" : "", //父类的前缀
	"controller" : "Home", //控制器的前缀名
	"model" : { //模型的属性定义
		"models" : "Array"
	},
	"viewModel" : [ //获取数据的方法定义
		"register",
		"login",
		"exit",
		"refresh"
	],
	"presenter" : [ //用户交互的方法定义
		"pushToNextViewController",
		"popToRootViewController",
		"showAlert",
		"closeButtonClick"
	]
}
- 生成器的设计思想是通过读取一个JSON的规则, 来生成对应的一套架构模板, 便于进行快速开发和代码规范. 当然规则可以根据不同的项目制定不同的规则, 这里为了演示只做了最简单的版本.
 
生成器的实现
进行代码生成, 之前的老大是通过java进行实现了,奈何我对java很是无感, 最近在学习算法, 准备尝试数据和人工智能的方向, 就捎带学习了Python, 这里就使用Python实现OC的代码架构生成.
builder.py
import os, sys, json //导入python模块
def cur_file_dir(): //获取路径
   path = sys.path[0]
   if os.path.isdir(path):
        return path
   elif os.path.isfile(path):
        return os.path.dirname(path)
file_json = open(cur_file_dir() + "/config.json", "r") //打开JSON的配置表
data_json = json.loads(file_json.read()) //读取配置表中的数据
root = str(data_json["super"]) //获取父类的前缀
controller = str(data_json["controller"]) //获取控制器的前缀名
presenter_h = ''
presenter_m = ''
for operation in data_json["presenter"]:
   presenter_h += '\n- (void)' + str(operation) + ';'
   presenter_m += '- (void)' + str(operation) + ' {\n\n}\n\n'
view_model_h = ''
view_model_m = ''
for request in data_json["viewModel"]: //遍历获取数据的方法
   view_model_h += '- (void)' + str(request) + 'WithParameter:(NSDictionary *)parameter finishedCallBack:(void (^)())finishCallBack;\n' //进行OC版本的字符串拼接
   view_model_m += '\n- (void)' + str(request) + 'WithParameter:(NSDictionary *)parameter finishedCallBack:(void (^)())finishCallBack {\n\n}\n'
model = ''
for key in data_json["model"]: //遍历数据模型
   type = ''
   modified = ''
   value = data_json["model"][key]
   if value == 'String':
      type = 'NSString * ' 
      modified = 'copy'
   elif value == 'Int':
   	  type = 'NSInteger '
   	  modified = 'assign'
   elif value == 'Float':
   	  type = 'CGFloat '
   	  modified = 'assign'
   elif value == 'Bool':
   	  type = 'BOOL '
   	  modified = 'assign'
   elif value == 'Array':
   	  type = 'NSArray * '
   	  modified = 'strong'
   elif value == 'Dictionary':
   	  type = 'NSDictionary * '
   	  modified = 'strong'
   else:
   	  type = value
   	  modified = 'strong'
   model += '\n@property (nonatomic,' + modified + ') ' + type + str(key) + ';' //拼接数据的属性
file_json.close() //关闭JSON文件
os.path.join(cur_file_dir(), controller) //进行文件夹之间的关联
os.mkdir(cur_file_dir() + "/" + controller) //创建关联文件夹
def builder(path, file): //获取模板生成的封装
   r = open(cur_file_dir() + '/Template/' + path, 'r') //打开模板文件
   d = r.read() //读模板文件的数据
   r.close() //关闭模板文件
   w = open(cur_file_dir() + '/' + controller + '/' + controller + file, 'w') //创建目标文件
   w.write(d.replace('<#Unit#>', controller).replace('<#unit#>', controller.lower()).replace('<#Root#>', root).replace('<#ViewOperation#>', presenter_h).replace('<#ViewOperation_m#>', presenter_m).replace('<#ModelInterface#>', model).replace('<#ViewModelInterface#>', view_model_h).replace('<#ViewModelImplementation#>', view_model_m)) 进行写入文件
   w.close() //关闭文件
builder('InterfaceTemplate.h', 'Interface.h')
builder('ControllerTemplate.h', 'ViewController.h')
builder('ControllerTemplate.m', 'ViewController.m')
builder('ModelTemplate.h', 'Model.h')
builder('ModelTemplate.m', 'Model.m')
builder('PresenterTemplate.h', 'Presenter.h')
builder('PresenterTemplate.m', 'Presenter.m')
builder('ViewModelTemplate.h', 'ViewModel.h')
builder('ViewModelTemplate.m', 'ViewModel.m')
builder('ViewTemplate.h', 'View.h')
builder('ViewTemplate.m', 'View.m')
- 这样通过在终端中 python builder.py, 就能根据JSON表进行架构的生成了.
 
彩蛋:
最近看了下Realm数据库, 虽说是很简单便捷, 但是需要继承, 耦合度太高, 原先使用的FMDB需要写SQL语句, 操作繁琐, 也不能一步到位. CoreData, 一直没有尝试过, 之后会进行学习.
只进行页面的缓存, 没有读写需求的使用数据库真是太痛苦了. 还不如写plist一劳永逸. ######.h
+ (void)cache:(Class)cls data:(NSDictionary *)data;  //存
+ (void)requestDataWithClass:(Class)cls finishedCallBack:(void(^)(NSDictionary * response))finishedCallBack; //读
.m
+ (NSString *)cacheWithClass:(Class)cls {
    return [[NSHomeDirectory() stringByAppendingPathComponent:@"Documents"] stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.plist", NSStringFromClass([cls class])]];
    //返回路径
}
+ (void)cache:(Class)cls data:(NSDictionary *)data {
    [data writeToFile:[DataBase cacheWithClass:cls] atomically:YES];
    //写入沙盒
}
+ (void)requestDataWithClass:(Class)cls finishedCallBack:(void(^)(NSDictionary * response))finishedCallBack {
    finishedCallBack([NSDictionary dictionaryWithContentsOfFile:[DataBase cacheWithClass:cls]]);
    //抛出数据
}
viewModel
- (void)dynamicBindingWithFinishedCallBack:(void (^)())finishCallBack {
    [DataBase requestDataWithClass:[NSObject class] finishedCallBack:^(NSDictionary *response) {
        
        NSDictionary * data = response;
        NSArray * models = data[@"models"];
        
        [self.models removeAllObjects];
        for (NSDictionary * dict in models) {
            [self.models addObject:[ModelTemplate modelWithDictionary:dict]];
        }
        
        finishCallBack();
    }];
    
    [NetWork requestDataWithType:MethodGetType URLString:@"http://localhost:3001/api/J1/getJ1List" parameter:nil finishedCallBack:^(NSDictionary * response){
        
        NSDictionary * data = response[@"data"];
        NSArray * models = data[@"models"];
        
        [self.models removeAllObjects];
        for (NSDictionary * dict in models) {
            [self.models addObject:[ModelTemplate modelWithDictionary:dict]];
        }
        
        [DataBase cache:[NSObject class] data:data[@"models"]];
        finishCallBack();
    }];
}
- 上面是在vm层进行获取数据时的使用, 这里用NSObject作为参数, 实际用时可以缓存不同的Model类. 这样数据读取和网络读取的代码也非常的工整了.