0
点赞
收藏
分享

微信扫一扫

程序员的上帝视角(1)中国古典哲学与编程思考

凡事有道。自古至今,人们从未放弃对于道的追求。古人追求的是天地万物运行的规律,得道之人,是为圣人。我们自然不敢与先贤相比,但是,也总免不了思考一个问题——程序之道在哪里?

bug从哪里来

促使我思考程序之道的契机是review一位同事的代码。一看到代码,尽管只是粗粗浏览,但下意识里就知道,这段代码一定存在严重bug。虽然测试的场景有限性,导致测试阶段无法发现这个bug,但后续一定会爆发。

作为程序员,我们习惯于自嘲每天都在写bug。修复bug往往很容易,但是,出现bug的原因倒是什么?很明显,不能总是简单的使用“粗心大意”一类的原因来解释写出bug的原因。那么,bug,尤其是低级bug,究竟为何会频频发生?

或许我们可以从一个日常生活中的场景获得一些启发——一个二年级的小朋友在加减乘除混合运算上,可能经常犯错;而一个高中生,同样的四则混合运算,出错的几率要小的多。

高中生与小学生相比,在四则运算的知识学习上所花费的时间,并不会有太大差异。那么,二者之所以在正确率上有如此大的差异,源自对于四则运算的理解程度上。高中生所具备的、对数学的认知高度,使其在计算四则运算时,完完全全站在一个更高的层面来思考和完成计算。

于程序员而言,是否也具备这样一个高度,使得我们编写代码,最大限度的避免bug。即使能够减少10%的bug频率,对于整个程序员的庞大体量来说,所产生的价值也是非常可观的。

编程之道

其实,不止是bug,我们常常会看到庞杂的代码,一堆杂草般摆在我们面前。这也是程序员最常吐槽的地方。一旦其中出现逻辑问题,只能一点点以补丁的形式修复。我见过无数类似的代码,这种代码便如洪七公的衣服——到处是洞,还到处是补丁。

无论是低级bug,还是杂乱的代码,本质原因仍然是程序员所具备的认知高度有限,无法完全掌控代码。解决之道,便是提升自我认知。不断修炼对于技术和内在逻辑的认知。当看待问题或者工作时,可以居高临下,一览无余,尽在掌握之中。

我们大多数程序员往往执着于技术的学习。毫无疑问,无论是对于编程语言,数据库,还是架构的学习,都可以大大扩展我们的知识面。但却忽略对于技术本质的思考。

说到底,技术只是工具,技术本身没有意义。有意义的是,我们利用这些技术做出了什么结果。而编程之道,在我看来,就是开发者的境界。这种境界,大到能够洞察技术本身,洞察需求本身,洞察实现本身;小到能够一眼洞察一段代码的本质,洞察一段代码的优劣,洞察一段代码的合理和不合理之处。

儒学、理学与心学带给我们的启示

该如何求得编程之道?或许,我们可以从先贤们的学习之道获得一些启发。儒学一直是我国2000年来的正统文化。其实,儒学也一直在发展和自我更新,后来出现的程朱理学和阳明心学都是对儒学的不断更新。抛开儒学、理学和心学的历史局限性不谈。我们仅仅看看它们在学习和思考方面都有各自的哪些主张。

儒学

学而时习之,是我们最为耳熟能详的一句话。强调的是不断不断的学习,并且不停温习。我们仔细考量,就会发现,学而时习之,只能帮助我们机械式的学习知识,更多的只能是记忆型的知识。

理学

理学认为,理存在于事物之中。要领会天理,需要格物致知。今日格一物,明日格一物,直至触类旁通,最终通达天理。

作为程序员,我们当然想做到博览群书,对各种技术信手拈来。但与技术的飞速发展和浩如烟海相比,一个人的精力和能力非常有限。如同朱夫子的格物致知,想格尽天下万物,何其之难。

我们常常听到的一个观点就是,不断学习,不断丰富自己的知识,直至能举一反三,解决日常遇到的技术问题。这简直与理学的格物致知不谋而合。至于何时能学到头,学到通透,似乎是一个比较遥远的话题。

也有的程序员更专注于某一项技术,认为研究透彻了某项技术,就可以一招鲜,吃遍天。这种形式,只能算做程序员职业生涯的一种生存方式。而不是我们学习和研究技术最终的目标。这样的学习方式,仍然脱离不了增加知识的范畴。

理学所提倡的学习和思考方式,与当前大多数程序员的学习方式最为相似——不断学习新的知识。优秀者,可以对各种技术说的头头是道,擅长列举各种知识图谱,知识树等等。但往往缺乏真正的思考。

心学

心学认为,物者,事也,理并非存在于物中,而是存在于人们的心中。只有用心参与到实际的实践活动中,并时时在内心反省,用自己的良知进行鉴别,才能获得真正的天理。

心学所提倡的,用心投入,不断扪心自问,在内心深处不断探究代码和设计的本质是什么,才是最有效的学习和提升的途径。在学习过程中,对于学习到的经验和知识,不停抽象反思,直至挖掘到个人认知层面的提升。也就是在程序层面上修心。

心外无物

仍然记得在高中阶段,总是为了没有解题思路而苦恼。现在回想起来,总算有点感悟——执着于做题、刷题,却忽视了最本质的思考,为什么可以有这样的解题思路,别人是如何想到这种解题思路的。

这正是心学所提倡的,我们需要“心外无物”。所有事物的本质,并不在事物之中,而在我们自己心里。我们必须用自己心来掌控知识。学习一门知识,并不是知道其原理,而是需要体会到这样的设计、思路到底体现了怎样的思想。

关于思维方式的陷阱

