美文网首页
面向对象(四)

面向对象(四)

作者: 凯玲之恋 | 来源:发表于2020-04-07 22:46 被阅读0次

    总以为把所有代码都塞到类里,自然就是在进行面向对象编程了。实际上,这样的认识是不正确的。

    1 哪些代码设计看似是面向对象,实际是面向过程的?

    1.1 滥用 getter、setter 方法

    为什么要每个属性都定义 getter、setter 方法的时候,他们的理由一般是,为了以后可能会用到,现在事先定义好,类用起来就更加方便,而且即便用不到这些 getter、setter 方法,定义上它们也无伤大雅。

    它违反了面向对象编程的封装特性,相当于将面向对象编程风格退化成了面向过程编程风格。

    面向对象封装的定义是:通过访问权限控制,隐藏内部数据,外部仅能通过类提供的有限的接口访问、修改内部数据。

    所以,暴露不应该暴露的 setter 方法,明显违反了面向对象的封装特性。数据没有访问权限控制,任何代码都可以随意修改它,代码就退化成了面向过程编程风格的了。

    getter 方法,返回的是一个 List集合容器。外部调用者在拿到这个容器之后,是可以操作容器内部数据的,也就是说,外部代码还是能修改 items 中的数据。

    可以通过 Java 提供的 Collections.unmodifiableList() 方法,让 getter 方法返回一个不可被修改的 UnmodifiableList 集合容器,而这个容器类重写了 List 容器中跟修改数据相关的方法,比如 add()、clear() 等方法。一旦我们调用这些修改数据的方法,代码就会抛出 UnsupportedOperationException 异常,这样就避免了容器中的数据被修改。

    
    public class ShoppingCart {
      // ...省略其他代码...
      public List<ShoppingCartItem> getItems() {
        return Collections.unmodifiableList(this.items);
      }
    }
    
    public class UnmodifiableList<E> extends UnmodifiableCollection<E>
                              implements List<E> {
      public boolean add(E e) {
        throw new UnsupportedOperationException();
      }
      public void clear() {
        throw new UnsupportedOperationException();
      }
      // ...省略其他代码...
    }
    
    ShoppingCart cart = new ShoppingCart();
    List<ShoppingCartItem> items = cart.getItems();
    items.clear();//抛出UnsupportedOperationException异常
    

    1.2 滥用全局变量和全局方法

    在面向对象编程中,常见的全局变量有单例类对象、静态成员变量、常量等,常见的全局方法有静态方法。

    静态方法一般用来操作静态变量或者外部数据

    静态方法将方法与数据分离,破坏了封装特性,是典型的面向过程风格。

    我们把程序中所有用到的常量,都集中地放到这个 Constants 类中。

    过,定义一个如此大而全的 Constants 类,并不是一种很好的设计思路。

    • 首先,这样的设计会影响代码的可维护性。
      查找修改某个常量也会变得比较费时,而且还会增加提交代码冲突的概率。
    • 次,这样的设计还会增加代码的编译时间。
      当 Constants 类中包含很多常量定义的时候,依赖这个类的代码就会很多。那每次修改 Constants 类,都会导致依赖它的类文件重新编译,因此会浪费很多不必要的编译时间。
    • 最后,这样的设计还会影响代码的复用性。

    如果我们要在另一个项目中,复用本项目开发的某个类,而这个类又依赖 Constants 类。即便这个类只依赖 Constants 类中的一小部分常量,我们仍然需要把整个 Constants 类也一并引入,也就引入了很多无关的常量到新的项目中。

    • 那如何改进 Constants 类的设计呢?我这里有两种思路可以借鉴。

    第一种是将 Constants 类拆解为功能更加单一的多个类,比如跟 MySQL 配置相关的常量,我们放到 MysqlConstants 类中;跟 Redis 配置相关的常量,我们放到 RedisConstants 类中。

    • Utils 类
      实际上,只包含静态方法不包含任何属性的 Utils 类,是彻彻底底的面向过程的编程风格。

    ,要尽量避免滥用,不要不加思考地随意去定义 Utils 类。

    1.3 定义数据和方法分离的类

    数据定义在一个类中,方法定义在另一个类中。

    一般情况下,VO、BO、Entity 中只会定义数据,不会定义方法,所有操作这些数据的业务逻辑都定义在对应的 Controller 类、Service 类、Repository 类中。

    2 在面向对象编程中,为什么容易写出面向过程风格的代码?

    在生活中,你去完成一个任务,你一般都会思考,应该先做什么、后做什么,如何一步一步地顺序执行一系列操作,最后完成整个任务。

    面向过程编程风格恰恰符合人的这种流程化思维方式。

    面向对象编程风格正好相反。它是一种自底向上的思考方式。它不是先去按照执行流程来分解任务,而是将任务翻译成一个一个的小的模块(也就是类),设计类之间的交互,最后按照流程将类组装起来,完成整个任务。

    这样的思考路径比较适合复杂程序的开发,但并不是特别符合人类的思考习惯。

    3 面向过程编程及面向过程编程语言就真的无用武之地了吗?

    一个数据处理相关的代码,以算法为主,数据为辅,那脚本式的面向过程的编程风格就更适合一些。

    面向过程编程是面向对象编程的基础,面向对象编程离不开基础的面向过程编程。

    面向对象和面向过程两种编程风格,也并不是非黑即白、完全对立的。

    不管使用面向过程还是面向对象哪种风格来写代码,我们最终的目的还是写出易维护、易读、易复用、易扩展的高质量代码。

    参考

    07 | 理论四:哪些代码设计看似是面向对象,实际是面向过程的?

    相关文章

      网友评论

          本文标题:面向对象(四)

          本文链接:https://www.haomeiwen.com/subject/asiophtx.html