组件化开发规范指导意见

零、背景介绍

组件化技术,对于经常开发同类型需求的开发团队来说,在提高开发效率和代码维护上是一个十分有利的工具。对于相同的业务流程来说,其高效复用(不改动或少量改动)的前提主要有两个纬度:

  • 1.所有的组件拥有相同的基础库
  • 2.编码风格的一致化

这里面基础库的一致性是较为根本和重要的,他可以让我们的组件在启用时,只需要添加组件的依赖就可以引入组件,而不用关心,由于依赖导致的报错。这让我们的精力可以更聚焦于具体的业务。

而编码风格的统一,无论是在开发阶段,还是打包阶段,都能有效帮我们规避掉很多烦人的小问题。

风格统一的好处

  1. 举一反三,看完一个组件,即可仿照此组件的模式进行规范化开发和维护
  2. 由于风格的统一,能有规律的找到承载某个业务的类文件
  3. 解决由于引入某个第三方库版本不一致导致的冲突
  4. 不同组件里资源文件不用担心被覆盖
  5. 避免APP壳工程在打包时AndroidManifest.xml合并发生错误

一、Module命名

Module作为组件化过程的中基本元素,这里主要根据Module的性质进行命名上的规范,方便快速区分和找到对应业务。

这里在介绍一下组件池这个概念,组件池的意思就是组件资源的一个汇总。一个团队随着组件化开发的深入,组件必然越来越多,大多数项目可能都存在登录组件,此时显然module_login这种命名已经不再合适了,所以这里需要在区分组件的基础上,再区分项目,这就是兼容了组件池的命名。

组件性质建议名称示例
基础组件lib_project_业务lib_base(自定义的通用工具类与项目无关) lib_hawk_router(下沉的接口和数据对象和项目有关)
通用标准业务逻辑mp_project_业务mp_hawk_login (标准的Model和Presenter层,封装标准通用逻辑)
业务组件module_project_业务module_hawk_main
对外暴露接口interface_project_业务interface_hawk_main

对于基础组件的管理,lib_base需要考虑的是对底层依赖的统一如控制上层组件使用统一的AndroidX依赖,和其他的标准库(如部门内自己开发的标准库),这里建议lib_base和lib_router不产生依赖关系,虽然说在router依赖了base后我们在组件内可以少写一个依赖,但是这样让router有了可以使用工具类的机会,为了避免router引入逻辑的可能,需要上层组件分别依赖lib_base与lib_router。

1.1使用lib_router的形式进行管理

在这里插入图片描述
因为使用统一底层的形式对开放的数据对象和服务进行统一管理,那么此处可以使用
组件名称{bean,event,services}的形式进行划分。

1.2使用Jar Library的形式

组件化对外暴露业务接口,常见的形式一般就是将此部分接口和数据对象做下沉,下沉至lib_router层,那么对于上层组件来说,这些对外服务就都是可见的了,不过这种形式有两个缺点:

  • 1.暴露的接口和数据对象可以被上层的每个组件调用,这样无法对调用者产生控制。
  • 2.都下沉至router,间接的导致了底层的膨胀

所以为了解决以上问题,保证对外暴露业务的独立性和私有性而推出 Jar Library 形式的解决方案。这里我们使用的是Jar Library的形式创建Module,这里面主要存放对外开放的业务接口,和接口涉及的基础数据对象。此部分由纯Java组成,只有接口和数据Bean。此部分Build之后会生成.jar这个就是交付给其他组件的对外暴露接口。

当其他组件需要你的服务的时候,只需要申请.jar就可以了,由于只有申请的组件才能拿到被申请组件的能力,这样就可以达到控制目的了。

申请通过的记录,那么出问题时只需要在记录的范围内分析;不通过的不能使用,达到了控制的目的。

如果使用了EventBus,那么Event也属于对外开放的对象。

二、包命名

组件内的包按照Module名进行命名避免打包时产生冲突。
其命名公式为::com.项目名称.组件名称

