接口隔离原则在前端的应用

alt

什么是接口隔离

接口隔离原则(ISP)是面向对象编程中的SOLID原则之一,它专注于设计接口。强调在设计接口时,应该确保一个类不必实现它不需要的方法。换句话说,接口应该尽可能地小,只包含一个类需要的方法,而不是一个庞大的接口,其中包含许多类不需要的方法。这样可以减少类之间的不必要依赖,提高模块化和代码的可维护性。

如果一个大型接口包含许多函数,但一个类不需要所有这些函数,它仍然必须实现全部,即使有些是不必要的。ISP建议我们应该将这样的大型接口拆分成更小、更专注的接口。这样,每个类可以实现它实际需要的函数,避免实现不必要的函数。

通过遵循这种方法,可以降低代码复杂性,使其更易于理解和维护。

ISP的主要目标包括:

  • 将大型复杂接口拆分成更小、更具体的接口。
  • 确保类不需要实现不必要的功能。
  • 避免给类带来不必要的责任,从而产生更清晰、更易于理解的代码。

例如:

如果一个接口有10个方法,但特定类只需要其中的2个,ISP建议拆分这个大型接口。这样,每个类可以实现它需要的方法,而不需要实现其他的。

示例 1:

假设我们有一个用于所有类型任务的Worker接口:

public interface Worker {
    void work();
    void eat();
}

我们可以通过创建用于工作的Workable和用于吃的Eatable的单独接口来解决这个问题:

public interface Workable {
    void work();
}

public interface Eatable {
    void eat();
}

现在,RobotWorker不再需要实现不必要的eat()方法,遵循了接口隔离原则(ISP)。

示例 2:

假设有一个机器接口,既可以运行也可以充电:

interface Machine {
    run();
    recharge();
}

然而,有些机器只能运行但不能充电。根据ISP,我们应该将充电的责任分离到不同的接口:

interface RunOnly {
    run();
}

interface Rechargeable {
    recharge();
}

现在,不需要充电的机器只实现run()方法,而需要充电的机器实现recharge()方法。这种分离遵循了接口隔离原则(ISP)。

示例 3:

假设我们有一个Vehicle类,既可以驾驶也可以飞行:

class Vehicle {
    drive();
    fly();
}

然而,不是所有的车辆都能飞行。为了解决这个问题,我们可以创建单独的接口:

class DriveOnly {
    drive();
}

class FlyAndDrive {
    drive();
    fly();
}

现在,只能驾驶的车辆将实现DriveOnly类,而既能驾驶又能飞行的车辆将实现FlyAndDrive类。这个解决方案遵循了接口隔离原则(ISP),确保类只实现它们需要的功能。

ISP的重要性和实际应用:

  • 提高代码可维护性:ISP确保类只被要求实现它们需要的方法。这使得代码更易于维护,因为类不会被不必要的方法所困扰。
  • 使用特定接口:通过使用更小、更专注的接口而不是大型通用接口,开发变得更加高效,因为没有必要处理不必要的功能。
  • 实际解决方案:想象一下,你正在处理不同类型的设备,如打印机、扫描仪和多功能设备。每个设备都有其特定的任务集。使用ISP,你可以为每个任务(例如,打印、扫描)创建单独的接口,这样每个设备只实现它需要的功能。这使得代码保持清晰和有序。

何时使用ISP:

  • 当多个类有不同的需求时,应该将大型通用接口拆分成更小、更具体的接口,而不是使用一个大型的通用接口。
  • 如果你注意到一个类被迫实现它不需要或不使用的方法,你可以应用ISP来确保类只实现相关功能。

