ios业务模块间互相跳转的解耦方案

*此文章需有一点runtime的知识,假设你不了解runtime,《高速理解Runtime of Objective-C》:

http://mp.weixin.qq.com/s?__biz=MzIxNDI0OTAzOQ==&mid=403005635&idx=1&sn=71375cb0dee51487c90087d488ff59fe#rd

问题:


一个app通常由很多个模块组成,全部模块之间免不了会相互调用,比如一个读书管理软件,可能会有书架、用户信息、图书详情等等模块,从用户信息-我读的书中,能够打开图书详情。而在图书详情-所在书架,又能够打开书架。

一般这样的需求我们可能会这实现:

/*用户信息模块*/
#import "UserViewController.h"
#import "BookDetailViewController.h"

@implementation UserViewController
//跳转到图书详情
+ (void)gotoBookDetail {
    BookDetailViewController *detailVC = [[BookDetailViewController alloc] initWithBookId:self.bookId];
    [self.navigationController.pushViewController:detailVC animated:YES];
}
@end

项目初期还好,速度快,够简单。可是项目发展到一定程度时,问题就来了,每一个模块都离不开其它模块,互相依赖粘在一起:

图1:模块依赖关系。箭头方向表示依赖,比方:Discover依赖BookDetail

解决方式:

遇到这样的情况,最直接的方法就是添加一个中间件,各个模块跳转通过中间件来管理。这样。全部模块仅仅依赖这个中间件。可是中间件怎么去调用其它模块那?好吧,中间件又会依赖全部模块。

好像除了添加代码的复杂度,并没有真正解决不论什么问题。

引入中间件的代码:

/*用户信息模块*/
#import "UserViewController.h"
#import "Mediator.h”

@implementation UserViewController
//跳转到图书详情
+ (void)gotoBookDetail {
    [Mediator gotoBookDetail:self.bookId];
}
@end

/*中间件*/
#import “Mediator.h”
#import “BookDetailViewController.h"

@implementation Mediator
//跳转到图书详情
+ (void)gotoBookDetail:(NSString *)bookid {
    BookDetailViewController *detailVC = [[BookDetailViewController alloc] initWithBookId:bookId];
    [self.navigationController.pushViewController:detailVC animated:YES];
}
@end

引入中间件的依赖关系:

图2:引入中间件的依赖关系

有没有一种方法,能够完美的解决这个依赖关系那?我们希望做到:每一个模块之间互相不依赖,而且每一个模块能够脱离project由不同的人编写、单独编译调试。以下的方案通过对中间件的改造,非常好的攻克了这个问题,解决后的模块间依赖关系例如以下:

图3: 比較理想的模块间依赖关系

实现方法:

我们通过一个实际的样例来分析一下。

请先下载demo并打开:https://github.com/zcsoft/ZC_CTMediator

先看一下文件夹结构,对整个project的组织结构有一个大致的了解,然后结合后面的结构图和每一个类的说明、以及project代码,来详细分析详细实现:

文件夹结构:

[CTMediatorproject文件夹]

|-[CTMediator]

|        |-CTMediator.h.m

|        |-[Categories]

|                |-[ModuleA]

|                        |-CTMediator+CTMediatorModuleAActions.h.m

|-[DemoModule]

|        |-[Actions]

|        |       |-Target_A.h.m

|        |-DemoModuleADetailViewController.h.m

|-AppDelegate.h.m

|-ViewController.h.m

说明:

[CTMediator]

负责跳转的中间件,全部模块间跳转都通过这个模块来完毕。

[DemoModule]

一个样例模块。假设我们要从其它业务(ViewController.h.m)中跳转到这个业务模块。

在这个demo中,我们的目的是从其它业务(ViewController.h.m中)跳转到DemoModule业务模块。

全部模块的引用关系如图:

图4:demo中个模块的引用关系

因为demo中仅仅是从ViewController.h.m中跳转到DemoModule模块。所以仅仅须要ViewController.h.m依赖CTMediator,CTMediator到DemoModule模块的调用是使用执行时完毕了(图片中的蓝线),在代码中不须要相护依赖。也就是说。假设一个模块不须要跳转到其它模块。就不须要依赖CTMediator。

执行时的时序:

图5:隐藏了模块内实现细节的引用关系

调用关系概述:

首先由ViewController.h.m发起调用请求给CTMediator。CTMediator通过runtime去调用目标模块DemoModule,目标模块DemoModule依据參数创建自己的一个实例。并把这个实例返回给CTMediator。CTMediator在把这个实例返回给ViewController.h.m(此时ViewController.h.m不须要知道这个实例的详细类型,仅仅须要知道是UIViewController的子类),然后由ViewController.h.m决定以什么样的方式去展示DemoModule。

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" width="544" height="425" />

图6: 完整的调用关系

调用关系详细解释:

1: ViewController.m发起调用请求给CTMediator(CTMediator+CTMediatorModuleAActions.m)。

ViewController.m-57行

UIViewController *viewController = [[CTMediator sharedInstance] CTMediator_viewControllerForDetail];