我们经常在网络上看到类似“xxx的13种定级思维方式”的视频或者文章。读了之后,略有感触,但却无法实际运用。这其实是将所有情况都罗列出来,“总有一款适合你”,从而导致我们掉入选择的陷阱。

真正适用自己的思维方式,一定是自己体悟出来的

我无法认同那些“n种顶级思维方式”的说法。因为真正适合自己的,仅仅只有一种,而且,完全是个人体悟出来的。当然,这种体悟可能是在接收了大量信息后,瞬间感悟到的,

具体到技术而言,在解决某个问题,或者重构某段代码后,我往往会问自己,这到底体现了一种什么样的思想。并不断思考这个问题,试图从庞大的汉语言文化中,用两个字来概括这种思想。直至找到那个关键词。而这个关键词只有两三个字,并且能够真正直击个人心里,让自己瞬间感觉对于整个事情的认知尽在掌握之中。而且这两个字能够触动自己的思想。凭借这种方法来强迫自己的思考,不断思考,不断挖掘自己大脑中思考的深度。这是一个反复的过程,你会想到各种词语进行概括,但只有一个词能够让你满意。我想,这可能才是心学所提倡的“心外无物”。

首先要解决的问题——精确

在程序员的日常思维里,一旦说到代码划分,最容易想到的就是代码的分层。以最常见的MVC或者DDD为例,常见的代码结构划分如下:

程序员的上帝视角(1)中国古典哲学与编程思考_四则运算

无论是MVC还是DDD的概念中,代码层次是从所担负的职责来划分的。例如,Controller层负责处理控制;Service层负责处理业务逻辑;DAO层负责处理数据库操作。除了xxxContoller, xxxService,xxxDAO等类之外。常规的代码还包含了DO,DTO,VO,以及一些Convert类。

一个常见的DO类如下所示:

@Data
public class CommonDO {
    public static final String FIELD_ID = "ID";
    /**
     * 自增id
     */
    @TableId(type = IdType.AUTO)
    private Long id;
    /**
     * 创建人id
     */
    @TableField(value = "create_user_id", fill = FieldFill.INSERT)
    private Long createUserId;
    /**
     * 创建人
     */
    @TableField(value = "create_user", fill = FieldFill.INSERT)
    private String createUser;
    /**
     * 创建时间
     */
    @TableField(value = "create_time", fill = FieldFill.INSERT)
    private LocalDateTime createTime;
    /**
     * 最后修改者
     */
    @TableField(value = "update_user", fill = FieldFill.INSERT_UPDATE)
    private String updateUser;
    /**
     * 最后修改时间
     */
    @TableField(value = "update_time", fill = FieldFill.INSERT_UPDATE)
    private LocalDateTime updateTime;
}

DAO层往往用于与数据库打交道。一个典型的Repository类的示例代码如下:

@Service
public class SubjectRepositoryImpl implements SubjectRepository {
    @Resource
    private SubjectMapper subjectMapper;
    @Override
    public Long addSubject(Subject subject) {
        SubjectDO subjectDo = SubjectDoConvert.INSTANCE.dto2Do(subject);
        subjectMapper.insert(subjectDo);
        return subjectDo.getId();
    }
    @Override
    public int updateSubject(Subject subject) {
        SubjectDO subjectDo = SubjectDoConvert.INSTANCE.dto2Do(subject);
        return subjectMapper.updateById(subjectDo);
    }
    @Override
    public Subject findById(Long id) {
        SubjectDO subjectDo = subjectMapper.selectById(id);
        return SubjectDoConvert.INSTANCE.do2Dto(subjectDo);
    }    
}

很明显,DO类,往往与数据表结构一一对应;而Repository类(及其接口)也都以增删改查的操作为基础。这些代码,往往都是有一定规则的;而且不涉及复杂的业务逻辑。我们可以认为,这些代码都是模板代码。

无论采用MVC还是DDD,对于数据表的增删改查往往是绕不过去的。而对应于单个数据表所产生的类,可能会达到10个之多。在一个典型项目中,可能会出现如下Java类:

DO、DTO、VO、Service、ServiceImpl、Repository、RepositoryImpl、Mapper、Controller、DOConvert、DTOConvert

这些类的代码,是完全有规律可循的。例如,类所处的模块和包的位置、类的命名、类中的属性命名、方法的基本内容、局部变量的命名、各类之间相互的引用关系等等。严格说来,程序员必须遵循统一的标准来编写这些类。也只有遵循统一的标准,才能让所有模块的代码看起来,如同出自同一人之手。无论是后期的维护还是扩展,每位项目成员都可以做到轻车熟路。

对于这类代码,我们可以用精确来概括。一方面是说,代码的编写有迹可循,不应该出现不同风格;另外一方面也是说,它们完全是可以自动化生成的。

利用插件生成精确代码

根据当前公司使用的代码框架和所使用的IDE,开发对应的插件来生成模板代码,是目前最好的方案。利用插件来生成代码,可以无缝集成到IDE、自动将生成的代码置入对应的module,并自动完成相互引用、自动编译等动作。

程序员的上帝视角(1)中国古典哲学与编程思考_思维方式_02

程序员的上帝视角(1)中国古典哲学与编程思考_思维方式_03

程序员的上帝视角(1)中国古典哲学与编程思考_四则运算_04

以上个人开发的集成到Itellij和Eclipse编写的插件,用于生成模板代码。这样生成的代码,无需程序员关注,并且精确生成到各个子模块、包中。

从精确代码中解放出来

一旦解决了日常没有太多技术含量的模板代码,我们的注意力,便可以集中到如何提升技术认知上来了。

举报

相关推荐

0 条评论