当前位置: 首页 > news >正文

C#中接口设计相关原则

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

相关文章:

C#中接口设计相关原则

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

Pytorch学习笔记——卷积操作

一、认识卷积操作 卷积操作是一种数学运算&#xff0c;它涉及两个函数&#xff1a;输入函数&#xff08;通常是图像&#xff09;和卷积核&#xff08;也称为滤波器或特征检测器&#xff09;。卷积核在输入函数上滑动&#xff0c;将核中的每个元素与其覆盖的输入函数区域中的对应…...

探索鸿蒙开发:鸿蒙系统如何引领嵌入式技术革新

嵌入式技术已经成为现代社会不可或缺的一部分。而在这个领域&#xff0c;华为凭借其自主研发的鸿蒙操作系统&#xff0c;正悄然引领着一场技术革新的浪潮。本文将探讨鸿蒙开发的特点、优势以及其对嵌入式技术发展的深远影响。 鸿蒙操作系统的特点 鸿蒙&#xff0c;作为华为推…...

chrome extension插件替换网络请求中的useragent

感觉Chrome商店中的插件不能很好的实现自己想要的效果,那么就来自己动手吧。 本文以百度为例: 一般来说网页请求如下: 当前使用的useragent是User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safar…...

PHP基础【介绍,注释,更改编码,赋值,数据类型】

源码 <?php //单行注释 /* 多行注释 *///通过header()函数发送http头的请求信息用来指定页面的字符集编码 header("Content-type:text/html;Charsetutf-8"); //告诉浏览器&#xff0c;当前页面的内容类型是HTML&#xff0c;并且页面内容使用的是UTF-8编码。//ph…...

ASP.NET小型证券术语解释及翻译系统的设计与开发

摘 要 在系统设计上&#xff0c;综合各种翻译类型网站优缺点&#xff0c;设计出具有任何使用者都可添加术语信息的且只有管理员能够实现术语修改及删除等独特方式的术语查看管理系统。此方式能够使术语量快速增大&#xff0c;并且便于使用者及管理员操作&#xff0c;满足相互…...

硬件知识积累 音频插座的了解,看音频插座的原理图来了解音频插座的引脚。

1. 音频接口 音频插座是一种用于连接音频信号线路的电子元件&#xff0c;常见于音频设备&#xff08;如音响、耳机、话筒等&#xff09;中。它的主要作用是将电子信号转化为声音信号&#xff0c;以满足人们对于音乐、电影、游戏等方面的需求。 根据插头形状的不同&#xff0c;音…...

error LNK2001: 无法解析的外部符号 “__declspec(dllimport) public: __cdecl ......

运行程序时&#xff0c;报如上图所示错误&#xff0c;其中一条是&#xff1a; ReflectionProbe.obj : error LNK2001: 无法解析的外部符号 "__declspec(dllimport) public: __cdecl osg::Object::Object(bool)" (__imp_??0ObjectosgQEAA_NZ) 报这个错误一般是因为…...

邮箱Webhook API发送邮件的性能怎么优化?

邮箱Webhook API发送邮件的步骤&#xff1f;如何用邮箱API发信&#xff1f; 随着业务规模的扩大&#xff0c;如何高效地通过邮箱Webhook API发送邮件&#xff0c;成为了许多企业面临的关键问题。下面&#xff0c;AokSend将探讨一些优化邮箱Webhook API发送邮件性能的方法。 邮…...

并发编程实现

一、并行编程 1、Parallel 类 Parallel类是System.Threading.Tasks命名空间中的一个重要类&#xff0c;它提供数据并行和任务并行的高级抽象。 For和ForEach Parallel类下的For和ForEach对应着普通的循环和遍历(普通的for和foreach)&#xff0c;但执行时会尝试在多个线程上…...

基于EBAZ4205矿板的图像处理:12图像二值化(阈值可调)

