C#中接口设计最佳实践

03125d5cff27c0be840e712b803638ac.jpeg

概述:设计接口涉及创建协定,这些协定定义类必须实现的方法、属性、事件或索引器。以下是在 C# 中设计良好接口的一些提示和最佳实践:使用接口隔离原则 (ISP)将较大的接口划分为更小、更具体的接口,以遵守 ISP,并确保实现类只需要实现它们使用的方法。// Bad example // A single interface for both lights and thermostats public interface IDevice {     void TurnOn();     void TurnOff();     void SetTemperature(int tempera

设计接口涉及创建协定,这些协定定义类必须实现的方法、属性、事件或索引器。以下是在 C# 中设计良好接口的一些提示和最佳实践:

使用接口隔离原则 (ISP)

将较大的接口划分为更小、更具体的接口,以遵守 ISP,并确保实现类只需要实现它们使用的方法。

// Bad example  
  
// A single interface for both lights and thermostats  
public interface IDevice  
{  
    void TurnOn();  
    void TurnOff();  
    void SetTemperature(int temperature);  
}  
  
public class SmartLight : IDevice  
{  
    public void TurnOn()  
    {  
        Console.WriteLine("Smart light turned on");  
    }  
  
    public void TurnOff()  
    {  
        Console.WriteLine("Smart light turned off");  
    }  
  
    public void SetTemperature(int temperature)  
    {  
        // Unsupported operation for a light  
        Console.WriteLine("Cannot set temperature for a light");  
    }  
}  
  
// Good example   
  
// Interface for a light device  
public interface ILight  
{  
    void TurnOn();  
    void TurnOff();  
}  
  
// Interface for a thermostat device  
public interface IThermostat  
{  
    void SetTemperature(int temperature);  
}  
  
// A smart light class implementing ILight  
public class SmartLight : ILight  
{  
    public void TurnOn()  
    {  
        Console.WriteLine("Smart light turned on");  
    }  
  
    public void TurnOff()  
    {  
        Console.WriteLine("Smart light turned off");  
    }  
}  
  
// A smart thermostat class implementing IThermostat  
public class SmartThermostat : IThermostat  
{  
    public void SetTemperature(int temperature)  
    {  
        Console.WriteLine($"Thermostat set to {temperature}°C");  
    }  
}

扩展和可测试性设计

接口在设计时应考虑扩展,以适应未来的更改和增强,而不会破坏现有实现。

// Interface representing a shape  
public interface IShape  
{  
    double CalculateArea();  
}  
  
// Rectangle implementation of the IShape interface  
public class Rectangle : IShape  
{  
    public double Width { get; }  
    public double Height { get; }  
    public Rectangle(double width, double height)  
    {  
        Width = width;  
        Height = height;  
    }  
    public double CalculateArea()  
    {  
        return Width \* Height;  
    }  
}  
  
// Circle implementation of the IShape interface  
public class Circle : IShape  
{  
    public double Radius { get; }  
    public Circle(double radius)  
    {  
        Radius = radius;  
    }  
    public double CalculateArea()  
    {  
        return Math.PI * Radius * Radius;  
    }  
}

在此示例中:

  • 我们有一个 IShape 接口,它表示一个形状,并使用 CalculateArea() 方法来计算其面积。

  • 我们有实现 IShape 接口的 Rectangle 和 Circle 形状类,每个类都提供自己特定于该形状的 CalculateArea() 方法的实现。

  • 该设计允许通过添加实现 IShape 接口的新形状类来轻松扩展,而无需修改现有代码。例如,如果我们想为 Square 扩展它,我们可以简单地创建一个新的类 Square 使用它自己的 CalculateArea() 方法实现 IShape。

// Square implementation of the IShape interface  
public class Square : IShape  
{  
    public double SideLength { get; }  
      
    public Square(double sideLength)  
    {  
        SideLength = sideLength;  
    }  
  
    public double CalculateArea()  
    {  
        return SideLength * SideLength;  
    }  
}

不可变接口

考虑将接口设计为不可变的,这意味着一旦定义,就无法修改它们。这有助于防止意外更改并确保代码库的稳定性。

// Immutable interface representing coordinates  
public interface ICoordinates  
{  
    // Readonly properties for latitude and longitude  
    double Latitude { get; }  
    double Longitude { get; }  
}  
  
public class Coordinates : ICoordinates  
{  
    public double Latitude { get; }  
    public double Longitude { get; }  
  