违反ISP导致的问题:

  • 不必要的方法实现:当一个类实现了一个大型接口,但没有使用所有方法时,它被迫实现不必要的方法。这导致代码中出现了不需要的方法。
  • 增加代码复杂性:大型接口可能导致类承担过多的责任,使得代码不必要地复杂。这种复杂性使得代码难以维护,引入新的变化可能变得有风险。
  • 违反类责任:当ISP被违反时,一个类可能不得不实现与其核心功能不直接相关的方法是。这也违反了单一责任原则(SRP),因为类参与了其主要角色之外的任务。
  • 维护和更新问题:当对大型接口进行更改时,所有实现该接口的类都必须适应这些更改。如果使用了更小的接口,只有相关的类需要更新,这使得维护一致性更容易。使用大型接口维护这种一致性可能变得具有挑战性。
  • 降低代码可重用性:大型接口迫使所有类实现所有方法,导致代码的可重用性降低。每个类可能最终包含不必要的代码,这降低了代码的整体可重用性。

假设你有一个名为Worker的大型接口,它包括work()和eat()方法。现在,对于机器人来说,没有必要eat()方法,但机器人类仍然需要实现它。这违反了ISP,导致与机器人功能无关的不必要方法。

因此,违反ISP会导致代码复杂性增加,使维护变得困难,并迫使不必要的方法实现。

接口隔离在前端的应用

在前端开发中,虽然没有直接的“接口”概念,但接口隔离原则仍然有许多应用场景,尤其是在模块化开发、API设计和组件化方面。其核心思想是,模块或组件应该提供专注且精简的接口,避免冗余或不相关的依赖。

用更简单的话说,它建议将大型接口或类拆分成更小、更专注的,允许客户端只使用对它们必要的部分。

这种方法促进了更清晰、更易于维护的代码,并提高了系统的灵活性,确保每个组件只与其所需的功能交互。

想象一下,一家餐厅有三种类型的顾客:

1)来吃米饭的,

2)来吃意大利面的,

3)来吃沙拉的。

如果我们为他们提供包含所有东西的相同菜单,许多项目对某些顾客来说将是无关紧要的。这将使菜单对他们来说不必要地复杂。

根据接口隔离原则(ISP),来吃米饭的顾客应该只得到米饭菜单,意大利面食客应该只收到意大利面菜单,沙拉食客应该只得到沙拉菜单。这样,每个人的体验都被简化了,允许每个顾客专注于他们实际想要的东西,没有任何不必要的选项。

这个类比说明了ISP如何鼓励定制接口以满足特定需求,使交互更简单、更高效。

React中的ISP简化:

在React中,我们经常创建包含许多属性或方法的大型组件。然而,一个组件并不总是需要所有这些属性。根据接口隔离原则(ISP),应该将组件拆分成更小的部分,以便每个组件只接收对其功能必要的属性和方法。

通过遵循这一原则,你可以实现:

  • 更清晰的代码:每个组件都专注于其特定任务,使代码库更容易理解和维护。
  • 提高可重用性:较小的组件可以在不同的上下文中重用,而不会携带不必要的属性。
  • 更好的性能:由于组件只接收它们需要的东西,渲染变得更加高效。
// 不好的例子:过多的 props 暴露给组件
const UserProfile = ({ user, onEditProfile, onDeleteProfile, onSendMessage }) => {
  // 组件需要处理很多不相关的操作
  return (
    <div>
      <h1>{user.name}</h1>
      <button onClick={onEditProfile}>Edit</button>
      <button onClick={onDeleteProfile}>Delete</button>
      <button onClick={onSendMessage}>Message</button>
    </div>
  );
};

// 改进后的例子:将组件接口精简,只关注展示用户信息
const UserProfile = ({ user }) => {
  return <h1>{user.name}</h1>;
};

// 将编辑、删除和发送消息操作抽象到外部
const UserActions = ({ onEditProfile, onDeleteProfile, onSendMessage }) => {
  return (
    <div>
      <button onClick={onEditProfile}>Edit</button>
      <button onClick={onDeleteProfile}>Delete</button>
      <button onClick={onSendMessage}>Message</button>
    </div>
  );
};

在这个例子中,UserProfile组件遵循接口隔离原则,专注于用户信息的展示,而不负责处理用户的操作逻辑,这些逻辑被移到UserActions组件中。