基于EBAZ4205矿板的图像处理&#xff1a;12图像二值化(阈值可调) 我的项目是基于EBAZ4205矿板的阈值可调的图像阈值二值化处理&#xff0c;可以通过按键调整二值化的阈值&#xff0c;key1为阈值加1&#xff0c;key4为阈值减1&#xff0c;key2为阈值加10&#xff0c;key5为阈值…...

人大金仓数据库报com.kingbase8.util.KSQLException: 致命错误: 用户 “SYSTEM“ Password 认证失败

com.kingbase8.util.KSQLException: 致命错误: 用户 “SYSTEM” Password 认证失败 解决办法&#xff1a; 问题在于用户权限只不足&#xff0c;相关配置文件在一般在 /data/sys hba.conf,修改IPV4 local connections选项中的改为trust。...

文件加密软件哪个好?文件加密软件排行榜前十名(好用软件推荐)

文件加密软件哪个好&#xff1f;这是许多个人和企业用户在面临数据保护需求时所关心的问题。随着数字化时代的推进&#xff0c;数据安全问题日益凸显&#xff0c;文件加密软件成为了保护数据安全的重要手段。本文将为您介绍当前市场上排名前十的文件加密软件&#xff0c;帮助您…...

Netty的第一个简单Demo实现

目录 说明需求ClientServer写法总结 实现运行 说明 Netty 的一个练习&#xff0c;使用 Netty 连通 服务端 和 客户端&#xff0c;进行基本的通信。 需求 Client 连接服务端成功后&#xff0c;打印连接成功给服务端发送消息HelloServer Server 客户端连接成功后&#xff0…...

K8S 哲学 - 服务发现 services

apiVersion: v1 kind: Service metadata:name: deploy-servicelabels:app: deploy-service spec: ports: - port: 80targetPort: 80name: deploy-service-podselector: app: deploy-podtype: NodePort service 的 endPoint &#xff08;ep&#xff09; 主机端口分配方式 两…...

Springboot工程创建

目录 一、步骤 二、遇到的问题及解决方案 一、步骤 打开idea,点击文件 ->新建 ->新模块 选择Spring Initializr&#xff0c;并设置相关信息。其中组为域名&#xff0c;如果没有公司&#xff0c;可以默认com.example。点击下一步 蓝色方框部分需要去掉&#xff0c;软件包…...

日本站群服务器的优点以及适合该服务器的业务类型?

日本站群服务器的优点以及适合该服务器的业务类型? 日本站群服务器是指位于日本地区的多个网站共享同一台服务器的架构。这种服务器架构有着诸多优点&#xff0c;使其成为许多企业和网站管理员的首选。以下是日本站群服务器的优点以及适合该服务器的业务类型的分析&#xff1…...

堆的应用2——TOPK问题

TOPK问题 TOP-K问题&#xff1a;即求数据结合中前K个最大的元素或者最小的元素&#xff0c;一般情况下数据量都比较大。 比如&#xff1a;专业前10名、世界500强、富豪榜、游戏中前100的活跃玩家等。 情况1——数据量小 对于Top-K问题&#xff0c;能想到的最简单直接的方式就…...

leetcode-5. 最长回文子串

题目描述 给你一个字符串 s&#xff0c;找到 s 中最长的回文子串。 如果字符串的反序与原始字符串相同&#xff0c;则该字符串称为回文字符串。 示例 1&#xff1a; 输入&#xff1a;s "babad" 输出&#xff1a;"bab" 解释&#xff1a;"aba"…...

【Flask 系统教程 1】入门及配置

当你开始学习 Flask 时&#xff0c;了解如何进行基本的配置是非常重要的。Flask 是一个简单而灵活的 Python Web 框架&#xff0c;它允许你快速构建 Web 应用程序&#xff0c;并且易于学习。在这篇博客中&#xff0c;我将介绍如何从零开始进行 Flask 的基础配置&#xff0c;适合…...

【Linux】shell脚本忽略错误继续执行

