只是为了纠正一个小细节,抽象类可以是部分抽象的或完全抽象的,这取决于它们是否部分实现(在这种情况下,我们通常称它们为抽象类) 或者根本没有实现并且没有成员变量(在这种情况下,我们通常称它们为接口)。
它们是抽象类的许多用途,其中大多数暗示着无需修改此代码即可扩展代码行为的能力。
假设您要编写一个程序,从键盘读取数据并将其写入屏幕。 (我将使用 C# 编写代码,因为它比 C++ 略短)
void Copy() {
int c;
while((c = Console.Read()) != -1) {
Console.Write(c);
}
}
现在,需求发生了变化。您希望能够从文件而不是键盘读取,具体取决于变量。
TextReader reader;
void Copy(bool readFromFile) {
int c;
while((c = ReadNextChar(readFromFile)) != -1) {
Console.Write(c);
}
}
int ReadNextChar(bool readFromFile) {
if(readFromFile) {
return reader.Read();
} else {
return Console.Read();
}
}
现在,您希望能够从网络读取。 bool 变成 enum,if/else 变成 switch/case 等等等等。
enum ReadMode { FROM_CONSOLE, FROM_FILE, FROM_NETWORK };
TextReader fileReader;
TextReader networkReader;
void Copy(ReadMode readMode) {
int c;
while((c = ReadNextChar(readMode)) != -1) {
Console.Write(c);
}
}
int ReadNextChar(ReadMode readMode) {
switch(readMode) {
case FROM_CONSOLE:
return Console.Read();
case FROM_FILE:
return reader.Read();
case FROM_NETWORK:
return networkReader.Read();
default:
return -1;
}
}
这可以继续下去。写作也可能出现同样的要求。该程序的问题在于它明确列出了您可以从文件中读取的所有方式。要从其他来源阅读,您必须修改代码(在enum ReadMode 中添加一个成员并在ReadNextChar 中添加一个case。如果您可以访问源代码,您可能认为这没问题,但如果它是外部库,您可能会认为这种做法很糟糕,因为您无法扩展这个美妙的复制库。
现在认识抽象类。如果您将所有来源抽象为一个Source 概念会怎样,因为毕竟您只需要向来源询问int。所以让我们来写这个抽象的概念。
interface Source {
int ReadNextChar();
}
以及所有三个实现:
class KeyboardSource : Source {
int ReadNextChar() {
return Console.Read();
}
}
class FileSource : Source {
TextReader reader;
FileSource(string path) {
reader = new StreamReader(path);
}
int ReadNextChar() {
return reader.Read();
}
}
class NetworkSource : Source {
TextReader reader;
NetworkSource (string url) {
reader = new StreamReader(path);
}
int ReadNextChar() {
return reader.Read();
}
}
并实现复制功能:
void Copy(Source source) {
int c;
while((c = source.ReadNextChar()) != -1) {
Console.Write(c);
}
}
现在您想添加任何源类型,您只需实现Source 的新派生,无需修改现有代码。 Copy 程序保持正确且未受影响,Source 的现有衍生程序也是如此。
这称为多态性。如果您查看Copy 的依赖关系,它们不包含有关文件、网络或用户界面的代码。它可以打包在一个库中,允许用户扩展它可以读取的源类型。
希望很清楚。写作部分也可以这样做。