Flutter跨平台应用Android开发经验谈Android技术知识

探究一下最适合Flutter的项目架构

2019-08-19  本文已影响18人  Android高级开发

背景

Flutter作为最近很火的一个跨平台技术,以其高性能、跨平台的一系列优秀特性成功吸引了很多开发者和组织的青睐,但是由于其不同于传统Android或iOS开发的Widget机制,使得视图的代码往往冗长、不够简洁,解决这种困境的方法就是在开发中合理地运用合适的架构模式,使得程序的视图与数据分离,这样视图层的代码只用专心进行视图的描述和操作即可,不涉及过多复杂的数据操作,这样就可以使视图层的代码达到简洁。由于Flutter目前没有官方推荐的项目架构,而且笔者也未遇到大家都说好用的架构模式,故此,笔者基于MVP的架构,设计了一套我个人比较青睐的架构模式,本文将详细介绍,希望可以和大家一起沟通、探索,力争衍生出一套适合Flutter的架构模式,从而大大提高生产力,如果文中有什么地方大家觉得设计的不合理的,大可提出,我们一起讨论。

从实例看模式

本文将基于一个经典的登录/注册需求展开描述,将登录/注册的界面与逻辑分离,以求达到解耦,我们首先想想,对于一个注册/登录需求,按照传统MVP的思想,应该如何将其视图与逻辑分离,大致的流程应该是这样的:

  1. IView、IPresenter、IModel三个接口分别是View、Presenter、Model类应该实现(Implements)的接口
  2. View类中持有IPresenter类的实例,记做IPresenter(实际上是Presenter类的实例,因为Presenter类实现了IPresenter接口),当View中的登录按钮被点击时,调用iPresenter对象的登录方法
  3. Presenter类中持有IView和IModel类的实例,记做iView和iModel(实际上是View类和Model类的实例,因为View类和Model类分别实现了IView接口和IModel接口),当Presenter的登录方法被调用时,调用iModel对象的登录方法
  4. Model类持有IPresenter的实例,记做IPresenter(实际上是Presenter类的实例,因为Presenter类实现了IPresenter接口),Model类的登录方法被调用后,进行网络操作,发起登录请求,获取登录的结果(成功或失败),并调用iPresenter对象的方法将结果回调给Presenter层
  5. Presenter接收到Model的回调之后调用iView对象的回调方法,将结果继续回调给View层进而实现视图更新,整个操作完成

总结一下,IView、IPresenter、IModel三个接口分别应该包含的方法如下:

IView:登录/注册操作、结果回调操作

IPresenter:登录/注册操作、结果回调操作

IModel:登录/注册操作

可以看到,IView、IPresenter、IModel三个接口都具有登录/注册操作,而IView、IPresenter都具有结果回调操作,这样在定义接口的时候,登录/注册操作的声明会被写3遍、结果回调操作的声明会被写2遍,代码上会出现臃肿和冗余。所以我对此进行了改进,改进示意图如下:

image

将登录/注册操作这类M、V、P都应该具有的操作抽取到IFunction这个接口中,然后让IView、IPresenter、IModel这三个接口实现(implements)IFunction这个接口,将结果回调操作抽取到ICallBack这个接口中,让IView、IPresenter这两个接口实现(implements)这个接口,然后,让View实现(implements)IView接口,让Presenter实现(Implements)IPresenter这个接口,让Model实现(Implements)IModel这个接口,这样,一个登录操作的完成流程是这样的:

  1. View类持有IPresenter类的实例,记做iPresenter(实际上是Presenter类的实例,因为Presenter类实现了IPresenter接口),当View中的登录按钮被点击时,调用iPresenter对象的登录方法
  2. Presenter类中持有IView和IModel类的对象,记做iView和iModel(实际上是View类和Model类的实例,因为View类和Model类分别实现了IView接口和IModel接口),当Presenter类的登录方法被调用时,调用iModel对象的登录方法
  3. Model类持有ICallBack类的实例,记做iCallBack(实际上是Presenter类的实例,因为Presenter类实现了IPresenter接口,而IPresenter接口实现了ICallBack接口),Model类的登录方法被调用后,进行网络操作,发起登录请求,获取登录的结果(成功或失败),并调用iCallBack对象的回调方法将结果回调给iPresenter
  4. Presenter接收到Model的回调之后调用iView对象的实例,将结果继续回调给View层进而实现视图更新,整个操作完成

这样,我们使用IFunction和ICallBack分别将登录/注册操作和结果回调操作进行封装,然后使IView、IPresenter、IModel分别实现自己应该具有的功能,最后让View、Presenter、Model分别实现IView、IPresenter、IModel接口,一步一步实现了视图与数据的解耦。

代码实现

以上是理论分析部分,落实在代码中,我来大家一步一步地实现:

