OOP 中的接口与抽象类

作者:微信公众号:【架构师老卢】
7-12 20:48
40

概述:在面向对象编程 (OOP) 领域,开发人员经常会遇到两个关键概念:接口和抽象类。虽然两者都是设计类的蓝图,但它们表现出不同的特征,并在不同的场景中使用。让我们介绍一下接口和抽象类之间的区别,以便更清楚地理解它们在软件开发中的作用。接口OOP 中的接口是一个协定,它定义了类必须实现的一组方法。它用作行为蓝图,而不提供任何实现细节。接口支持多重继承,允许一个类实现多个接口。在接口中声明的所有方法都必须由实现接口的类实现。接口促进了关注点的明确分离,并鼓励实施特定行为,但没有具体说明应如何实现这些行为。接口支持多重继承,允许一个类实现多个接口。当一个类需要展示来自各种来源的行为时,这种灵活性是有益的

在面向对象编程 (OOP) 领域,开发人员经常会遇到两个关键概念:接口和抽象类。虽然两者都是设计类的蓝图,但它们表现出不同的特征,并在不同的场景中使用。

让我们介绍一下接口和抽象类之间的区别,以便更清楚地理解它们在软件开发中的作用。

接口

OOP 中的接口是一个协定,它定义了类必须实现的一组方法。它用作行为蓝图,而不提供任何实现细节。接口支持多重继承,允许一个类实现多个接口。

在接口中声明的所有方法都必须由实现接口的类实现。接口促进了关注点的明确分离,并鼓励实施特定行为,但没有具体说明应如何实现这些行为。

接口支持多重继承,允许一个类实现多个接口。当一个类需要展示来自各种来源的行为时,这种灵活性是有益的。

接口不能有字段或构造函数。它们只关注方法签名,不允许包含变量或构造函数定义。

下面是 C# 中的一些简单示例,用于说明接口的使用。

interface IDrawable  
{  
    void Draw();  
}  
  
interface IResizable  
{  
    void Resize(int factor);  
}  
  
class Square : IDrawable, IResizable  
{  
    public void Draw()  
    {  
        Console.WriteLine("Drawing a square");  
    }  
  
    public void Resize(int factor)  
    {  
        Console.WriteLine($"Resizing the square by a factor of {factor}");  
    }  
}  
  
class Circle : IDrawable  
{  
    public void Draw()  
    {  
        Console.WriteLine("Drawing a circle");  
    }  
}  
  
class Program  
{  
    static void Main()  
    {  
        Square square = new Square();  
        square.Draw();       // Output: Drawing a square  
        square.Resize(2);    // Output: Resizing the square by a factor of 2  
  
        Circle circle = new Circle();  
        circle.Draw();       // Output: Drawing a circle  
    }  
}

在以下情况下使用接口:

  1. **跨不相关类的多个行为:**如果你想为可能没有共同的基本实现,但需要展示多种行为的类定义一个协定,那么接口是你要走的路。它们支持多重继承。
  2. **与实施脱钩:**当您想要将类的定义与其实现详细信息分离时,请使用接口。接口提供了清晰的关注点分离,允许不同的类实现相同的方法集,而无需绑定到特定的基类。
  3. **仅强制执行方法签名:**如果您有兴趣在不提供任何实现细节的情况下为类指定协定,那么接口是理想的选择。它们定义了方法签名,但未规定应如何实现这些方法。
  4. 继承的灵活性: 当您需要允许类实现多个协定(接口)的灵活性时,与抽象类相比,接口提供了更通用的解决方案。

抽象类

另一方面,抽象类是一个不能自行实例化的类。它可能包含抽象方法(没有实现的方法)和具体的方法(有实现的方法)。抽象类支持单一继承,这意味着一个类只能从一个抽象类继承。

抽象类既可以有抽象方法,也可以有具体方法。扩展抽象类的子类是实现抽象方法所必需的,但它们也可以继承具体方法。

抽象类支持单次继承。一个类只能从一个抽象类继承,与接口相比,这限制了灵活性。

抽象类可以有字段、构造函数和方法。这允许在类中更全面地封装数据和行为。

让我们看一个 C# 中的抽象类示例:

abstract class Shape  
{  
    // Abstract method to be implemented by subclasses  
    public abstract double CalculateArea();  
  
