前往小程序,Get更优阅读体验!
立即前往
发布
社区首页 >专栏 >在 JavaScript 和 TypeScript 框架中应用 SOLID 原则

在 JavaScript 和 TypeScript 框架中应用 SOLID 原则

作者头像
萌萌哒草头将军
发布2025-02-19 11:07:26
发布2025-02-19 11:07:26
8000
代码可运行
举报
文章被收录于专栏:前端框架
运行总次数:0
代码可运行

介绍

SOLID 原则是面向对象设计的五个基本原则,旨在帮助开发者创建可维护、可扩展和可重用的代码。虽然这些原则起源于面向对象编程,但它们可以有效地应用于 JavaScript。本文通过JS中的真实示例解释了每个原则。

1.单一职责原则 (Single Responsibility Principle, SRP)

原则: 每个类或模块应该只有一个单一的职责,即只负责一项功能。这样可以降低类之间的耦合度,提高代码的可维护性。

例如下面的 react 代码,我们经常看到组件负责太多事情——例如管理UI和业务逻辑。

代码语言:javascript
代码运行次数:0
复制
function UserProfile({ userId }) {
  const [user, setUser] = useState(null);

  useEffect(() => {
    fetchUserData();
  }, [userId]);

  async function fetchUserData() {
    const response = await fetch(`/api/users/${userId}`);
    const data = await response.json();
    setUser(data);
  }

  return <div>{user?.name}</div>;
}

UserProfile组件违反了SRP,因为它同时处理UI渲染和数据提取。重构之后

代码语言:javascript
代码运行次数:0
复制
// Custom hook for fetching user data
function useUserData(userId) {
  const [user, setUser] = useState(null);

  useEffect(() => {
    async function fetchUserData() {
      const response = await fetch(`/api/users/${userId}`);
      const data = await response.json();
      setUser(data);
    }
    fetchUserData();
  }, [userId]);

  return user;
}

// UI Component
function UserProfile({ userId }) {
  const user = useUserData(userId); // Moved data fetching logic to a hook

  return <div>{user?.name}</div>;
}

我们将数据获取逻辑与 UI 分离,让每个部分负责单个任务。

2.开放/封闭原则(OCP)

原则: 软件实体(类、模块、函数等)应该对扩展开放,对修改封闭。这意味着你应该能够通过扩展现有代码来添加新功能,而不需要修改已有的代码。

例如下面的 JavaScript 代码,有一个运行良好的表单验证功能,但将来可能需要额外的验证逻辑。每当您需要新的验证规则时,您就必须修改此功能,从而违反 OCP

代码语言:javascript
代码运行次数:0
复制
function validate(input) {
  if (input.length < 5) {
    return 'Input is too short';
  }
  if (!input.includes('@')) {
    return 'Invalid email';
  }
  return 'Valid input';
}

重构之后,我们可以扩展验证规则,而不需要修改原有的验证函数,遵循OCP

代码语言:javascript
代码运行次数:0
复制
function validate(input, rules) {
  return rules.map(rule => rule(input)).find(result => result !== 'Valid') || 'Valid input';
}

const lengthRule = input => input.length >= 5 ? 'Valid' : 'Input is too short';
const emailRule = input => input.includes('@') ? 'Valid' : 'Invalid email';

validate('test@domain.com', [lengthRule, emailRule]);

3.里氏替换原则(LSP)

原则: 子类应该能够替代其父类,并且在程序中可以无缝使用。换句话说,使用子类的对象时,程序的正确性不应受到影响。

例如react中,当使用高阶组件(HOC)或有条件地渲染不同组件时,LSP有助于确保所有组件的行为都可预测

但是下面的代码中,组件不能互换,因为它们使用不同的 propsonClickhref)。

代码语言:javascript
代码运行次数:0
复制
function Button({ onClick }) {
  return <button onClick={onClick}>Click me</button>;
}

function LinkButton({ href }) {
  return <a href={href}>Click me</a>;
}

// Inconsistent use of onClick and href makes substitution difficult
<Button onClick={() => {}} />;
<LinkButton href="/home" />;

重构之后,两个组件(ButtonLinkButton)在语义上都是正确的,遵守 HTML 可访问性标准,并且在遵循 LSP 时行为一致

代码语言:javascript
代码运行次数:0
复制
function Actionable({ onClick, href, children }) {
  if (href) {
    return <a href={href}>{children}</a>;
  } else {
    return <button onClick={onClick}>{children}</button>;
  }
}

function Button({ onClick }) {
  return <Actionable onClick={onClick}>Click me</Actionable>;
}

function LinkButton({ href }) {
  return <Actionable href={href}>Go Home</Actionable>;
}

4.接口隔离原则(ISP)

原则: 不应该强迫一个类依赖于它不使用的方法。应该将大接口分解成小接口,以便实现更精确的依赖关系,降低类之间的耦合度。

例如下面的 React 组件有时会收到不必要的 props,导致代码紧密耦合且臃肿,

代码语言:javascript
代码运行次数:0
复制
function MultiPurposeComponent({ user, posts, comments }) {
  return (
    <div>
      <UserProfile user={user} />
      <UserPosts posts={posts} />
      <UserComments comments={comments} />
    </div>
  );
}

重构之后,通过将组件拆分为更小的组件,每个组件仅依赖于它实际使用的数据。

代码语言:javascript
代码运行次数:0
复制
function UserProfileComponent({ user }) {
  return <UserProfile user={user} />;
}

function UserPostsComponent({ posts }) {
  return <UserPosts posts={posts} />;
}

function UserCommentsComponent({ comments }) {
  return <UserComments comments={comments} />;
}

5.依赖倒置原则(DIP)

原则: 高级模块不应该依赖于低级模块。两者都应该依赖于抽象(例如接口)

下面的代码中,UserComponentfetchUser 函数紧密耦合。

代码语言:javascript
代码运行次数:0
复制
function fetchUser(userId) {
  return fetch(`/api/users/${userId}`).then(res => res.json());
}

function UserComponent({ userId }) {
  const [user, setUser] = useState(null);

  useEffect(() => {
    fetchUser(userId).then(setUser);
  }, [userId]);

  return <div>{user?.name}</div>;
}

重构之后,通过将 fetchUserData 注入组件,我们可以轻松地交换实现以进行测试或用于不同的用例。

代码语言:javascript
代码运行次数:0
复制
function UserComponent({ userId, fetchUserData }) {
  const [user, setUser] = useState(null);

  useEffect(() => {
    fetchUserData(userId).then(setUser);
  }, [userId, fetchUserData]);

  return <div>{user?.name}</div>;
}

// Usage
<UserComponent userId={1} fetchUserData={fetchUser} />;

结论

SOLID 原则对于确保您的代码干净、可维护且可扩展非常有效,即使在 JavaScriptTypeScript 框架中也是如此。应用这些原则使开发人员能够编写灵活且可重复使用的代码,这些代码易于随着需求的发展而扩展和重构。通过遵循 SOLID,您可以使您的代码库变得强大并为未来的增长做好准备

本文翻译的原文地址:Applying SOLID Principles in JavaScript and TypeScript Framework

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2024-10-28,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 萌萌哒草头将军 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 介绍
  • 1.单一职责原则 (Single Responsibility Principle, SRP)
  • 2.开放/封闭原则(OCP)
  • 3.里氏替换原则(LSP)
  • 4.接口隔离原则(ISP)
  • 5.依赖倒置原则(DIP)
    • 结论
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档