包所在为止命名规则示例
业务组件com.project.***com.project.login
对外暴露接口com.project.interfaces.***com.project.interfaces.login

注:interface为关键字不能出现在包名中,所以此处使用interfaces!

组件内,对于对开开放接口的实现类建议进行统一命名放置在service中。

如:package com.telephone.login.service;

在这里插入图片描述

三、类命名

3.1组件内类命名

组件内类命名在Google推荐的命名方式下,应该尽量体现此类的归属性。

示例
ApplicationLoginApplication
ActivityLoginAuthCodeActivity
FragmentLoginAuthCodeFragment
AdapterLoginAuthCodeAdapter

这里应该尽量避免抽象命名。以LoginAuthCodeActivity举例,一个应用中可能存在不止一个验证码页面,如果直接使用AuthCodeActivity的形式,虽然由于组件间包名不同的原因,在打包时不会报错,但在我们全局查找的时候就会出现多个AuthCodeActivity的类,这样对于判断类所处的组件就需要查看处于类名末尾的包名,这样显然不够直观高效。而具体业务命名可以直接快速的锁定业务类。

组件化中,组件内一般会存在对外暴露接口的实现,以及接入其他组件服务的接口管理类。

3.2组件内通有服务规范

这里对于实现类建议命名为:对外接口名称+Impl
如登录的服务接口实现:

public interface LoginService {}

@ServiceProvider
public class LoginServiceImpl implements LoginService 

对于接入的接口管理类建议命名:对外接口名称+Manager

如登录组件需要设置组件的功能:

public enum LoginServiceManager {

    INSTANCE;

    private static final String TAG = "LoginServiceManager";
    
    private Context context;
    
    private MainService mainService;

    public void init(Context context) {
    
        this.context = context;

        mainService = AppJoint.service(MainService.class);

    }

    public MainService getMainService() {
        return mainService;
    }

}

类的开头部分,应尽量保证存在注释,注释应该体现

  • 类的编写时间
  • 类的作者
  • 类的大致功能,如果涉及核心业务类,应该有设计概述。

此处只作为建议。

/**
* Create Time :   2020-5-5 11:11:11
* Author      :   XXX
* Describe    :   登录业务,主要包含登录时登录信息验证和跳转注册页等业务。
*/

四、资源命名

由于Android Studio对于上下层依赖Module中的同名资源是不做冲突处理的,所以此处同样建议使用组件名称对资源名称进行归属,避免上下级同名资源被覆盖。

资源类型示例
layout文件login_activity_quicklogin.xml、login_activity_register.xml
anim文件login_slide_in.xml
mipmap文件login_btn_submit.png
string文件< string name=“login_submit”>提交< /string>

另外对于良好设计的应用UI一般会给出主体颜色,和标准字体大小,以及对应的标准色。此部分建议下沉至lib_base中使用如上的命名方法命名,以此来统一应用整体风格。
在这里插入图片描述
另外我们也可能存在,一个组件内部是一个独立的UI显示风格,那么此时建议将独立的UI风格设计移回组件内部。

五、版本号依赖统一管理

随着组件化的推进组件的数量会逐渐增减,此时如果使用一个组件一个build.gradle的形式对组件进行管理,显然是低效,且容易出错的。

此处给出通用配置gradle的模版。

文件名称:config.gradle
存放位置:Project的最外层