    // Constructor to initialize the latitude and longitude  
    public Coordinates(double latitude, double longitude)  
    {  
        Latitude = latitude;  
        Longitude = longitude;  
    }  
}

首选组合而不是继承

在设计接口时,优先考虑组合而不是继承。这促进了代码的重用和灵活性。

// Interface representing a component that can be composed into other classes  
public interface IComponent  
{  
    void Process();  
}  
  
// Example class implementing the IComponent interface  
public class Component : IComponent  
{  
    public void Process()  
    {  
        Console.WriteLine("Performing action in Component");  
    }  
}  
  
// Example class demonstrating composition  
public class CompositeComponent  
{  
    private readonly IComponent _component;  
    public CompositeComponent(IComponent component)  
    {  
        _component = component;  
    }  
    public void Execute()  
    {  
        _component.Process();  
    }  
}

避免接口过载

具有多种方法的重载接口,仅参数的数量或类型不同,可能会导致混淆。请改用不同的方法名称或重构接口。

public interface IVehicle  
{  
    void Start();  
    void Stop();  
    void Accelerate(int speed);  
    void Accelerate(double accelerationRate);  
}

虽然类中的重载方法是一种常见的做法,但接口中的重载方法可能会导致混淆,并使类实现哪种方法变得不那么清楚。通常,最好对不同的行为使用不同的方法名称,或者在必要时将它们分隔到多个接口中

使用泛型

利用泛型创建灵活且可重用的接口,这些接口可以处理不同类型的接口。这使我们能够编写更通用的代码,并且可以处理更广泛的场景。

// Generic interface for a data access layer  
public interface IDataAccessLayer<T>  
{  
    Task<T> GetByIdAsync(int id);  
  
    Task<IEnumerable<T>> GetAllAsync();  
}

版本控制接口

当接口随时间推移而发展时,请考虑对它们进行版本控制,以保持向后兼容性,同时引入新功能。这可以通过接口继承或在接口名称中使用版本控制等技术来实现。

// Interface representing a service for processing orders  
public interface IOrderService  
{  
    Task ProcessAsync(Order order);  
}  
  
// Interface representing a service for processing orders (version 2)  
public interface IOrderServiceV2  
{  
    Task ProcessAsync(OrderV2 order);  
}

使用协变接口和逆变接口

利用 .NET 中的协方差和逆变,在处理接口实现时允许更灵活的类型转换。

// Covariant interface for reading data  
public interface IDataReader<out T>  
{  
    T ReadData();  
}  
  
// Contravariant interface for writing data  
public interface IDataWriter<in T>  
{  
    void WriteData(T data);  
}

避免脂肪界面

FAT 接口包含太多成员,这使得它们难以实现和维护。将大型接口拆分为更小、更集中的接口。

// Bad example   
  
public interface IDataRepository  
{  
    Task<Data> GetByIdAsync(int id);  
    Task AddAsync(Data data);  
    Task GenerateReportAsync();  
    Task<bool> ValidateAsync(Data data);  
} 

// Good example  
  
// Interface for data retrieval operations  
public interface IDataRepository  
{  
    Task<Data> GetByIdAsync(int id);  
    Task CreateAsync(Data data);  
}  
  
  
// Interface for data reporting operations  
public interface IDataReporting  
{  
    Task GenerateReportAsync();  
}  
  
// Interface for data validation  
public interface IDataValidation  
{  
    Task<bool> ValidateAsync(Data data);  
}

使用显式接口实现

当类实现具有相同名称的成员的多个接口时,请使用显式接口实现来消除它们的歧义。这样可以更好地控制接口成员的可见性。

public interface IInterface1  
{  
    void Method();  
}  
  
public interface IInterface2  
{  
    void Method();  
}  
  
public class MyClass : IInterface1, IInterface2  
{  
    // Explicit implementation of IInterface1.Method  
    void IInterface1.Method()  
    {  
        Console.WriteLine("IInterface1.Method");  
    }  
  
    // Explicit implementation of IInterface2.Method  
    void IInterface2.Method()  
    {  
        Console.WriteLine("IInterface2.Method");  
    }  
}

最后的思考

通过遵循这些提示和最佳实践,我们可以在 C# 中创建设计良好的界面,这些界面清晰、灵活且易于测试和维护。

如果你喜欢我的文章,请给我一个赞!谢谢

-

技术群:添加小编微信并备注进群

小编微信:mm1552923   

公众号:dotNet编程大全    

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值