vlambda博客
学习文章列表

秀,Java代码写出了C语言的感觉

众所周知,目前有三种常见的编程范式:面向过程编程、面向对象编程和函数式编程,而面向对象编程又是这三种里面最主流的编程范式,大部分软件都是基于面向对象编程这种编程范式来开发的。

在很多程序员的认识中,觉得用Java这样的面向对象语言把所有代码写到所有类里面就是在进行面向对象编程了,但不知道大家有没有发现,有些代码的风格看起来是面向对象的代码,从本质上看却是面向过程的代码风格。下面我就举几个常见的在面向对象的语言中写出面向过程风格代码的情况。


滥用gettersetter方法



不少程序员喜欢在定义完类的属性之后,就顺手把这些属性getter、setter方法直接写上或者用IDE自动生成。我之前也是这么写的,因为在学校是这么写的,工作中不少同事也是这么写的,就没觉得有什么问题,认为以后如果用到的时候更方便,即便用不到也没有影响,后来在重新学些面向对象之后,发现这种做法实际上违反了面向对象编程的封装特性,相当于把编程风格退化成了面向对象编程的风格。

为什么这么说呢?大家可以看一下下面这个例子。

ShoppingCart {
    List<ShoppingCartItem> = ArrayList<>()() {
        .}

    (itemsCount) {
        .= itemsCount}

    () {
        .}

    (totalPrice) {
        .= totalPrice}

    List<ShoppingCartItem> () {
        .}

    (ShoppingCartItem item) {
        .add(item)+++= item.getPrice()}

ShoppingCart是一个简化后的购物车类,它有三个private属性:itemsCount、totalPrice、items,分别表示商品数量、总价和商品列表。对于itemsCount、totalPrice两个属性,我们定义了它们的 getter、setter 方法。对于 items 属性,定义了它的getter方法和 addItem()方法。

乍一看没什么问题,但是仔细思考当中的逻辑就会发现不对劲,itemsCount的值应该跟items的size对应的,totalPrice的值应该是items列表中每个商品的单价相加,也就是说itemsCount和totalPrice这两个属性值的变化要参考items,虽然把itemsCount和totalPrice定义为private属性,但是却提供了public的getter、setter方法,那么外部可以通过setter方法随意修改这两个属性的值,可能就会造成这两个值于items的值对应不上,跟把这两个属性定义为public没什么区别。

而面向对象的封装特性就是通过控制访问权限,隐藏内部的数据,外部只能通过类提供的接口访问、修改内部的数据,所以暴露不该暴露的setter方法,明显的违反了封装特性,代码便退化成了面向过程的编程风格了。

除此之外,items属性虽然没有setter方法,但是它的getter方法返回的是一个List的集合,外部在得到这个集合之后还是能修改items的数据。

所以在设计实现类的是时候应该尽量避免给属性定义setter方法,对于返回值是集合容器,也要避免内部数据被修改。


滥用全局变量和全局方法


滥用全局变量和全局方法也可能会违反面向对象的编程风格,项目中一些代码中的配置参数,一般都设置为常量,放到一个 Constants 类中,Utils类里面的方法一般都会定义成静态方法,可以在不用创建对象的情况下,直接拿来使用。但是静态方法将方法与数据分离,破坏了封装特性。对于这两种类,应该根据功能和职责尽量细化拆解成多个小类,一方面提高代码的可维护性和复用性,另一方面减少代码的编译时间。






END


以上便是两种在面向对象编程中常见的可能会造成面向过程开发的场景,虽然使用了类和对象,但是要么破坏了封装,比如滥用getter或setter,要么抽象不完全。实际的开发过程中还有其他违反面向对象编程的情况,我们在平时自己写demo练习的时候可能感觉不到这样写有什么不妥,但在具体的可实用的项目开发中,可能就会给自己或者别人留下很多坑。



长按二维码



您点的每个在看,我都认真当成了喜欢