ext {
    //true 每个业务Module可以单独开发
    //false 每个业务Module以lib的方式运行
    //修改之后需要Sync方可生效
    isModule = false
    //版本号
    versions = [

            compileSdkVersion    : 29,
            buildToolsVersion    : "29.0.3",

            applicationId        : "com.xjl.moudlecriterion",        //应用ID
            minSdkVersion        : 21,
            targetSdkVersion     : 29,
            versionCode          : 1,                   //版本号
            versionName          : "0.0.1",            //版本名称

            javaSDKVersion       : 1.8,//javaSDK版本

            androidxVersion      : "1.1.0",
            multidexVersion      : "1.0.3",

            appjointVersion      : "1.7",
            butterknifeVersion   : "10.2.1",
            eventbusVersion      : "3.2.0",
            gsonVersion          : "2.8.6",
            glideVersion         : "4.11.0",
            javaxVersion         : "1.2",
            pickerViewVersion    : "4.1.9",
            rx3JavaVersion       : "3.0.0",
            rx3AndroidVersion    : "3.0.0",

    ]
    dependencies = [
            "appcompat"           : "androidx.appcompat:appcompat:${versions["androidxVersion"]}",
            //方法数超过65535解决方法64K MultiDex分包方法
            "multidex"            : "com.android.support:multidex:${versions["multidexVersion"]}",
            //组件化框架appjoint
            "appjoint"            : "io.github.prototypez:app-joint:${versions["appjointVersion"]}",
            "appjoint_core"       : "io.github.prototypez:app-joint-core:${versions["appjointVersion"]}",
            //黄油刀
            "butterknife"         : "com.jakewharton:butterknife:${versions["butterknifeVersion"]}",
            "butterknife_compiler": "com.jakewharton:butterknife-compiler:${versions["butterknifeVersion"]}",
            //事件订阅
            "eventbus"            : "org.greenrobot:eventbus:${versions["eventbusVersion"]}",
            "gson"                : "com.google.code.gson:gson:${versions["gsonVersion"]}",
            //图片加载
            "glide"               : "com.github.bumptech.glide:glide:${versions["glideVersion"]}",
            //时间,地址,条件选择器
            "pickerView"          : "com.contrarywind:Android-PickerView:${versions["pickerViewVersion"]}",
            //RX家族
            "rx3_java"            : "io.reactivex.rxjava3:rxjava:${versions["rx3JavaVersion"]}",
            "rx3_android"         : "io.reactivex.rxjava3:rxandroid:${versions["rx3AndroidVersion"]}",
    ]

}

此模版由Groovy语言编写,主要使用的是List,Map数据结构。
如果感觉读起来吃力可以看这篇帖子:https://blog.csdn.net/u010451990/article/details/105382861

注:新开发的项目不再推荐使用28以下的SDK统一推荐切换到28以上,将依赖的Android库更换为AndroidX!
切换至AndroidX之后的继承

子类父类
Activityandroidx.fragment.app.FragmentActivity
Fragmentandroidx.fragment.app.Fragment
Adapterandroidx.recyclerview.widget.RecyclerView.Adapter

六、依赖控制

Gradle 3.0开始,使用 implementation, api, runtimeOnly, compileOnly 进行依赖控制。

使用规范:
implementation: 用于组件范围内的依赖,不与其他组件共享。(作用域是组件内,建议)
api: 用于基础层的依赖,要穿透基础层,暴露给组件层。(作用域是所有,不建议)
runtimeOnly: 用于 app 宿主壳的依赖,组件间是绝对隔离的,编译时不可见,但参与打包。(无作用域,建议)
compileOnly: 用于高频依赖,防止 already present 错误。一般是开源库用来依赖 support 库防止与客户的 support 库版本冲突的。

七、数据操作

数据库模块建议在各个组件内独立实现,尽量不要做下沉处理。一个组件拥有一个独立的数据库,库内可以创建相关的表。当我们使用注解类型的数据库时,应该有意识的将含有注解的数据对象和原生数据对象区分开,即有注解的对象只做数据库交互操作,而UI刷新,数据回调,组件内外应该统一到无注解的原生数据对象上(不含有注解)。

所在位置示例
所在包com.project.module.db
实体com.project.module.db.bean
daocom.project.module.db.dao

其他规范可以参考:Auligelite 进行开发

八、组件版本控制

应用层使用gradle.properties统一管理版本

初始版本为:0.0.1,不要以1.0.0开始
测试版本格式:0.0.1-SNAPSHOT(SNAPSHOT 为快照版本)
正式版本格式:0.0.1

对于组件自身,无论是开发中业务的多少,还是考虑到后期可能的经常变动,和应用层使用一个版本号都是不合适的,此时需要组件自身维护自身版本。