2: CTMediator+CTMediatorModuleAActions.m通过定义好的參数调用CTMediator。因为CTMediator+CTMediatorModuleAActions是CTMediator的扩展,所以能够直接使用self来调用CTMediator的实现。CTMediator+CTMediatorModuleAActions.m-行23

UIViewController *viewController =
        [self performTarget:kCTMediatorTargetA
                     action:kCTMediatorActionNativFetchDetailViewController
                     params:@{@"key":@"value"}];

3: CTMediator依据CTMediator+CTMediatorModuleAActions.m传过来的目标和參数发起实际调用。这个调用关系是在执行时完毕的。所以此处并不须要在代码上依赖被调用者。假设被调用者不存在,也能够在执行时进行处理。CTMediator.m-93行

return [target performSelector:action withObject:params];

4/5: Target_A创建一个DemoModuleADetailViewController类型的实例(这个实例是Target_A通过DemoModuleADetailViewController类的alloc/init创建的)。Target_A.m-20行

DemoModuleADetailViewController *viewController = [[DemoModuleADetailViewController alloc] init];

6: Target_A返回创建的实例到CTMediator.m(发起时是通过runtime,步骤3),返回后CTMediator.m并不知道这个实例的详细类型,也不会对这个类进行不论什么解析操作,所以CTMediator.m跟返回的实例之间是没有不论什么引用关系的。

Target_A.m-23行

7: CTMediator.m返回步骤6中得到的实例到CTMediator+CTMediatorModuleAActions.m(发起时是步骤2)。CTMediator.m-93行

8: CTMediator+CTMediatorModuleAActions.m会将步骤7返回的实例当作UIViewController处理。接下来会在执行时推断这个实例的类型是不是UIViewController(是不是UIViewController的子类)。然后将得到的UIViewController交给调用者ViewController.m(由ViewController.m负责以何种方式进行展示)。

CTMediator+CTMediatorModuleAActions.m-行29

全部类的功能例如以下:

CTMediator.h.m

功能:指定目标(target。类名)+动作(action,方法名)。并提供一个字典类型的參数。CTMediator.h.m会推断target-action能否够调用,假设能够。则调用。因为这一功能是通过runtime动态实现的,所以在CTMediator.h.m的实现中。不会依赖不论什么其它模块,也不须要知道target-action的详细功能,仅仅要target-action存在。就会被执行(target-action详细的功能由DemoModule自己负责)。

CTMediator.h里实际提供了两个方法,分别处理url方式的调用和target-action方式的调用。当中,假设使用url方式,会自己主动把url转换成target-action。

CTMediator+CTMediatorModuleAActions.h.m

功能:CTMediator的扩展,用于管理跳转到DemoModule模块的动作。

其它模块想要跳转到DemoModule模块时。通过调用这个类的方法来实现。

可是这个类中。并不真正去做跳转的动作,它仅仅是对CTMediator.h.m类的封装,这样用户就不须要关心使用CTMediator.h.m跳转到DemoModule模块时详细须要的target名称和action名称了。

‘CTMediator.h.m’+‘CTMediator+CTMediatorModuleAActions.h.m’共同组成了一个面相DemoModule的跳转,而且它不会在代码上依赖DemoModule,DemoModule是否提供了对应的跳转功能,仅仅体如今执行时能否够正常跳转。至此,CTMediator这个中间层实现了全然的独立,其它模块不须要预先注冊。CTMediator也不须要知道其它模块的实现细节。唯一的关联就是须要在‘CTMediator+CTMediatorModuleAActions.h.m’中写明正确的target+action和正确的參数,而且这些action和參数仅仅依赖于Target_A.h.m。action和參数的正确性仅仅会在执行时检查。假设target或action不存在。能够在‘CTMediator.h.m’中进行对应的处理。

既:CTMediator不须要依赖不论什么模块就能够编译执行。

Target_A.h.m

提供了跳转到DemoModule模块的对外接口,与CTMediator+CTMediatorModuleAActions.h.m相互对应,能够说它仅仅用来为CTMediator+CTMediatorModuleAActions.h.m提供服务,所以在实现CTMediator+CTMediatorModuleAActions.h.m时仅仅须要參考Target_A.h.m就可以,足够简单以至于并不须要文档来辅助描写叙述。其它模块想跳转到这个模块时,不能直接通过Target_A.h.m实现。而是要通过CTMediator+CTMediatorModuleAActions.h.m来完毕。

这样,就实现了模块间相互不依赖,而且仅仅有须要跳转到其它模块的地方,才须要依赖CTMediator。

DemoModuleADetailViewController.h.m

DemoModule模块的主视图,这个样例中。会从ViewController.h.m跳转到这个模块。

AppDelegate.h.m

APP入口,从应用外通过Scheme跳入程序时会经过这个类。

ViewController.h.m

APP主视图,须要在这里跳转到DemoModule模块。

@转载请包涵以下全部信息

參考资料:

1: 方案来自:http://casatwy.com/iOS-Modulization.html?hmsr=toutiao.io&utm_medium=toutiao.io&utm_source=toutiao.io

感兴趣的前往阅读原文。

