OC

设计模式

设计模式

Posted by sunzhongliang on September 11, 2018

iOS中的MVC、MVP、MVVM以及架构设计

MVC的特点

标准版的MVC架构的特点,可以参见iOS中的UITableviewController
Controller中持有Model和View,View的职责 是负责视图的展示,Controller充当了Model和View之间通信的桥梁;

  • 优点:View、Model可以重复利用,可以独立使用
  • 缺点:Controller的代码过于臃肿(Controller给View赋值的时候)

日常开发中一般都会对标准的MVC模式进行稍微的变动,以便代码写起来更方便

  • 优点:对Controller进行瘦身,将View内部的细节封装起来了,Controller的一些职责交给View去实现了,外界不知道View内部的具体实现
  • 缺点:View依赖于Model

MVP的特点

MVP依然是三个角色,Model、View和Presenter(主持人)
本质上是将Controller的职责是交给Presenter去主持、管理。Controller只需要处理跟Presenter的关联。 示例:
AppPresenter.h 文件

@interface AppPresenter : NSObject
- (instancetype)initWithController:(UIViewController *)controller;
@end

AppPresenter.m 文件

@interface AppPresenter() <AppViewDelegate>
@property (weak, nonatomic) UIViewController *controller;
@end

@implementation AppPresenter
- (instancetype)initWithController:(UIViewController *)controller
{
    if (self = [super init]) {
        self.controller = controller;
        
        // 创建View
        AppView *appView = [[AppView alloc] init];
        appView.frame = CGRectMake(100, 100, 100, 150);
        appView.delegate = self;
        [controller.view addSubview:appView];
        
        // 加载模型数据
        AppModel *app = [[AppModel alloc] init];
        app.name = @"QQ";
        app.image = @"QQ";
        
        // 赋值数据
        appView.iconView.image = [UIImage imageNamed:app.image];
        appView.nameLabel.text = app.name;
    }
    return self;
}
#pragma mark - AppViewDelegate

- (void)appViewDidClick:(AppView *)appView
{
    NSLog(@"presenter 监听了 appView 的点击");
}

MVVM的特点

MVVM跟MVP是有一点相似,都是将Controller的工作交给了Presenter和ViewModel。不一样的地方是属性监听绑定,View监听ViewModel里的数值改变 示例:
AppViewModel.h 文件

@interface AppViewModel : NSObject
- (instancetype)initWithController:(UIViewController *)controller;
@end

AppViewModel.m 文件

@interface AppViewModel() <AppViewDelegate>
@property (weak, nonatomic) UIViewController *controller;
@property (copy, nonatomic) NSString *name;
@property (copy, nonatomic) NSString *image;
@end

@implementation AppViewModel

- (instancetype)initWithController:(UIViewController *)controller
{
    if (self = [super init]) {
        self.controller = controller;
        
        // 创建View
        AppView *appView = [[AppView alloc] init];
        appView.frame = CGRectMake(100, 100, 100, 150);
        appView.delegate = self;
        appView.viewModel = self;
        [controller.view addSubview:appView];
        
        // 加载模型数据
        AppModel *app = [[AppModel alloc] init];
        app.name = @"QQ";
        app.image = @"QQ";
        
        // 设置数据
        self.name = app.name;
        self.image = app.image;
    }
    return self;
}

#pragma mark - AppViewDelegate

- (void)appViewDidClick:(AppView *)appView
{
    NSLog(@"viewModel 监听了 appView 的点击");
}

@end

AppView.h

@class AppView, AppViewModel;

@protocol AppViewDelegate <NSObject>
@optional
- (void)appViewDidClick:(AppView *)appView;
@end

@interface AppView : UIView
@property (weak, nonatomic) AppViewModel *viewModel;
@property (weak, nonatomic) id<AppViewDelegate> delegate;
@end

AppView.m

// 引入FaceBook的FBKVOController,可以很方便进行属性的监听变化
#import "AppView.h"
#import "NSObject+FBKVOController.h"