九、关于Maven发布

9.1发布的好处

一个组件相对稳定后,把该组件打包成 aar,发布到 github 或 maven,然后将本地Module依赖更换成远程依赖。

  1. 可大幅减少编译时间
  2. 可以降低运存占用
  3. 修改后更方后的复用
  4. 为平台化做好准备

9.2Artifactory 发布

目前部门使用的是Artifactory进行组件的管理。
发布流程如下

9.2.1添加项目插件

在项目的最外层的build.gradle中添加:
在这里插入图片描述

注:classpath "org.jfrog.buildinfo:build-info-extractor-gradle:4.15.2"

jfrog的版本号和build.gradle的版本号是匹配的,如果升级修改gradle之后发现发布失败,需要对jfrog做升级或者降级处理。

9.2.2Module添加发布task

在module的build.gradle中追加如下代码(不需要做插入,直接复制粘贴到之前的module配置下面即可),进行发布。

参数请换成自己的。

 打包发布配置开始 
apply plugin: 'com.jfrog.artifactory'
apply plugin: 'maven-publish'

def MAVEN_LOCAL_PATH ='http://10.110.16.26:8082/artifactory'
def ARTIFACT_ID = 'project_login'
def VERSION_NAME = '0.0.1'
def GROUP_ID = 'com.project.login'

publishing {
    publications {
        aar(MavenPublication) {
            groupId GROUP_ID
            version = VERSION_NAME
            artifactId ARTIFACT_ID
            // Tell maven to prepare the generated "*.aar" file for publishing
            artifact("$buildDir/outputs/aar/${project.getName()}-release.aar")
        }
    }
}

artifactory {
    contextUrl = MAVEN_LOCAL_PATH
    publish {
        repository {
            repoKey = 'libs-release-local'
            username = ""
            password = ""
        }
        defaults {
            publications('aar')
            publishArtifacts = true

            properties = ['qa.level': 'basic', 'dev.team': 'core']
            publishPom = true
        }
    }
}
 打包发布配置结束 

9.2.3发布

在这里插入图片描述
点开需要发布module的build按照数字步骤操作即可。

十、组件升级

组件的升级应该遵从:开放关闭原则

即我们对外开放的方法有改变时,不建议直接进行修改。而是通过重载或者新建方法的方式来实现。
如:

public void login(String username,String password);

增加参数时:

public void login(String username,String password,String token);

对于废弃的方法应该给出注解并给出取消原因

@Deprecated
public void login(String username,String password);

十一、组件加载顺序

如果组件间存在先后加载顺序依赖,如组件A加载的基础是组件B先加载,那么此时需要明确组件的优先级。
对于组件的优先级建议使用区间形式定义,如10000、20000、30000
方便优先级变化时,修改一个组件的优先级就可以了。尽量不要简单的使用1、2、3、4这种形式。
另外建议将此部分也写入到统一配置文件当中,方便在调整优先级或划分优先级的时候产生问题。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
Vue组件开发规范是基于Web Components的思想而来的。在Vue中,组件开发可以帮助我们更好地组织和重用代码。 Vue的组件注册有两种方式:全局组件注册和局部组件注册。 全局组件注册语法是通过Vue.component(组件名称, {data:组件数据, template:组件模板内容})来实现的。这样注册的组件可以在整个应用程序中被使用。 局部组件注册可以在一个父组件中注册子组件,只在父组件的作用域内可见。这样可以避免组件之间的命名冲突。 在组件开发中,我们希望尽可能多地重用代码,并且避免组件之间的冲突。因此,编写自定义组件的方式并不容易,需要考虑到HTML、CSS和JS的各个方面。 总结起来,Vue组件开发规范遵循了Web Components的思想,通过全局组件注册和局部组件注册来实现组件的重用和代码的组织。这样可以提高代码的可维护性和可复用性。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [Vue系列之组件](https://blog.csdn.net/weixin_62277266/article/details/128213687)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值