通过将组件拆分成更小、专注的部分,我们确保每个组件都做得很好,提高了可维护性,并使适应或扩展功能变得更容易。这种方法还促进了更好的可重用性,因为开发人员可以选择只符合他们要求的组件,而不需要携带不必要的负担。

接口隔离在状态管理中的应用

在使用状态管理工具(如Vuex或Redux)时,接口隔离原则同样重要。状态管理通常负责管理整个应用的全局状态,如果将所有状态逻辑都暴露给各个组件,可能会导致组件的复杂性增加。因此,通过模块化管理状态,并隔离组件与不相关的状态,能够降低组件对全局状态的依赖。

// 不好的例子:所有状态和操作集中在一个大store中
const store = new Vuex.Store({
  state: {
    user: null,
    orders: [],
  },
  mutations: {
    setUser(state, user) {
      state.user = user;
    },
    setOrders(state, orders) {
      state.orders = orders;
    },
  },
  actions: {
    fetchUser({ commit }) {
      // 请求用户信息
    },
    fetchOrders({ commit }) {
      // 请求订单信息
    },
  },
});

// 改进后的例子:将状态和操作按模块分离
const userModule = {
  state: { user: null },
  mutations: {
    setUser(state, user) {
      state.user = user;
    },
  },
  actions: {
    fetchUser({ commit }) {
      // 请求用户信息
    },
  },
};

const orderModule = {
  state: { orders: [] },
  mutations: {
    setOrders(state, orders) {
      state.orders = orders;
    },
  },
  actions: {
    fetchOrders({ commit }) {
      // 请求订单信息
    },
  },
};

const store = new Vuex.Store({
  modules: {
    user: userModule,
    orders: orderModule,
  },
});

将状态和逻辑分模块处理后,组件只需与它们关心的模块交互,遵循了接口隔离原则,代码更加易读、易维护。

接口隔离原则能够帮助我们保持代码的高内聚性和低耦合性。无论是组件设计、API请求模块化,还是状态管理与样式处理,遵循接口隔离原则都可以提高代码的可维护性和扩展性。这一原则提醒我们,在设计模块或接口时,应专注于模块的核心功能,避免将过多无关的职责暴露给调用者。

接口隔离原则(ISP)的缺点

虽然接口隔离原则(ISP)有许多优点,但它也有一些局限性。以下是ISP的一些缺点:

需要更多的接口:遵循ISP通常需要将大型接口拆分成更小的接口。这可能导致创建大量接口,使代码管理变得复杂。

增加编码和维护工作:有许多接口,每个接口都需要单独实现。这增加了开发人员的工作量,可能需要更多时间。此外,稍后进行更改可能需要在多个地方进行更新,使维护变得复杂。

过度工程的风险:ISP有时可能会引入过度的复杂性,特别是当创建了太多小接口时。这种方法可能导致过度工程,给项目带来不必要的复杂性。

复杂的依赖管理:使用ISP可能会使组件或类依赖于各种接口。这可能会使依赖管理变得复杂,因为多个依赖关系来自多个接口,这使得跟踪它们变得困难。

在应用ISP时,可能会出现诸如创建过多接口、增加编码和管理挑战等问题,这可能会增加项目的复杂性。

结论

接口隔离原则(ISP)有助于保持编程中的模块化和灵活性。通过将大型接口或组件拆分成更小的部分,它消除了不必要的复杂性。使用ISP允许我们在组件中只实现必要的方法或属性,使代码更简单、更可重用、更易于维护。尽管它有时可能导致接口和代码的增加,但当正确应用时,它可以极大地提高软件设计的组织和有效性。因此,正确实施ISP对于提高质量和软件开发的长期成功至关重要。

本文由 mdnice 多平台发布

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/55511.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

书生大模型实战(从入门到进阶)L3-彩蛋岛-InternLM 1.8B 模型 Android 端侧部署实践