首先定义我们所说的登录/注册操作对应的LoginInterface接口:

enum RequestType { LOGIN, SIGNUP }
class LoginInterface {
 //这里通过RequestType区分当前操作是登录操作还是注册操作
 logInOrSignUp(String userName, String passWord, RequestType requestType) {}
}

然后来定义回调方法对应的ILoginCallBack接口:

class ILoginCallBack {
 //登录/注册成功
 logInOrSignUpSuccess(User user, String describe) {}
 //登录/注册失败
 logInOrSignUpFailed(String describe) {}
}

然后来定义IView接口,因为View应该有登录/注册、回调两个功能,因此应该让IView接口实现LoginInterface、ILoginCallBack两个接口:

class ILoginPage implements LoginInterface,ILoginCallBack {
 showProsess(bool show) {}
 @override
 logInOrSignUp(String userName, String passWord, RequestType requestType) {
 // TODO: implement logInOrSignUp
 return null;
 }
 @override
 logInOrSignUpFailed(String describe) {
 // TODO: implement logInOrSignUpFailed
 return null;
 }
 @override
 logInOrSignUpSuccess(User user, String describe) {
 // TODO: implement logInOrSignUpSuccess
 return null;
 }
}

注意,这里只是定义接口,所以方法不用实现。

定义IPresenter接口,由于Presenter应该有登录/注册、回调两个功能,因此应该让IView接口实现LoginInterface、ILoginCallBack两个接口:

class ILoginPagePresenter implements LoginInterface, ILoginCallBack {
 @override
 logInOrSignUp(String userName, String passWord, RequestType requestType) {
 // TODO: implement logInOrSignUp
 return null;
 }
 @override
 logInOrSignUpFailed(String describe) {
 // TODO: implement logInOrSignUpFailed
 return null;
 }
 @override
 logInOrSignUpSuccess(User user, String describe) {
 // TODO: implement logInOrSignUpSuccess
 return null;
 }
}

最后定义IModel接口,其只需要登录/注册一个功能即可,因此让IModel接口实现Logininterface这一个接口即可:

class ILoginModel implements LoginInterface {
 @override
 logInOrSignUp(String userName, String passWord, RequestType requestType) {
 // TODO: implement logInOrSignUp
 return null;
 }
}

然后我们来实现View、Presenter、Model三个类:

View:

class _LoginState extends State<LoginPage> implements ILoginPage{
 ILoginPagePresenter iLoginPagePresenter;
 @override
 void initState() {
 iLoginPagePresenter = LogInPresenter(this);
 super.initState();
 }
 @override
 logInOrSignUp(String userName, String passWord, RequestType requestType) {
 iLoginPagePresenter.logInOrSignUp(userName, passWord, requestType);
 }
 @override
 logInOrSignUpFailed(String describe) {
 // TODO: 登录/注册失败,更新视图
 }
 @override
 logInOrSignUpSuccess(User user, String describe) {
 // TODO: 登录/注册成功,更新视图
 }
}

Presenter:

class LogInPresenter implements ILoginPagePresenter {
 ILoginPage _iLoginPage;
 ILoginModel _iLoginModel;
 LogInPresenter(this._iLoginPage) {
 _iLoginModel = LoginModel(this);
 }
 @override
 logInOrSignUp(String userName, String passWord, RequestType requestType) {
 _iLoginModel.logInOrSignUp(userName, passWord, requestType);
 }
 @override
 logInOrSignUpFailed(String describe) {
 _iLoginPage.logInOrSignUpFailed(describe);
 }
 @override
 logInOrSignUpSuccess(User user, String describe) {
 _iLoginPage.logInOrSignUpSuccess(user, describe);
 }
}

Model:

class LoginModel implements ILoginModel {
 ILoginCallBack _iLoginCallBack;
 LoginModel(this._iLoginCallBack);
 @override
 logInOrSignUp(String userName, String passWord, RequestType requestType) {
 // TODO: 发起网络操作进行登录/注册
 _iLoginCallBack.logInOrSignUpSuccess(user, describe);
 }
}

至此我们就对登录/注册这个功能完成了架构改造,可以发现,经过架构改造之后,整个项目的条理非常清晰,避免了视图层代码太过冗余的痛点,利于维护、测试。

总结

本文针对Flutter中的登录/注册场景进行了架构改造,整个逻辑是基于MVP,解决了MVP中接口冗余的痛点,当然本架构还存在很多问题,欢迎大家及时指出,一起交流。

给大家分享一份移动架构大纲,包含了移动架构师需要掌握的所有的技术体系,大家可以对比一下自己不足或者欠缺的地方有方向的去学习提升;

需要高清架构图以及图中视频资料和文章项目源码的可以加入我的技术交流群:825106898私聊群主小姐姐免费获取

image
上一篇下一篇

猜你喜欢

热点阅读