创建抽象类不是浪费时间吗?

时间:2014-01-18 10:25:50

标签: c# oop abstract-class

我对抽象类感到困惑,我知道这不是一个与编程问题有关的问题。但这是我能找到解决方案的唯一地方。
众所周知,抽象类无法实例化,您无法在这些类中实现任何内容。我们可以派生这些类,并可以在派生类中实现。所以我的问题是:

如果我们必须在派生类中实现逻辑,我们必须在派生类中使用完整的函数和类,那么我们为什么要创建抽象类?

有人可以给我一个明确的解释吗?如果有任何好的例子,那么请告诉我

6 个答案:

答案 0 :(得分:4)

当您使用抽象类时,您在抽象类中有一些实现,但是将特定于派生类的位保留为抽象类。例如,我可以为所有形状定义一个抽象类:

abstract class Shape
{
  public int Sides{get;protected set;}
  public abstract int CalculateArea();
}

所有形状都有特定数量的边,因此可以进入一般情况(即Shape)。区域的计算取决于形状的类型(三角形,正方形等),因此如果属于派生类。但是,我希望我的类层次结构的用户能够从任何CalculateShape实例调用Shape

我如何创建派生类并覆盖CalculateLength

class Square : Shape
{
  public Square(int sideLength)
  {
    this.Sides = 4;
    this.SideLength = sideLength;
  }

  public int SideLength{get;private set;}

  public override int CalculateArea()
  {
    return this.SideLength * this.SideLength
  }
}

请注意,我没有重新实现Shape中的所有Square,我只实现了Square个特定位。

答案 1 :(得分:3)

根据MSDN

  

无法实例化抽象类。抽象类的目的是提供多个派生类可以共享的基类的公共定义。例如,类库可以定义一个抽象类,用作其许多函数的参数,并要求程序员使用该库通过创建派生类来提供自己的类实现。

以上是所有技术解释,在外行风格中,它就像创建一个蓝图,提供信息它可以做的所有事情,所有派生类将提供自己的过程如何做到这一点。
例如,我们可以考虑Driver,我们知道有许多驱动程序类型,例如auto-rickshaw drivertruck drivertractor driver等,但它们都有一个共同的分母功能{ {1}} Driving每个进程都会有所不同,因此

  

Driving:具有Driver功能的抽象类
  Driving:派生类扩展Auto-RickshawDriver

你的问题,

  

如果我们必须在派生类中实现逻辑,并且我们必须在派生类中使用完整的函数和类,那么我们为什么要创建抽象类?

仅仅因为我们想要定义一些共同的功能,每个其他类都必须遵守它的成员。一般而言,基于功能创建组,即我们可以说该类属于一个组,我们确信该组的那些常见功能将存在。

答案 2 :(得分:1)

抽象类将包含常见的不完整实现,然后可以从多个派生类中重用它们。

  • protected 其中的方法只能从派生类内部调用。
  • 抽象方法需要在派生类中实现。
  • 虚拟方法可以在派生类中进行ocerriden

在.NET框架中,您可以找到许多此类抽象类的示例,例如System.IO.Stream

使用所有这些类的代码可以使用Stream的公共公共接口来引用它们,而不必知道它使用的具体Stream实现。

答案 3 :(得分:1)

只是为了纠正一个小细节,抽象类可以是部分抽象的或完全抽象的,这取决于它们是否部分实现(在这种情况下我们通常称它们为抽象类)或根本没有实现并且没有成员变量(在这种情况下,我们通常称它们为 interfaces )。

它们是抽象类的许多用途,其中大多数意味着能够在不修改此代码的情况下扩展代码行为。

假设您想编写一个从键盘读取数据并将其写入屏幕的程序。 (我将使用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变为enumif / 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并在case中添加ReadNextChar。如果您有权访问来源,您可能会认为这是正常的代码,但如果它是一个外部库,你可能会认为这种做法很糟糕,因为你无法扩展这个精彩的拷贝库。

现在遇到抽象类。如果您将所有资源抽象为单个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的依赖关系,它们不包含有关文件,网络或用户界面的代码。它可以打包在一个库中,允许用户扩展它可以读取的源类型。

希望它很清楚。写作部分也可以这样做。

答案 4 :(得分:0)

注意这个例子:

abstract class A 
{
   // forces subclasses to implement
   abstract void method1();

   virtual void method2() 
   {
      // some common logic
      // that sublcasses can
      // modify
   }

   protected void method3()
   {
      // some common logic
   }
}

class B : A 
{
    void method1() 
    {
       // needs to be implemented
    }

    override void method2()
    {
       // optional: e.g. something added
       base.method1();
    }

}

class C : A    {
   //...
}

不,您可以在基类中指定通用逻辑,以及定义每个子类将具有的抽象方法。所以你的基类就像一个界面。您可以将method1()调用到B和C的每个实例。

所以:不!这绝对是不浪费时间; - )。

答案 5 :(得分:0)

不是,

假设你有一个基类

class Foo{
   public String getGreetingMessage(){
      return "Hello " + getName();
   }

   public abstract String getName();
}

你有一个派生类

class Bar extends Foo{
  public String getName(){
     return "MeMamo";
  }
}
class Boo extends Foo{
   public String getName(){
       return "Boo";
   }
}

在另一个类中,我从derived,

调用方法
Bar b = new Bar();
System.out.println(b.getGreetingMessage());  // Hello MeMamo.

Boo boo = new Boo();
System.out.println(boo.getGreetingMessage());  // Hello Boo;

我不需要再次编写基本方法。 假设另一个派生类的操作。