1 接口
当需要定义一组方法或属性,但不关心具体实现时,可以使用接口。当然,如果是开发小型工具类或者内部工具、小型项目时可以不使用接口,直接使用类,但如果是大型项目、框架设计或需要支持插件化的场景,则最好使用接口。
从 C# 8.0 开始,接口可以包含默认实现,但实现类仍然可以选择重写默认实现。
1.1 接口的优势
1、统一标准,支持多态
通过接口,可以将不同的类视为同一类型,从而实现多态性。比如:一个接口可以被不同的类实现,然后通过接口去调用这些类的方法。
2、解耦
接口可以将定义与实现分离,降低代码的耦合度。
3、支持多重继承
一个类可以继承多个接口,从而在该类实现不同接口的方法。
interface IShape
{
void Draw();
}
interface IColor
{
void SetColor(string color);
}
class Circle : IShape, IColor
{
public void Draw()
{
Console.WriteLine("Drawing a circle");
}
public void SetColor(string color)
{
Console.WriteLine("Setting color to " + color);
}
}
4、使用接口可以让依赖可替换,使单元测试更容易
5、扩展性高
接口将定义与实现分离,调用方代码依赖于接口(抽象),而不是具体的实现类。这样,当需要替换或扩展实现时,只需要提供新的实现类,而不需要修改调用方的代码。
如:有一个支付系统,最初只支持信用卡支付,但未来可能需要支持更多的支付方式(如支付宝、微信支付等)。如果直接使用类,扩展性会很差;而使用接口,则可以轻松扩展。
直接使用类:
class CreditCardPayment
{
public void Pay(decimal amount)
{
Console.WriteLine($"Paid {amount} via Credit Card");
}
}
class PaymentProcessor
{
private CreditCardPayment _payment;
public PaymentProcessor()
{
_payment = new CreditCardPayment(); // 直接依赖具体类
}
public void ProcessPayment(decimal amount)
{
_payment.Pay(amount);
}
}
class Program
{
static void Main(string[] args)
{
PaymentProcessor processor = new PaymentProcessor();
processor.ProcessPayment(100.00m); // 输出: Paid 100.00 via Credit Card
}
}
如果现在需要支持支付宝支付,必须修改 PaymentProcessor
类,增加对支付宝的支持。这种设计违反了开闭原则(对扩展开放,对修改关闭),每次添加新的支付方式都需要修改调用方代码。
使用接口:
// 定义接口
interface IPayment
{
void Pay(decimal amount);
}
// 信用卡支付实现
class CreditCardPayment : IPayment
{
public void Pay(decimal amount)
{
Console.WriteLine($"Paid {amount} via Credit Card");
}
}
// 支付宝支付实现
class AlipayPayment : IPayment
{
public void Pay(decimal amount)
{
Console.WriteLine($"Paid {amount} via Alipay");
}
}
// 支付处理器
class PaymentProcessor
{
private IPayment _payment;
// 通过构造函数注入依赖
public PaymentProcessor(IPayment payment)
{
_payment = payment;
}
public void ProcessPayment(decimal amount)
{
_payment.Pay(amount);
}
}
class Program
{
static void Main(string[] args)
{
// 使用信用卡支付
IPayment creditCardPayment = new CreditCardPayment();
PaymentProcessor processor1 = new PaymentProcessor(creditCardPayment);
processor1.ProcessPayment(100.00m); // 输出: Paid 100.00 via Credit Card
// 使用支付宝支付
IPayment alipayPayment = new AlipayPayment();
PaymentProcessor processor2 = new PaymentProcessor(alipayPayment);
processor2.ProcessPayment(200.00m); // 输出: Paid 200.00 via Alipay
}
}
如果需要支持新的支付方式(如微信支付),只需要创建一个新的实现类,而不需要修改PaymentProcessor
或调用方的代码。
如果使用类,且通过新增方法来实现新功能(如微信支付),而不是修改现有代码,这样会:
①、导致类的职责过重,PaymentProcessor
类会随着支付方式的增加而变得臃肿,违反了单一职责原则。
②、调用方需要修改。调用方需要知道具体使用哪个支付方法(如 PayByCreditCard
、PayByAlipay
、PayByWeChat
),如果调用方需要动态选择支付方式,代码会变得复杂。
③、难以扩展。如果需要支持更多的支付方式,必须不断修改 PaymentProcessor
类,添加新的方法,这种方式在支付方式较少时可行,但在支付方式较多时会变得难以维护。
1.2 接口和抽象类
特性 | 接口(Interface) | 抽象类(Abstract Class) |
实例化 | 不能实例化 | 不能实例化 |
字段 | 不能包含字段 | 可以包含字段 |
多重继承 | 支持多重继承 | 不支持多重继承 |
2 abstract
使用abstract关键字定义。
abstract的作用是提供一种规范或契约,确保派生类具有一致的行为。
2.1 抽象类
抽象类不能被实例化,只能作为其他类的基类。
抽象类可以包含抽象方法、具体方法、字段、属性等。
2.2 抽象方法
抽象方法没有实现,必须在派生类中重写,且要求派生类必须提供具体实现。
抽象方法只能存在于抽象类中。