前言

在最初学习Java的时分,咱们都听到过一句话,Java是面向对象言语。每当说到面向对象的时分,许多开发者也嗤之以鼻:都什么年代了,谁还不知道面向对象。

重学规划形式后,请回答,你真的面向对象了吗?

重学设计模式——你真的面向对象了吗?

你真的了解面向对象吗

一般状况下,咱们会将面向对象的特性分为四大特性,分别是:封装、笼统、承继、多态。以这四大特性作为代码规划标准的编程风格咱们一般称之为面向对象编程。

咱们都知道Java言语是面向对象言语,那么用Java言语完成的代码便是面向对象编程吗?答案是否定的。在了解这个原因之前,首先咱们需求需求知道面向对象四大特性分别能够处理什么问题。

封装

封装特性说白了便是数据拜访约束或许叫数据拜访保护,这一特性需求依靠言语自身具有拜访权限机制。比方在Java中 运用private、public、protect等修饰符修复变量来控制变量读、写的权限控制,这一点是最简略被开发者忽略也是开发者最不在意或许简略运用过错的一点。这一点咱们后续会详细解说。

笼统

笼统特性主要用来躲藏办法的详细完成。也有一种说法将上面说到的四大特性中的笼统这一特性排除在外,这是由于函数自身便是一种笼统,函数内部包含详细的完成逻辑对调用者来说是不需求关注详细完成办法的。在Java言语中除了函数自身,通常运用interface接口和abstract笼统关键字来完成,笼统更像是一种理论辅导,许多代码规划准则都是基于笼统理论来完成的。

举个详细的比方,在Android开发中咱们常常会运用到地图事务,以运用百度地图为例,开发者或许为了模块的通用性,会界说一系列的接口,代码如下所示:

publicinterfaceBaiduMapApi{
/**
*加载地图
*/
voidloadBaiduMap();
/**
*毁掉地图
*/
voiddestoryBaiduMap();
}

按照笼统特性和代码规划准则来说,其实这套规划是有些瑕疵的。笼统要将详细完成躲藏起来,假如以后事务中的百度地图更改成了高德地图,那么这一套接口命名规划就会产生歧义。并且或许会为后人埋坑。

较为合理的规划代码如下所示:

publicinterfaceMapApi{
/**
*加载地图
*/
voidloadMap();
/**
*毁掉地图
*/
voiddestoryMap();
}

这样一来,接口的规划遵循了笼统准则,更便于开发者后续的扩展和保护。

承继

承继用来表示类之间is-a的联系,比方:猫是动物、狗是动物,动物都会吃饭、睡觉,咱们则会创建一个动物类,代码如下所示:

publicclassAnimal{
privatevoideat(){
System.out.println("--eat--");
}
privatevoidsleep(){
System.out.println("--eat---");
}
}

然后再创建两个子类承继自Animal类,代码如下所示:

publicclassBridextendsAnimal{
@Override
publicvoideat(){
super.eat();
}
@Override
publicvoidsleep(){
super.sleep();
}
}
publicclassDogextendsAnimal{
@Override
publicvoideat(){
super.eat();
}
@Override
publicvoidsleep(){
super.sleep();
}
}

承继的最大优点便是完成代码复用,Java言语中一个类是无法承继多个父类的,那么原因是什么呢?这是由于承继多个问题会出现”钻石问题“,感兴趣的可自行了解,这里不做过多解说了。

承继尽管能够完成代码复用,可是过度运用承继会导致嵌套过深,代码难以阅览和保护,所以在规划准则中也会说组合办法优于承继。

多态

接着来看最终一个特性:多态。多态是许多规划形式和规划准则完成的基础,比方常用的策略形式和里式替换准则等。简略的说,多态便是子类能够替换父类,举个比方:

比方在事务中,需求供给一个办法完成设备信息打印功用,设备中类有A、B等多种,代码如下所以:

publicclassPrintUtil{
privatevoidprint(Aa){
}
privatevoidprint(Bb){
}
}

按照一般完成办法,每增加一种设备类型,都需求在PrintUtil新增一个打印办法,且逻辑都在PrintUtil类中使得难以扩展和保护。依靠多态的特性,咱们能够这样来完成,首先界说一个接口,代码如下所示:

publicinterfacePrintInterface{
voidprint();
}

使A、B类都承继PrintInterface接口,代码如下所示:

publicclassAimplementsPrintInterface{
@Override
publicvoidprint(){
System.out.println("-A设备的打印-");
}
}
publicclassBimplementsPrintInterface{
@Override
publicvoidprint(){
System.out.println("-B设备的打印-");
}
}

修正PrintUtil类中的办法如下所示:

publicclassPrintUtil{
publicvoidprint(PrintInterfaceprintInterface){
printInterface.print();
}
}

需求打印设备信息时,可直接选用如下办法:

publicstaticvoidmain(String[]args){
PrintUtilprintUtil=newPrintUtil();
Aa=newA();
printUtil.print(a);
Bb=newB();
printUtil.print(b);
}

这样,当增加一种设备时,咱们只需求将设备类承继自PrintInterface接口,并在类内部完成自己的打印规则即可,不需求改动PrintUtil中的代码,提高了代码的可扩展性。

了解了面向对象的四大特性后,接着来看你真的面向对象了吗?

重学设计模式——你真的面向对象了吗?
重学设计模式——你真的面向对象了吗?

