C#中接口设计相关原则

news/2024/9/23 6:38:08/

在C#中,接口(Interface)是一种引用类型,它定义了一个契约,指定了一个类必须实现的成员(属性、方法、事件、索引器)。接口不提供这些成员的实现,只指定成员必须按照特定的方式被实现。

1、使用接口隔离原则 (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");  }  
}

2、扩展和可测试性设计

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

// 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");  }  
}

http://www.ppmy.cn/news/1443194.html

相关文章

探索Vue 3 reactive()原理及其实现步骤

探索Vue 3 reactive()原理及其实现步骤 引言 Vue 3中引入的Composition API&#xff0c;以其强大的灵活性和可组合性&#xff0c;彻底改变了Vue应用的开发方式。在这场革新中&#xff0c;reactive()函数扮演了核心角色&#xff0c;它使得开发者能够轻松创建响应式对象&#x…

七彩虹(Colorful)隐星P16 2023款笔记本电脑原装出厂Win11系统镜像下载 带建Recovery一键还原功能

七彩虹原厂Windows预装OEM专用系统&#xff0c;恢复出厂开箱状态一模一样 适用型号&#xff1a;隐星P16 23 链接&#xff1a;https://pan.baidu.com/s/1Ig5MQMiC8k4VSuCOZRQHUw?pwdak5l 提取码&#xff1a;ak5l 原厂W11系统自带所有驱动、出厂时自带的主题与专用壁纸、系…

代谢组数据分析三:差异分析

Differetial Analysis 差异分析的目的是为了筛选代谢物标记物,常用的方法有以下几种 倍数变化法 (Fold Change),也有基于log2的Fold change,计算组间倍数变化 T检验,计算组间均值的t统计量差别 PLS-DA或OPLS-DA的VIP(Variable Importance for the Projection,变量投影重要…

H3C 交换机配置 IGMP-snooping 注意点

IGMP-snooping 配置 参考&#xff1a; https://www.h3c.com/cn/d_202106/1413130_30005_0.htm#_Ref478716672 1.4 开启设备的 IGMP Snooping 特性 使能 IGMP Snooping <H3C>sys System View: return to User View with CtrlZ. [H3C]igmp-snooping [H3C-igmp-snoopin…

Java+playwright+testNG实现UI自动化测试

今天来讲讲使用Java结合最新的playwright来做UI自动化测试 目前网上大部分都是关于使用Python做自动化的教程&#xff0c;Java的比较少一些&#xff0c;但是我认为使用Java做自动化还是有优点的&#xff0c;性能就好一点&#xff0c;当然大家根据实际需求来。 一、 普通UI测试 …

【AI+chat】手把手配置kimichat集成到微信公众号中对话聊天

最近发现coze&#xff08;这里指国内版的https://www.coze.cn/&#xff09;可以配置对话到微信公众号&#xff08;订阅号&#xff09;中&#xff0c;且集成了月之暗面kimichat。 这里用个人公众号配置试一下。下面是详细步骤 步骤一&#xff1a;获取微信订阅号的开发者 ID 1…

Spring Kafka—— KafkaListenerEndpointRegistry 隐式注册分析

由于我想在项目中实现基于 Spring kafka 动态连接 Kafka 服务&#xff0c;指定监听 Topic 并控制消费程序的启动和停止这样一个功能&#xff0c;所以就大概的了解了一下 Spring Kafka 的几个重要的类的概念&#xff0c;内容如下&#xff1a; ConsumerFactory 作用&#xff1a;…

VSCode 配置 Qt 开发环境

文章目录 1. 环境说明2. 配置系统环境变量 1. 环境说明 操作系统&#xff1a;Windows 11VSCode版本&#xff1a;1.88.1CMake版本&#xff1a;3.27.7Qt6版本&#xff1a;6.7.0(MinGW 11.2.0 64-bit) 2. 配置系统环境变量 自行根据自己的Qt安装路径配置 配置 MinGW 和 CMake C…