面对对象,不再迷茫

这篇文章写于2015年4月6日,学习面对对象编程(OO)课程时所写。从原来的站点移动而来。

一、面对对象二三事

所谓面对对象,就是Object-Oriented。我觉得,OO本不该是很难的一门课。因为面对对象无外乎就是抽象,封装,继承,多态。然而最近的几次作业,我们发现,事情并不是想象中的那么简单。一个慢慢变得智能的电梯,想把它做好确并不是那么容易。殊不知,做好那八字口诀,也需要自己的一番苦行修炼。

 

  • 抽象指的是把事物抽象成高级程序语言中类的过程。

    • 那么类是什么呢?以C++为例,类实际上是特殊的结构体(struct)。对于结构体而言,里面存放的多为变量(其实也可以存放函数),而且具有外部可访问性。而类是结构体的一种拓展,既可以有属性变量,也可以有方法,而且可以单独控制每个属性和方法的对外访问性。
    • 而抽象的过程是一个模块化的过程。把要实现的功能根据属性和功能划分为一个个类,从而凸显出软件的结构与层次,提高软件的可读性和可维护性。一个好的抽象方式,会让程序更加易于维护,减少自己犯错的可能。
    • 我的抽象思考方法:

      1.  数据应该由谁储存?输入输出的数据可能是字符串的形式,那么,考虑到要实现的功能,字符串是否能够方便进行数据处理呢?如果答案是否定的,就可以用一个数据类来储存这个字符串中所包含的信息。注意,此时的我们类如同一个C语言里的结构体。在构造方法中我们可以进行对类的实例进行初始化。如果有多个这样的实例,我们要进行储存,你可以新建一个列表类来将它们存放于其中。如果用Java库中的ArrayLIst能直接满足你的需要,也可以直接使用。
      
      1. 数据需要进行什么操作?比如比较,获取值,改变值,重新变成字符串等等。那么这些方法应当就在数据类中实现。通过这种方式,数据类中就储存了数据,也具有处理单个数据和处理多个数据的方法。同理,列表类如果需要实现特定的排序,也应当写在列表类中。这么做,让类具有了完备性,涉及到该类的操作可以直接在类中完成,这样的设计才算得上合理。
      2. 对整个程序的功能进行划分。划分可以有多种方式,可以是按照指令执行的先后顺序进行分阶段,比如流水线工作的每一级可以划分为一个类;也可以是按照行为划分,完成某种行为的都归为一类。这么做之后,几个大类就分出来了。
      3. 对每个类的功能进行评估,对功能复杂的类进行分拆,那么就会得到许多功能更明确的类。这样每个类的功能并不复杂,而且更加专一,从而避免了出现庞大的类的情况。
      4. 分析类与类之间的依赖关系,避免出现一个类需要知道很多类,避免类与类之间形成强耦合关系,这样才更利于维护与调试。
         
  • 封装指的是将属性私有化,提供公有的方法访问私有属性。

    • 显然当我们不想让别的类修改当前类中的属性时,我们可以把属性声明为private,通过get()方法来获取值。但是如果我们想让别的类修改当前类中的属性时,为什么也需要声明为private呢?直接用public直接访问就好了嘛?其实不然。用统一的接口来访问属性是为了防止属性被莫名修改而不知道原因。通过统一的接口,我们可以知道,是哪个类的哪个方法调用了get()或者set(),从而便于管理与调试。
    • 所以,在一般情况下,类的属性应当声明为private或者protected,并根据需要设置相应的get()和set()方法。
       
  • 继承是从原有的类中派生出新的类,在已有的行为和属性上扩展出新的行为。

    • 继承的一个显而易见的功能就是,两个类的属性和方法基本重合,为了防止复制粘贴大段的代码,可以直接继承,减少了代码量,使代码更加简洁。
    • 然而这并不意味着一旦有相同的方法,你就需要用到继承。继承的重点有两个,一是重用,二是扩展。如果子类不能是对父类进行扩展,甚至是减少了父类的功能,这样的继承就不是很有必要,而且这是一种不好的设计风格。
    • 所以,使用继承关系时,两个类应当一个为另一个的扩展。如果两个类相似而又非常不同,可以让它们继承同一个抽象类。例如圆形和三角形都继承于形状类。
       
  • 多态是父类型的引用可以指向子类型的对象。

    • 我们知道继承扩展了类,即子类比父类更丰富。那么对父类型的引用可以指向子类也应当是合理的,因为子类中有父类的所有属性和方法(可能被重写但依然存在)。
    • 多态增加了程序的可扩展性。比如用篮子放水果,对于不同的水果,又不需要用不同的篮子。就让各种水果继承水果抽象类,篮子类中放置水果类的对象就可以了。这样当水果变化的时候,就不需要修改代码。

二、 弄崩这个程序!

在OO课中,另一个收获就是安全的程序设计。我觉得对于OIer而言,正缺少这种训练。信息学竞赛题目的输入往往是符合题目中所给的格式的。而现实中用户的输入却往往是不可测的,那么对于程序员来说,保持程序的正常工作是非常重要的。因为用户不希望看到的是程序的崩溃,我们也必须尽可能地让程序能显示错误信息或者提示信息。

在这么几次的训练当中,我收获了一些经验:

  1. 每当使用条件语句的时候,尽量考虑处理每一个分支的情况。特别是看似无法达到的分支,一定要加入出错提示。
  2. 在使用数组索引时,都要检查索引是否越界。
  3. 如果一个对象可能为null,则必须在调用该对象方法前检查其是否为null,以免出错。
  4. 必要时候给可能出问题的语句加入try{}catch(Exception e){}