今天我们来简单地谈一下面向对象编程的理念。
当下在软件设计领域,面向对象是一个非常热门的设计方法。很多人都知道,我们最常用的几门语言都支持面向对象:如 C++,Python 等等。甚至 Java 和 C# 本身就是完全面向对象的。
可能有不少人对对象(Object)感觉到很陌生,而入门级的 Java 程序员则对 Java 中 NullPointerException 感到无所适从。其实使用对象进行编程的理念很简单:尽量不写重复代码,尽量将一个类别的东西用一个对象包裹起来。这种理念贯彻下来就是面向对象编程。
类
类就是实现这种理念的方法。
无论在 C++ 还是在 Java,C# 中,类都是一个非常好用的结构。我们可以先找出两个概念的相同点,然后根据他们的相同点的关系来组织类。
抽象类(Abstract Class)
抽象类是一个不可以实例化,但可以在类里面写方法具体实现的类。通俗地说,就是不能被 new 出来的类。
比方说,假如我们在软件设计中需要对很多动物进行控制。那么我们就可以创造一个叫 Animal 的抽象类。此外,因为动物都需要吃食物,那么我们可以在抽象类里面创建一个抽象方法 eat。这样任何继承 Animal 的类都必须实现 eat 这个方法,同时每一个 Animal 的子类都可以有自己吃食物的方法(比如猴子喜欢吃香蕉,老虎喜欢吃肉等等)。
然后我们也可以创造 Reptile 和 Mammal 两个类继承 Animal 这个类。同时,我在 Reptile 里面创建了一个 eat 方法。由爬行动物和哺乳动物的共性,我们可以在 Reptile 里面创建 crawl 方法,在 Mammal 里面创建 walk 方法。
然后再创建 Crocodilia、Turtle 继承 Reptile 和 Human、Rabbit 继承 Mammal。这时候,由于在这段创建的四个类分别继承了 Reptile 和 Mammal,因此我们就不需要在 Crocodilia 和 Turtle 里面写 crawl 函数了。当然我们也不需要给 Human 和 Rabbit 写 walk 函数。但是由于在 Animal 中声明了抽象方法 eat,且我们只在 Reptile 里面实现了 eat 方法,而在 Mammal 里面我们并没有实现 eat 方法,所以我们必须在继承 Mammal 的类里面分别为其实现 eat 方法。
最终我们会得到一个这样的继承关系:
接口(Interface)
Java 把一个不能被实例化,且不能在里面写方法的具体实现的「类」叫做接口(Interface)。C++ 也有类似的结构,但 C++ 对 Interface 的定义会稍微复杂。C++ 必须写一个纯虚函数以声明这个类是一个「接口」。
class Interface{
public:
void pure_virtual() = 0;
};
看上去接口很没用对吧:既不能被实例化,也不能有具体的方法实现。但是其实这是因为编译器在内存中寻址所导致的问题。
以上面我们创建的动物继承关系作例子,如果我们需要在一个函数里面传入 Crocodilia,Turtle,Human 或 Rabbit 其中之一,那我们需要怎么办呢?总不可能复制四个除了参数不一样以外其他每一个方面都一样的函数吧。
答案就是我们只需要创建一个以 Animal 对象为参数的函数。同时将 Animal 的一个子类(在这里是四个具体动物的类)就可以了。但是有一个问题,继承 Animal 的类如果在里面创建了一个 Animal 没有的方法怎么办?此时在内存里面,Animal 的指针并不能控制 Animal 没有的方法。于是,接口的意义就出来了。我们可以在接口里面声明所有必须要用到的方法,因此接口的指针就可以控制所有我们需要用到方法的内存地址。
所以,我们可以总结出接口的一个功能:为编译器寻找需要函数的内存地址提供方便。