2: 本文同一时候也參考了:http://blog.cnbang.net/tech/3080/

欢迎大家关注我:iDevShare

或加我微信:lofocus

时间: 2024-10-20 22:37:14

ios业务模块间互相跳转的解耦方案的相关文章

IOS应用程序间相互跳转 与 传值

阅读本文之前建议参考:<Communicating with Other App程序间通信[翻译]> 在IOS中,实现在app1中打开app2,方法如下: 1,在app2中注册一个URL Schemes 注册的方法很简单,在XXX-info.plist文件中新增一个URL types数组元素,取一个名字,比如"tekuba_net".或者在工程的-TARGETS-Info,中找到URL Types添加一个URL Schemes,名为"tekuba_net"

iOS之应用间的跳转(无第三方参与)

一:URL:统一资源定位符(可以没有路径,但必须有协议头) 通常默认情况下应用程序是没有URL的.并且在iOS中打开一个应用程序只要拿到该应用的协议头即可. 一个应用程序可以在info->URL Type中设置一个URL(协议头),进行协议头配置. 打开其他应用方法代码如下: 具体步骤: 1.获取到对应程序的URL 2.判断程序中是否安装了对应的应用程序,方法是canOpenURL 3.如果是->打开应用程序 代码如下: 二:如何打开/跳转的应用中判断打开的是应用的哪个页面(比如朋友圈或者好友

iOS开发,应用间的跳转

预习:URL由两部分组成-- 1.scheme:协议头(http://  ftp:// 等等) 2.path:路径(URL中path可以没有) 一.简单实现跳转到指定APP(也就是说跳转到的APP必须有自己的URL) -- 测试APP跳转到wechatAPP 设置APP的URL 在测试APP中点击按钮实现跳转到微信APP                                                                          二.实现跳转到制定页面(控制器

IOS APP与APP和APP与Safari间的跳转

最近做项目遇到这么一个需求:从APP 跳转到 Safari 然后再跳回APP .以前做过从APP 跳到Safari ,很简单,这里就不多说了.重点说一下从Safari跳回到APP,以及从APP跳到APP.(其实这两个几乎是一回事.) 首先从Safari跳到APP 既然要想跳到你指定的APP,那么就需要在你的APP中定义一个特殊的标示,也就是一个URL协议. 定义URL协议的如下图  TARGETS --> info -->URL Types-->添加一个URL协议 如图 其中URL Sc

iOS应用程序开发之应用间的跳转

简介 配置和实现 判断应用启动方式 一.简介 最实际项目开发中,我们难免会遇到需要从一个应用跳转到另一个应用的情况.比如微信分享,实际就是一种应用间的跳转.但是有时候我们需要实现自己的两个应用间的跳转,以便可以推广我们其他的应用.这个时候我们需要使用UIApplication的openURL:的方法 二.配置 1??注册自定义的URL 首先被启动的应用需要向iPhone注册一个自定义URL协议.这是在你的项目文件夹的info.plist文件进行的 1. 右键,选择“Add Row”, Key值选

应用间的跳转

应用间的跳转 标签(空格分隔): ios进阶 在手机app中经常需要在应用间跳转,比如微信分享,支付宝付款,第三方登陆等. 简单的例子 这些都是很常见的应用,要实现这样的功能需要在应用程序中设置 URL Schemes(目标应用程序) 然后在程序中写如下代码就可以跳转了 - (IBAction)weixing { NSURL *url = [NSURL URLWithString:@"weixin://"]; if ([[UIApplication sharedApplication]

零基础学python-16.5 模块间全局变量的修改以及其他访问全局变量的方法

这一章节我们来讨论一下python里面模块间全局变量的修改以及其他访问全局变量的方法 1.模块间全局变量的修改 x=1#全局变量,其实就是模块里面所有的函数都可以使用 def test(): print(x) if __name__=='__main__': test() 上面的代码保存为test.py import test if __name__=='__main__': test.x=2 test.test() print(test.x) 上面的代码保存为test2.py 我们运行test

不同视图间的跳转方式

从一个视图控制器切换到另一个视图控制器的几种方式 1,模态(modal)画面的显示方法: 例如iphone通讯录管理程序中,追加新的通讯纪录时,就是使用这种模态画面 例:点击一个按钮,进入另一个界面 - (void)goForward { ModalViewController * modalView = [[ModalViewControlleralloc]init]; modalView.modalTransitionStyle = UIModalTransitionStyleFlipHor

关于项目模块间交互的一个设计思路

1.背景 某API项目,项目天然地按业务分为了不同的包,那么每个包都独立处理自己的业务逻辑,独立接管数据源,独立地向外部提供数据,彼此基本互不通信. 不过,随着需求的增多和业务的堆积,项目的复杂度越来越大,但是每个独立模块却又不足以独立出去成为一个单独的项目,而模块间又因业务需要开始发生交互的时候,问题来了. 2.问题描述 由于模块间的数据交互,按照Spring的套路,是可以直接把容器接管的对象注入到你需要的地方去的,那么一旦你开始问其他模块要数据,开始要命了.如上图,如果模块3问模块2和模块4