在 shell 脚本中&#xff0c;可以使用 set -e 命令来设置脚本在遇到错误时退出执行。如果你希望脚本忽略错误并继续执行&#xff0c;可以在脚本开头添加 set e 命令来取消该设置。 举例1 #!/bin/bash# 取消 set -e 的设置 set e# 执行命令&#xff0c;并忽略错误 rm somefile…...

Leetcode 3577. Count the Number of Computer Unlocking Permutations

Leetcode 3577. Count the Number of Computer Unlocking Permutations 1. 解题思路2. 代码实现 题目链接&#xff1a;3577. Count the Number of Computer Unlocking Permutations 1. 解题思路 这一题其实就是一个脑筋急转弯&#xff0c;要想要能够将所有的电脑解锁&#x…...

苍穹外卖--缓存菜品

1.问题说明 用户端小程序展示的菜品数据都是通过查询数据库获得&#xff0c;如果用户端访问量比较大&#xff0c;数据库访问压力随之增大 2.实现思路 通过Redis来缓存菜品数据&#xff0c;减少数据库查询操作。 缓存逻辑分析&#xff1a; ①每个分类下的菜品保持一份缓存数据…...

mysql已经安装,但是通过rpm -q 没有找mysql相关的已安装包

文章目录 现象&#xff1a;mysql已经安装&#xff0c;但是通过rpm -q 没有找mysql相关的已安装包遇到 rpm 命令找不到已经安装的 MySQL 包时&#xff0c;可能是因为以下几个原因&#xff1a;1.MySQL 不是通过 RPM 包安装的2.RPM 数据库损坏3.使用了不同的包名或路径4.使用其他包…...

使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台

🎯 使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台 📌 项目背景 随着大语言模型(LLM)的广泛应用,开发者常面临多个挑战: 各大模型(OpenAI、Claude、Gemini、Ollama)接口风格不统一;缺乏一个统一平台进行模型调用与测试;本地模型 Ollama 的集成与前…...

docker 部署发现spring.profiles.active 问题

报错&#xff1a; org.springframework.boot.context.config.InvalidConfigDataPropertyException: Property spring.profiles.active imported from location class path resource [application-test.yml] is invalid in a profile specific resource [origin: class path re…...

《C++ 模板》

目录 函数模板 类模板 非类型模板参数 模板特化 函数模板特化 类模板的特化 模板&#xff0c;就像一个模具&#xff0c;里面可以将不同类型的材料做成一个形状&#xff0c;其分为函数模板和类模板。 函数模板 函数模板可以简化函数重载的代码。格式&#xff1a;templa…...

Go 语言并发编程基础:无缓冲与有缓冲通道

在上一章节中&#xff0c;我们了解了 Channel 的基本用法。本章将重点分析 Go 中通道的两种类型 —— 无缓冲通道与有缓冲通道&#xff0c;它们在并发编程中各具特点和应用场景。 一、通道的基本分类 类型定义形式特点无缓冲通道make(chan T)发送和接收都必须准备好&#xff0…...

纯 Java 项目(非 SpringBoot)集成 Mybatis-Plus 和 Mybatis-Plus-Join

纯 Java 项目&#xff08;非 SpringBoot&#xff09;集成 Mybatis-Plus 和 Mybatis-Plus-Join 1、依赖1.1、依赖版本1.2、pom.xml 2、代码2.1、SqlSession 构造器2.2、MybatisPlus代码生成器2.3、获取 config.yml 配置2.3.1、config.yml2.3.2、项目配置类 2.4、ftl 模板2.4.1、…...

c++第七天 继承与派生2

这一篇文章主要内容是 派生类构造函数与析构函数 在派生类中重写基类成员 以及多继承 第一部分&#xff1a;派生类构造函数与析构函数 当创建一个派生类对象时&#xff0c;基类成员是如何初始化的&#xff1f; 1.当派生类对象创建的时候&#xff0c;基类成员的初始化顺序 …...