代码整洁之道读书笔记——第六章:对象和数据结构

it2024-12-08  13

第六章 对象和数据结构

6.1 数据抽象

作者通过一个例子说明了暴露实现和隐藏实现

一种是类中包含public修饰的变量,另一种是抽象接口

不愿暴露细节,更愿意以抽象形态表述数据。不要傻乐着乱加取值器和插值器

6.2 数据、对象的反对称性

这一小节说了一个问题,就是过程式代码和面向对象式代码,其实我对此也是深有感触

我们就说一个实战问题,系统中有3个角色:学生、老师、校长

有很多界面,因为角色的不用需要展示不同的view,或者说有不同的功能

如果我们用过程式代码来写:

private void showXXXView() { if (user instance Student) { } else if (user instance Teacher) { } else if (user instance SchoolMaster) { } }

其实大部分人都会选择这么写,if/else嘛,直接干就完了,但是if/else可能会有很多具体实现的代码,十分臃肿

那么如果新加的界面还需要判断身份,那我们继续在新的页面写if/else,感觉就可以吧,反正已经习惯了

当我们的项目已经达到一定的规模了,可能有好几百个甚至上千个Activity了,此时需求变了,需要再加入两种角色,比如教导主任和学生班长,那这可好玩了,我们得去找到所有判断身份的地方,给他们再加两句else。而且部分界面教导主任的权限和老师一样,学生班长的权限和学生一样。一会这一样一会那不一样,我靠,几百上千个Activity得加废了。此刻真的是会明白了设计模式的重要性。

如果我们用面向对象来写:

public abstract class User { public abstract void showXXXView(XXX xxx); public abstract void handleXXXData(XXX xxx); ...... } public class Student extend User { public void showXXXView(XXX xxx) { } public void handleXXXData(XXX xxx) { } ...... } public class Teacher extend User { public void showXXXView(XXX xxx) { } public void handleXXXData(XXX xxx) { } ...... } public class SchoolMaster extend User { public void showXXXView(XXX xxx) { } public void handleXXXData(XXX xxx) { } ...... }

其实这么写好处多多,我们把代码中恶心的if/else完全清除掉了

如果新加的界面还需要判断身份,那我们在User基类中添加一个抽象方法,然后各个方法中再去实现此抽象方法

那么如果新加的界面还需要判断身份,那我们继续在新的页面写if/else,感觉就可以吧,反正已经习惯了

当我们的项目已经达到一定的规模了,可能有好几百个甚至上千个Activity了,此时需求变了,需要再加入两种角色,比如教导主任和学生班长,那我们只需要新建两个类,然后去继承User类,或者说教导主任还可以继承老师,学生班长可以继承学生。这样比较灵活,然后将所有抽象方法再实现一次。

这么看来新加一个角色,我们用面向对象的思想来写代码还是比较轻松的。

这两种定义的本质:它们是截然对立的

过程式代码:便于添加新函数,超级超级难以添加新类

面向对象代码:便于添加新类,稍难以添加新函数

综合来说面向对象的思想还是更适合我们去写业务

如果就拿角色的多态来说的话,我们可能也就3到5个角色,那么每添加一个函数,则需要修改3到5个类

如果用过程式代码写,倘若有200个Activity,新加一个角色,就得修改200个类,而且过程式代码将代码实现过程都写在了if/else中,超级臃肿

我们在写代码的时候一定要注意选择使用更合适的那种方式

6.3 得墨忒定律

模块不应了解它所操作对象的内部情形

得墨忒定律认为,类C的方法f只应该调用以下对象的方法:

C由f创建的对象作为参数传递给f的对象由C的实体变量持有的对象 class C { private User user = new User(); private void f(List<String> list) { a(); Info info = new Info(); info.list = list; if (user.isStudent()) { } // 禁止下列情况 // String path = info.getVideo().getUrl() } private void a() { } }

6.3.1 火车失事

那种一连串的调用是肮脏的风格,我们最好是通过声明临时的变量来做切分

如果调用的只是简单的变量,没有任何行为,那么也就不适用于得墨忒定律了

6.3.2 混杂

有的对象中既有可以执行的函数,又有公共变量,也有公共访问器或者改值器,公共访问器和改值器会将私有变量公开化,诱导外部函数以过程式程序使用数据结构的方式使用这些变量,我们应该避免创造这种结构

6.3.3 隐藏结构

大致说了一个问题,还是要避免得墨忒定律

ctx.getOptions().getScratchDir().getAbsolutePath(); 我们拿到它的绝对路径要干什么呢? 是要去创建一个FileStream 反向思维思考一下 那么我们可以选择不去获取路径,而是让ctx去创建fileStream ctx.createScratchFileStream(classFileName)

6.4 数据结构对象

最为精炼的数据结构,是一个只有公共变量没有函数的类。通常称为Bean,而且在android中通常所有的属性是用public来修饰的,正常情况下我们不要往Bean中塞进业务规则的方法,不要把Bean当作了数据结构和对象的混合体

Active Record

通常拥有一些save和find这样的方法

这个Bean中写什么东西,其实就是涉及到了领域模型中的失血、贫血、充血等模型,这里先暂时不说这个了

6.5 小结

这节说的还是比较基础的,比较好理解,下次我再来读这里的时候,我会再来写新的体会

 

 

最新回复(0)