目录 1 环境准备 1.1 安装rust 1.2 安装Android Studio 1.3 设置环境变量 2 转换模型 2.1 安装mlc-llm 2.2 (可选)转换参数 2.3 (可选)生成配置 2.4 (可选)上传到huggingface 2.5 (可选) 测试转换的模型 3 打包运行 3.1 修改配置文件 3.2 运行打包命令 3.3 创建签…

管理方法(12)-- 采购管理

采购人员不是在为公司讨价还价,而是在为顾客讨价还价,我们应该为顾客争取最低的价钱。-----山姆 沃尔顿 沃尔玛的创始人。 1. 采购的定义和原则 5R原则:适时(Right Time)、适质(Right Quality)、适量(Right Quantity)、适价(Right Price)、适地(Right Place)。…

数据结构——栈的基本操作

前言 介绍 &#x1f343;数据结构专区&#xff1a;数据结构 参考 该部分知识参考于《数据结构&#xff08;C语言版 第2版&#xff09;》55 ~ 59页 &#x1f308;每一个清晨&#xff0c;都是世界对你说的最温柔的早安&#xff1a;ૢ(≧▽≦)و✨ 1、栈的基本概念 栈&#x…

【SpringBoot详细教程】-08-MybatisPlus详细教程以及SpringBoot整合Mybatis-plus【持续更新】

目录 🌲 MyBatis Plus 简介 🌾入门案例 🌾 MP 简介 🌲 MP 的CRUD 🌾 新增 🌾 删除 🌾 修改在进行 🌾 根据ID查询 🌾 查询所有 🌲 分页功能 🌾 设置分页参数 🌾 设置分页拦截器 🌲 优化启动 🌾 取消mbatisPlusBanner 🌾 取消Sprin…

国外电商系统开发-运维系统功能清单开发

一、最终效果图 二、功能清单 功能 描述 自定义日志绘图 根据Nginx、Apache登录日志文件绘图&#xff0c;绘图数据包括&#xff1a;访问量走势&#xff0c;500错误&#xff0c;200正确百分比等 创建服务器 加入服务器 主机状态自动检查 加入主机到系统后&#xff0c;系统…

LeetCode: 1971. 寻找图中是否存在路径

寻找图中是否存在路径 原题 有一个具有 n 个顶点的 双向 图&#xff0c;其中每个顶点标记从 0 到 n - 1&#xff08;包含 0 和 n - 1&#xff09;。图中的边用一个二维整数数组 edges 表示&#xff0c;其中 edges[i] [ui, vi] 表示顶点 ui 和顶点 vi 之间的双向边。 每个顶点…

记OpenGL的Demo中增加ImGui后界面不显示的问题

百思不得其解&#xff1a; 我的ImGui界面呢&#xff1f; 经过不断调整代码&#xff0c;也可能是对这还不太熟悉吧&#xff0c;最终将drawData放在clearColor后界面出来了&#xff0c;仔细想想也能理解&#xff0c;先画了界面&#xff0c;再渲染了背景色&#xff0c;将界面盖住了…

虚商目前有哪些业务痛点?

虚拟运营商当前面临的业务痛点主要集中在市场竞争、运营成本、技术依赖、用户体验及政策监管等方面。 一、市场竞争激烈 1、竞争者数量增加: 随着市场准入门槛的降低&#xff0c;越来越多的企业进入虚拟运营商市场导致市场竟争日益激烈。为了争夺市场份额&#xff0c;企业不得不…

【Xcode Command Line Tools】安装指南

安装指令 xcode-select --install安装 完成安装 验证 $ xcode-select -p /Library/Developer/CommandLineTools

【Spring】运行Spring Boot项目,请求响应流程分析以及404和500报错

1. 运行项目 import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Appl…

【车载开发系列】ParaSoft单元测试环境配置(四)

