专栏名称: 知识小集
目录
相关文章推荐
曲线猎手  ·  2025年融资融券的最低利率【最低3.0%】 ·  昨天  
壹股经  ·  调整中寻机会! ·  昨天  
壹股经  ·  调整中寻机会! ·  昨天  
银行家杂志  ·  中国外贸信托卫濛濛:提升信托服务质效 ... ·  4 天前  
淘股吧  ·  DeepSeek,大消息! ·  3 天前  
淘股吧  ·  牛市二波!又要吹爆了...... ·  3 天前  
51好读  ›  专栏  ›  知识小集

Minya 分层框架实现的思考(二):构建依赖及数据传输

知识小集  · 掘金  ·  · 2018-04-13 04:50

正文

Minya 分层框架实现的思考(二):构建依赖及数据传输

上一篇我们主要从理论上讲述如何通过 转移依赖 来轻量化我们的 ViewController ,同时在 View 层和 Store 层之间传输数据。在这一篇中,我们将通过 Demo 来更清晰地描述 Minya 框架的实际操作,包括如何去构造 pipeline ,如何去构建三层对象对 pipeline 的依赖,以及数据如何通信。

Minya 分层框架实现的思考(一):依赖转移

Demo 的基本效果可查看公众号(这里无法上传视频)。

这里主要展示了两个页面,其对应的 ViewController 分别是 SearchViewController InterestingnessViewController 。后面的示例主要以这两个类为主。

由于接口是基于 Frickr 的,所以如果想运行起来,需要去 Frickr 上注册一个应用,以获取 API KEY 和密钥,并填充到 AppDelegate.m 的下面一行代码中。(另外还请自备梯子)

[[FlickrKit sharedFlickrKit] initializeWithAPIKey:@"This is your API key" sharedSecret:@"This is the secret"];

Scene

在进入主题之前,先来了解一下 Scene 对象。我们将一个页面描述为一个 Scene 对象,其代码如下:

@interface MIScene : NSObject

@property (nonatomic, copy, nonnull) NSString *viewName;             //!< view name
@property (nonatomic, copy, nonnull) NSString *controllerName;       //!< controller name
@property (nonatomic, copy, nonnull) NSString *storeName;            //!< store name

+ (instancetype _Nullable)sceneWithView:(NSString * _Nonnull)viewName controller:(NSString * _Nonnull)controllerName store:(NSString * _Nonnull)storeName;

@end

这个类很简单,只是用来组合一个页面的三层对象。这个类并不是必须的,只是为了表达清晰。注意这里三个属性的类型都是 NSString ,意味着我们将通过反射机制来创建一个 ViewController 对象及其关联的 Store View (同时也意味了更多的硬编码,我们会在后面说明)。

具体的创建过程在 MIMediator 中,代码如下所示:

- (UIViewController *)viewControllerWithScene:(MIScene *)scene context:(NSDictionary<NSString *,id> *)context callback:(MICallback)callback {
    
    Class controllerClass = NSClassFromString(scene.controllerName);
    Class storeClass = NSClassFromString(scene.storeName);
    Class viewClass = NSClassFromString(scene.viewName);
    
    id<MIStore> store = [[storeClass alloc] initWithContext:context];
    
    return [[controllerClass alloc] initWithStore:store viewClass:viewClass callback:callback];
}

MIMediator 是页面跳转的一个中介者,主要是负责横向数据流操作,在这不多解释。

因此,创建及使用一个 Scene 看起来是下面这样:

MIScene *scene = [MIScene sceneWithView:@"SearchView" controller:@"SearchViewController" store:@"SearchStore"];
    UIViewController *viewController = [[MIMediator sharedMediator] viewControllerWithScene:scene context:nil];
    UINavigationController *navigation = [[UINavigationController alloc] initWithRootViewController:viewController];

正如上一篇后面所说, ViewController View 层和 Store 层的了解仅限于一两个接口,而三层同时依赖于同一个 pipeline ,这就意味着如果两个 View 都依赖于同一个 pipeline ,那么这两个 View 可以相互替换,同理 ViewController Store 也一样。这样,我们就可以根据 pipeline 来拼装三层对象。即如果 View 调整了,但整体展示的数据还是那些,那我们的 ViewController Store 都不需要变动,在创建 Scene 时,我们换一个 View 名就行了。(当然这是一种理想状态)。

构造 pipeline

由于 View ViewController Store 都是依赖于 pipeline ,所以 pipeline 可以说是整个框架的核心。如何去构造 pipeline ,将决定整个结构的灵活性。可以从两个角度来考虑这个问题:

  1. 根据 View 层来构造 pipeline

对于一个 App 来说, View 层是和用户直接交互的,它即是用户输入数据的来源,也是业务数据的表述。采集和显示哪些数据,都需要根据 View 来确定。另外,一个复杂的 View 层可能由多个甚至多层子 View 构成,每个子 View 有不同的数据需求,所以它能更精细地去表述数据,包括页面点击这种 flag 数据。因此,我们可以根据视图的树形结构,构造一棵类似的 pipeline 树形结构。如下图所示:

图1:根据视图层级结构来构建 pipeline

这种方案还有一个好处,每一个子视图只需要依赖于其对应的 pipeline 即可,而不需要依赖于整个 pipeline