    // Concrete method with shared implementation  
    public void DisplayArea()  
    {  
        Console.WriteLine($"Area: {CalculateArea()}");  
    }  
}  
  
class Circle : Shape  
{  
    public double Radius { get; set; }  
  
    public Circle(double radius)  
    {  
        Radius = radius;  
    }  
  
    // Implementation of the abstract method  
    public override double CalculateArea()  
    {  
        return Math.PI * Math.Pow(Radius, 2);  
    }  
}  
  
class Rectangle : Shape  
{  
    public double Length { get; set; }  
    public double Width { get; set; }  
  
    public Rectangle(double length, double width)  
    {  
        Length = length;  
        Width = width;  
    }  
  
    // Implementation of the abstract method  
    public override double CalculateArea()  
    {  
        return Length * Width;  
    }  
}  
  
class Program  
{  
    static void Main()  
    {  
        Circle circle = new Circle(5);  
        circle.DisplayArea(); // Output: Area: 78.54  
  
        Rectangle rectangle = new Rectangle(4, 6);  
        rectangle.DisplayArea(); // Output: Area: 24  
    }  
}

在以下情况下使用抽象类:

  1. 常见实现: 如果您有一组通用的方法,并且在一组相关类中有一些共享实现,请使用抽象类。这使您可以提供基本实现,同时仍强制执行由子类实现的某些方法。
  2. 代码可重用性: 抽象类支持通过继承重用代码。如果要在多个类之间共享方法、字段或属性,则抽象类是一个合适的选择。
  3. 部分实现: 当你有一个类只能提供其方法的部分实现,并期望子类完成实现时,抽象类是合适的。
  4. **单一继承就足够了:**如果您的设计需要单一继承,并且您的类从公共基类继承是有意义的,请选择一个抽象类。

让我们举个例子来突出 C# 中抽象类和接口之间的确切区别。

using System;  
  
// Abstract class with a mix of abstract and concrete methods  
abstract class Vehicle  
{  
    // Abstract method to be implemented by derived classes  
    public abstract void Start();  
  
    // Concrete method with shared implementation  
    public void Stop()  
    {  
        Console.WriteLine("Vehicle stopped");  
    }  
}  
  
// Interface with method signatures only  
interface IFlyable  
{  
    void TakeOff();  
    void Land();  
}  
  
/* Concrete class inheriting from an abstract class   
 and implementing an interface */  
class Airplane : Vehicle, IFlyable  
{  
    // Implementation of the abstract method from the abstract class  
    public override void Start()  
    {  
        Console.WriteLine("Airplane started");  
    }  
  
    // Implementation of interface methods  
    public void TakeOff()  
    {  
        Console.WriteLine("Airplane taking off");  
    }  
  
    public void Land()  
    {  
        Console.WriteLine("Airplane landing");  
    }  
}  
  
class Program  
{  
    static void Main()  
    {  
        // Error: Cannot create an instance of the abstract class 'Vehicle'  
        Vehicle car = new Vehicle();  
  
        /* Using a class that inherits from an abstract class   
 and implements an interface */  
        Airplane airplane = new Airplane();  
        airplane.Start();   // Output: Airplane started  
        airplane.TakeOff(); // Output: Airplane taking off  
        airplane.Land();    // Output: Airplane landing  
        airplane.Stop();    // Output: Vehicle stopped  
    }  
}
  • Vehicle是一个抽象类,具有抽象 () 和具体 () 方法。Start()Stop()
  • IFlyable是一个具有两个方法签名 ( 和 ) 的接口。TakeOff()Land()
  • Airplane是一个具体的类,它继承自(抽象类)并实现(接口)。VehicleIFlyable
  • 尝试创建抽象类的实例会直接导致编译错误。Vehicle
  • 该类演示了抽象方法和接口方法的实现。Airplane

结论

接口和抽象类在 OOP 中有不同的用途。虽然接口侧重于定义行为契约,但抽象类在通用实现和扩展功能的灵活性之间提供了平衡。

当你有一个共同的基实现,通过继承重用代码,并且单一继承就足够了,选择一个抽象类就足够了。当您需要为不相关的类定义协定,仅强制执行方法签名,并通过多次继承支持多种行为或协定时,请选择接口。

相关留言评论
昵称:
邮箱:
阅读排行