【车载开发系列】ParaSoft单元测试环境配置&#xff08;四&#xff09; 【车载开发系列】ParaSoft单元测试环境配置&#xff08;四&#xff09; 【车载开发系列】ParaSoft单元测试环境配置&#xff08;四&#xff09;一. 如何设置过滤二. 如何设置静态扫描的规则三. 如何设置单…

Pycharm 本地搭建 stable-diffusion-webui

一、下载工程源码 Github官方连接 https://github.com/AUTOMATIC1111/stable-diffusion-webui 二、Pycharm打开工程 1、设置环境 文件-设置-项目-Python解析器-添加解释器-添加本地解释器 Conda环境-创造新环境-Python版本3.10 注意一定要选择Python3.10版本&#xff0c;否…

虚拟机三种网络模式详解

在电脑里开一台虚拟机&#xff0c;是再常见不过的操作了。无论是用虚拟机玩只有旧版本系统能运行的游戏&#xff0c;还是用来学习Linux、跑跑应用程序都是很好的。而这其中&#xff0c;虚拟机网络是绝对绕不过去的。本篇文章通俗易懂的介绍了常见的虚拟网络提供的三种网络链接模…

C/C++进阶(一)--内存管理

更多精彩内容..... &#x1f389;❤️播主の主页✨&#x1f618; Stark、-CSDN博客 本文所在专栏&#xff1a; 学习专栏C语言_Stark、的博客-CSDN博客 其它专栏&#xff1a; 数据结构与算法_Stark、的博客-CSDN博客 ​​​​​​项目实战C系列_Stark、的博客-CSDN博客 座右铭&a…

大数据-151 Apache Druid 集群模式 配置启动【上篇】 超详细!

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; 目前已经更新到了&#xff1a; Hadoop&#xff08;已更完&#xff09;HDFS&#xff08;已更完&#xff09;MapReduce&#xff08;已更完&am…

《Linux从小白到高手》理论篇(六):Linux软件安装一篇通

List item 本篇介绍Linux软件安装相关的操作命令&#xff0c;看完本文&#xff0c;有关Linux软件安装相关操作的常用命令你就掌握了99%了。 Linux软件安装 RPM RPM软件的安装、删除、更新只有root权限才能使用&#xff1b;查询功能任何用户都可以操作&#xff1b;如果普通用…

移情别恋c++ ദ്ദി˶ー̀֊ー́ ) ——13.mapset(模拟实现)

1.对红黑树进行改造 1.1treenode模板参数改变 之前构建treenode模板参数传的是class k,class v(set为k&#xff0c;k&#xff1b;map是k&#xff0c;v&#xff09;&#xff0c;现在直接用T代替 template<class T> //这里直接传了T作为模板参数&#xff0c;T可能是pai…

【WRF工具】cmip6-to-wrfinterm工具概述:生成WRF中间文件

cmip6-to-wrfinterm工具概述 cmip6-to-wrfinterm工具安装cmip6-to-wrfinterm工具使用快速启动&#xff08;Quick start&#xff09;情景1&#xff1a;MPI-ESM-1-2-HR&#xff08;默认&#xff09;&#xff1a;情景2&#xff1a;BCMM情景3&#xff1a;EC-Earth3 更改使用&#x…

进度条(倒计时)Linux

\r回车(回到当前行开头) \n换行 行缓冲区概念 什么现象&#xff1f; 什么现象&#xff1f;&#xff1f; 什么现象&#xff1f;&#xff1f;&#xff1f; 自己总结&#xff1a; #pragma once 防止头文件被重复包含 倒计时 在main.c中&#xff0c;windows.h是不可以用的&…

大语言模型入门(一)——大语言模型智能助手

一、大语言模型智能助手 2022年末ChatGPT一经推出&#xff0c;一时间不注册个账号用一下都跟不上潮流了。然而&#xff0c;我们要注册OpenAI的账号使用ChatGPT还是一件比较麻烦的事情&#xff08;懂的都懂&#xff09;。好在&#xff0c;国内各大团队非常给力地及时推出了自研的…