不过这种方案的问题在于,一旦 View 变更,将直接影响到 pipeline 的构造,进而可能影响 ViewController Store pipeline 属性的监听。

  1. 根据业务来构造 pipeline

这种方案是把业务相关的一组数据整合在一个 pipeline 对象里面(一个业务可能对应多个 View ),再把一个页面里面的多个 pipeline 组织成一棵 pipeline 树。

这种方式的优点是 pipeline 相对独立于 View 层,除了一些 View 相关的数据外, pipeline 不会受 View 过多的影响。缺点是这种 pipeline 对数据的表述比较粗旷, View 层可以监听到一些与其无关的数据。

在实际开发过程中,可以根据实际情况来确定使用哪种方案构造 pipeline 。构造 pipeline 的主要任务在 store 层中完成,因为这里是数据的处理中心。以 InterestingnessStore 为例,我们将 InterestingnessStore 的业务逻辑拆分到两个 store 中,每个 store 维护其自身的 pipeline ,然后在 InterestingnessStore 中构建起 pipeline 的层级结构。

InterestingnessPipeline.h

@interface InterestingnessPipeline : MIPipeline

@property (nonatomic, strong) TopImagePipeline *imagePipeline;
@property (nonatomic, strong) PhotoListPipeline *photoListPipeline;

- (void)setShowImageAtIndex:(NSUInteger)index;

@end

InterestingnessStore.m

@interface InterestingnessStore ()

@property (nonatomic, strong) InterestingnessPipeline *interestPipeline;    // Pipeline

@property (nonatomic, strong) TopImageStore *imageStore;                    // Top image store
@property (nonatomic, strong) PhotoListStore *photoStore;                   // Photo List store

@end

@implementation InterestingnessStore

// ...

- (InterestingnessPipeline *)interestPipeline {
    if (!_interestPipeline) {
        _interestPipeline = [[InterestingnessPipeline alloc] init];
        
        _interestPipeline.imagePipeline = self.imageStore.imagePipeline;
        _interestPipeline.photoListPipeline = self.photoStore.photoListPipeline;
    }
    return _interestPipeline;
}

@end

以上 pipeline 层级结构与 View 的层级结构对应,即我们采用方案一来构造 pipeline

建立依赖

store 层中根据需求构造好 pipeline 后,就需要建立 ViewController View 层对 pipeline 的依赖了,这个过程并不复杂,但是比较繁琐。这一操作主要是通过 ViewController 向上分发 pipeline 来完成的。我们再来看看 MIViewController 的实现。


@implementation MIViewController

// ...

- (void)viewDidLoad {
    [super viewDidLoad];
    
    // Set up pipeline
    [self setupPipeline:self.store.pipeline];
    [self.view setupPipeline:self.store.pipeline];
    
    // Add observers of the pipeline data.
    [self addObservers];
}

// ...

@end

- viewDidLoad 方法里面调用了两个 - setupPipeline

  • 第一个是 ViewController 自身的方法,这可以构建 ViewController pipeline 的依赖,当然如果 ViewController pipeline 没有数据需求,则子类可以不实现这个方法;
  • 第二个是 ViewController 的根视图的设置方法,这个方法将 pipeline 传递给 View 层, View 层可以根据实际需要再去设置自身的 pipeline ,以及将子 pipeline 分发各子 View ,例如 InterestingnessView 的实现:
@interface InterestingnessView ()

@property (nonatomic, strong) InterestingnessPipeline *pipeline;

@property (nonatomic, strong) TopImageView *topImageView;
@property (nonatomic, strong) PhotoListView *photoListView;

@end

#pragma mark - InterestingnessView implementation

@implementation InterestingnessView

- (instancetype)initWithFrame:(CGRect)frame {
    self = [super initWithFrame:frame];
    if (self) {
        [self addSubview:self.topImageView];
        [self addSubview:self.photoListView];
        
        // ...
    }
    return self;
}

- (void)setupPipeline:(__kindof MIPipeline *)pipeline {
    self.pipeline = pipeline;
    
    // 分发二级 pipeline
    [self.topImageView setupPipeline:self.pipeline.imagePipeline];
    [self.photoListView setupPipeline:self.pipeline.photoListPipeline];
}

// ...

@end

至此, ViewController View pipeline 的依赖关系构建完成。构建完成后,各层就只需要与 pipeline 打交道了,从 pipeline 中读取数据,或者把数据写入 pipeline 中。

数据传输

最后来看看最主要的部分:数据传输。回到最上面的 Demo ,我们在第二个页面中点击列表中的一个单元格,顶部的图片信息就跟着变化。我们来看看这种变化时如何产生的。我们先从目标视图即 TopImageView 说起,这个 View 监听了 TopImagePipeline url 属性:







请到「今天看啥」查看全文


推荐文章
壹股经  ·  调整中寻机会!
昨天
壹股经  ·  调整中寻机会!
昨天
淘股吧  ·  DeepSeek,大消息!
3 天前
淘股吧  ·  牛市二波!又要吹爆了......
3 天前
气质女人  ·  冬天衣服那么贵,不会清洗全白费!
8 年前
妙法佛音  ·  【法音梵唱】《佛在何方》
7 年前
199IT互联网数据中心  ·  论文:数据挖掘学科发展报告(附下载)
7 年前