@interface AppView()

@property (weak, nonatomic) UIImageView *iconView;
@property (weak, nonatomic) UILabel *nameLabel;
@end

@implementation AppView

- (instancetype)initWithFrame:(CGRect)frame
{
    if (self = [super initWithFrame:frame]) {
        UIImageView *iconView = [[UIImageView alloc] init];
        iconView.frame = CGRectMake(0, 0, 100, 100);
        [self addSubview:iconView];
        _iconView = iconView;
        
        UILabel *nameLabel = [[UILabel alloc] init];
        nameLabel.frame = CGRectMake(0, 100, 100, 30);
        nameLabel.textAlignment = NSTextAlignmentCenter;
        [self addSubview:nameLabel];
        _nameLabel = nameLabel;
    }
    return self;
}

- (void)setViewModel:(AppViewModel *)viewModel
{
    _viewModel = viewModel;
    
    __weak typeof(self) waekSelf = self;
    [self.KVOController observe:viewModel keyPath:@"name" options:NSKeyValueObservingOptionNew block:^(id  _Nullable observer, id  _Nonnull object, NSDictionary<NSKeyValueChangeKey,id> * _Nonnull change) {
        waekSelf.nameLabel.text = change[NSKeyValueChangeNewKey];
    }];
    
    [self.KVOController observe:viewModel keyPath:@"image" options:NSKeyValueObservingOptionNew block:^(id  _Nullable observer, id  _Nonnull object, NSDictionary<NSKeyValueChangeKey,id> * _Nonnull change) {
        waekSelf.iconView.image = [UIImage imageNamed:change[NSKeyValueChangeNewKey]];
    }];
}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    if ([self.delegate respondsToSelector:@selector(appViewDidClick:)]) {
        [self.delegate appViewDidClick:self];
    }
}

@end

设计模式(Design Pattern)

是一套被反复使用、代码设计经验的总结
使用设计模式的好处是:可重用代码、让代码更容易被他人理解、保证代码可靠性
一般与编程语言无关,是一套比较成熟的编程思想

设计模式可以分为三大类
创建型模式:对象实例化的模式,用于解耦对象的实例化过程
单例模式、工厂方法模式,等等

结构型模式:把类或对象结合在一起形成一个更大的结构
代理模式、适配器模式、组合模式、装饰模式,等等

行为型模式:类或对象之间如何交互,及划分责任和算法
观察者模式、命令模式、责任链模式,等等

浅谈架构

一个项目做到一定的程度,必然会产生混乱的情况,这时候我们要对项目进行一个划分,遵守一定的原则解决耦合性这种世纪难题,对于iOS来说,SOLID是非常适合的

  • 单一功能原则
    • 对象功能要单一,不要在一个对象里面添加太多的功能
  • 开闭原则
    • 扩展是开放的,修改是关闭的
  • 里氏替换原则
    • 所有引用基类的地方必须能透明地使用其子类的对象
  • 接口隔离原则
    • 接口的用途要单一,不要一个接口根据不同的参数实现不同的功能
  • 依赖反转原则
    • 高层模块不应该依赖低层模块,二者都应该依赖其抽象;抽象不应该依赖细节,细节应该依赖抽象

遵守以上这些原则的架构就比较容易维护,易于扩展,最后我们针对项目要根据合适的粒度进行划分,粒度过大过小都不合适,组件应该是可组装的,独立的业务单元,具有高内聚低耦合的特性。采用组件的模式来改造APP,对于代码的逻辑,模块间通信改动都不大,按照功能对模块进行划分,相同类型的放到同一个文件夹下面,使用cocoapods进行管理组件

不管做到任何程度,模块之间必然存在耦合,也不是说模块之间一点耦合都不可以有,其实做好上下依赖关系,划分好上下层关系就会易于维护和管理

本文首次发布于 孙忠良 Blog, 作者 [@sunzhongliang] , 转载请保留原文链接.