你真的面向对象了吗?

与面向对象并列的是面向进程,很多时分,咱们运用面向对象言语写出来的代码或许都是面向进程的,但假如想让项目中彻底没有面向进程风格的代码,这一点是非常不切实际的。但了解过错的运用办法能够辅导咱们在以后的编码进程中写出更易理解、更易扩展的代码。

正确规划各种Util东西类

Util东西类

在Android开发中,相信每个每个项目中都有一推Util东西类,这一些东西类也常被咱们认为是好用的轮子,比方常常规划的UserUtil、FileUtil、DeviceUtil,用来在不同类之间调用相同的办法。假如一个Util东西类中仅有若干静态办法没有任何特点,那么这个东西类咱们彻底能够称之为是面向进程的。

在规划东西类的时分,咱们要尽量坚持”单一责任“准则,比方一个DeviceUtil中界说了各种获取设备参数的办法也界说了和文件有关的办法,那么这个类就没有遵循单一责任准则,所以咱们要尽量避免规划大而全的东西类,要按照实际功用,让类的责任尽或许的坚持单一。

Config装备文件

除了Util东西类之外,Config文件也是Android开发者常常会运用到的,在组件化的开发中,咱们会为每个模块装备路由文件,写出的代码或许如下所示:

publicclassArouteConfig{
publicStringAModuleMainActivity="A/MainActivity";
publicStringAModuleSetActivity="A/SetActivity";
publicStringBModuleMainActivity="B/MainActivity";
publicStringBModuleSetActivity="B/SetActivity";
}

ArouteConfig类中界说了A、B等module的路由装备变量,这样规划在功用完成中是彻底没问题的,可是设想一下,一来 组件化的目的便是为了模块解耦开发,不同模块的负责人都会修正这个装备文件,很有或许导致抵触和难以保护,二来 假如另一个项目中同样用到了B module,这个时分咱们会把B moudle和ArouteConfig类迁移到另一个项目中,如此一来,ArouteConfig中便界说了许多冗余的变量且不契合单一责任准则。

所以在规划中,咱们能够考虑将装备文件拆分更细粒,分别新建AMoudleArouteConfig与BModuleArouteConfig,这样对应模块的负责人只需保护对应模块的路由装备不会导致抵触,也提高了类规划的内聚性和代码的复用性。

不要盲目的界说各种装备文件

对Android开发工程师而言,咱们或许会比较排斥 将一些静态变量界说在Activity中,都会直接抽取一个装备文件,写在装备文件中,假如这些静态变量仅在某一个Activity中运用到了,那彻底没有必要独自界说一个装备文件的,假如你确认需求,那就尽快去界说吧!只需合适项目需求即可。

反思运用GsonFormat随意生成get、set办法

Android开发工程师或Java开发工程师常常会运用编辑器中复写办法,给所有的变量生成get、set办法,尤其是Android开发工程师,拿到后台回来的json数据后,直接运用GsonFormat生成对应的实体类,几乎不要太爽~

比方,服务器回来用户数据结构如下所示:

{
"userName":"HuangLinqing",
"age":27,
"birthday":"819561600"
}

重学设计模式——你真的面向对象了吗?

运用GsonFormat或编辑器快捷键自动生成的实体类如下所示:

publicclassUser{
privateStringuserName;
privateintage;
privateStringbirthday;
publicStringgetUserName(){
returnuserName;
}
publicvoidsetUserName(StringuserName){
this.userName=userName;
}
publicintgetAge(){
returnage;
}
publicvoidsetAge(intage){
this.age=age;
}
publicStringgetBirthday(){
returnbirthday;
}
publicvoidsetBirthday(Stringbirthday){
this.birthday=birthday;
}
}

一般状况下,这样编写也不会有什么问题。但细心来看,这段代码显然违反了面向对象中的封装特性,这是由于出生日期、和年纪是相关联的,而出生日期和年纪都露出了set办法,假如某个开发的同事在运用过错的状况调用了setBirthday办法,会导致经过出生日期计算的年纪和回来年纪不符的状况。所以正确的做法是,假如给出生日期供给了对外设置的办法,那么年纪就不应该对外露出设置的办法,且要自动计算,修正后的代码如下所示:

publicclassUser{
privateStringuserName;
privateintage;
privateStringbirthday;
publicStringgetUserName(){
returnuserName;
}
publicvoidsetUserName(StringuserName){
this.userName=userName;
}
publicintgetAge(){
returnage;
}
publicStringgetBirthday(){
returnbirthday;
}
publicvoidsetBirthday(Stringbirthday){
this.birthday=birthday;
age=(当时时刻-birthday);
}
}

我猜你必定会说,谁闲着没事会设置那个办法,咱们确保都不用不就行了吗?是的,没错,但团队间的协作标准需求用标准去衡量而不能以口头的保证作为根据,万一那个大废便是你自己呢?

重学设计模式——你真的面向对象了吗?
重学设计模式——你真的面向对象了吗?

写在最终

除了本文中所说到的,其实还有好多常常遇到却不以为意的坑。好的代码需求运用标准标准去说话,当然这里的标准只需合适你们的项目便是最好的。重学规划形式之后,请回答,你真的面向对象了吗?

重学设计模式——你真的面向对象了吗?
重学设计模式——你真的面向对象了吗?