前端架构学习-1:关注点分离(SoC: Separation of Concerns)与单一职责(SRP: Single Responsibility Principle)

SoC,全称是Separation of Concerns,中文是关注点分离。软件工程中的模块化设计和这个有关。我一开始的理解是指将系统分成不同的部分,每个部分处理一个特定的功能或问题。比如,前端开发中的HTML、CSS、JavaScript各司其职,HTML负责结构,CSS负责样式,JavaScript负责行为,这就是一种关注点分离的应用吧。那在架构设计中,可能意味着将不同的业务逻辑、数据管理、用户界面分开,比如分层架构中的表现层、业务逻辑层、数据层。

然后是SRP,单一职责原则,属于SOLID原则中的一个。它的核心思想是一个类或者模块应该只有一个引起变化的原因,也就是只负责一个功能。如果一个类承担的职责过多,修改其中一个功能可能会影响到其他功能,导致代码脆弱。例如,一个类既处理用户验证又处理数据存储,这样当验证逻辑改变时,可能需要修改这个类,而数据存储的改变也会影响它,这不符合SRP。

不过,这两个概念听起来有点相似,都是关于职责划分的。它们的区别在哪里呢?可能SoC更宏观,指的是系统层面的模块划分,而SRP更针对类或模块内部的职责单一。比如,在分层架构中,SoC将系统分成不同的层,而每一层中的各个模块或类需要遵循SRP,每个类只做一件事。

1. 关注点分离(Separation of Concerns, SoC)

定义
将系统拆分为独立的部分,每个部分专注于解决一个特定的问题或功能模块,避免不同逻辑相互干扰。

核心思想

  • 模块化:将复杂系统分解为功能独立的模块(如前端中的组件、服务层、数据层)。

  • 降低耦合:各模块通过明确定义的接口通信,减少直接依赖。

  • 提高复用性:独立模块可被多个场景复用(如通用的身份验证服务)。

实际应用

  • 分层架构(表现层、业务逻辑层、数据层):

     
    // 表现层(React组件)
    function UserList() {const { users } = useUserData(); // 业务逻辑层钩子return <ul>{users.map(user => <li>{user.name}</li>)}</ul>;
    }// 业务逻辑层(自定义Hook)
    function useUserData() {const { data } = useFetch('/api/users'); // 数据层调用return { users: data };
    }// 数据层(通用请求封装)
    function useFetch(url) {const [data, setData] = useState(null);useEffect(() => {fetch(url).then(res => setData(res.json()));}, [url]);return { data };
    }
  • 技术栈分离:HTML(结构)、CSS(样式)、JavaScript(行为)。

违反示例

// 混合了UI渲染、数据获取和业务逻辑
function MessyComponent() {const [data, setData] = useState([]);useEffect(() => {// 数据获取逻辑fetch('/api/data').then(res => res.json()).then(data => {// 业务逻辑处理const filtered = data.filter(item => item.active);setData(filtered);});}, []);// UI渲染return <div>{data.map(item => <span>{item.name}</span>)}</div>;
}

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

定义
一个类、函数或模块应当只有一个引起变化的原因,即仅承担一项明确职责。

核心思想

  • 高内聚:所有代码围绕单一目标组织。

  • 低风险修改:修改一个功能不会意外破坏其他功能。

  • 明确边界:通过职责划分,提升代码可读性。

实际应用

  • 类设计

     
    // 违反SRP:一个类处理用户验证和日志记录
    class UserAuth {constructor() {this.logger = new Logger();}login(user) {// 验证逻辑...this.logger.log(`User ${user} logged in`); // 混合职责}
    }// 符合SRP:拆分验证和日志职责
    class AuthService {login(user) { /* 纯验证逻辑 */ }
    }class Logger {log(message) { /* 记录日志 */ }
    }
  • 函数设计

     
    // 违反SRP:函数同时解析数据并渲染UI
    function processAndRender(data) {const parsed = JSON.parse(data); // 解析document.getElementById('output').innerHTML = parsed.value; // 渲染
    }// 符合SRP:拆分职责
    function parseData(data) {return JSON.parse(data);
    }function renderOutput(content) {document.getElementById('output').innerHTML = content;
    }

3. SoC 与 SRP 的关系与区别

维度 关注点分离(SoC) 单一职责(SRP)
作用范围 系统架构层面(模块/层/服务) 代码单元层面(类/函数/组件)
核心目标 功能模块间的解耦 代码单元内的职责纯净性
典型应用 微服务架构、分层设计 类设计、函数拆分
违反后果 系统难以扩展和维护 代码脆弱、修改风险高

协同工作示例
在微前端架构中:

  1. SoC:将系统拆分为多个独立子应用(如订单、用户、商品模块)。

  2. SRP:每个子应用内部遵循单一职责(如订单模块仅处理订单相关逻辑)。

 
// 微前端主应用(SoC体现)
import { registerMicroApps } from 'qiankun';registerMicroApps([{name: 'order-app',entry: '//localhost:3001',container: '#order-container',activeRule: '/orders' // 职责:仅处理订单相关路由},// 其他子应用...
]);// 子应用内部(SRP体现)
// OrderService.js - 仅处理订单业务逻辑
class OrderService {createOrder() { /* 单一职责 */ }cancelOrder() { /* 单一职责 */ }
}

4. 如何落地实践?

步骤 1:识别职责边界

  • 通过用户故事或功能描述提取核心职责(如“用户管理”、“支付处理”)。

  • 使用事件风暴(Event Storming) 划分业务域。

步骤 2:代码重构

  • 工具辅助

    • ESLint插件(如eslint-plugin-clean-code)检测SRP违反。

    • AI工具(如GitHub Copilot)建议代码拆分。

步骤 3:设计模式强化

  • 工厂模式:分离对象创建与使用逻辑。

  • 策略模式:将算法实现与调用解耦。

     
    // 策略模式示例(符合SRP)
    const paymentStrategies = {creditCard: (amount) => { /* 信用卡支付逻辑 */ },paypal: (amount) => { /* PayPal支付逻辑 */ },
    };class PaymentProcessor {constructor(strategy) {this.strategy = strategy; // 单一职责:执行支付}execute(amount) {return this.strategy(amount);}
    }

步骤 4:持续监控

  • 使用代码复杂度分析工具(如CodeClimate)监控类/函数的职责纯度。

  • 定期架构评审,确保模块边界清晰。


5. 常见误区与解决方案

误区 后果 解决方案
过度细分产生大量微小模块 维护成本增加 按业务域聚合,使用模块命名空间
混淆技术分层与业务分层 架构混乱 明确分层标准(技术层 vs 业务层)
将SRP机械应用至每个函数 代码碎片化 允许合理聚合(如数据转换步骤组合)

6. AI工具辅助实践

  • 代码生成
    输入提示词:

    “生成一个符合SRP的React用户管理组件,分离数据获取和UI渲染”

    AI生成代码:

     
    // UserFetcher.js(数据层)
    export const fetchUsers = async () => {const res = await axios.get('/api/users');return res.data;
    };// UserList.js(表现层)
    const UserList = ({ users }) => (<ul>{users.map(user => <li key={user.id}>{user.name}</li>)}</ul>
    );// UserContainer.js(业务逻辑层)
    const UserContainer = () => {const [users, setUsers] = useState([]);useEffect(() => {fetchUsers().then(data => setUsers(data));}, []);return <UserList users={users} />;
    };
  • 架构分析
    使用Sourcegraph或CodeQL扫描代码库,识别跨层调用或巨型类。

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

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

相关文章

Markdown常用语法整理

Markdown常用语法整理 一、标题 h1 一级标题下面加=== h2 二级标题下面加--- h3 h4 h5 二、文字高亮这段文字将被高亮显示...三、插入链接或者图片 链接是前面中括号后面小括号:点击跳转百度图片是链接前面加感叹号:四、列表 无序列表(*或+或-),如:黄瓜 玉米 茄子黄瓜 玉…

P1121 环状最大两段子段和

链接 https://www.luogu.com.cn/problem/P1121 题目思路这个O(n)的思路很好:https://www.cnblogs.com/kamimxr/p/11438701.html。 关键思路: 答案分两种情况,一种是选择的两段均不跨越n到1(也就是环),另一种是选择的两段跨过了环; 如果均不跨越环,那么也就是意味着这是一道…

muduo网络库核心代码阅读(Thread,EventLoopThread,EventLoopThreadPool)(4)

muduo网络库核心设计为one loop one thread,即一个线程一个事件循环。其中,主Reactor负责监听新连接,并将新连接平均分配到从Reactor中,而从Reactor则对分配到其上的用户进行io交互,接收并处理用户发来的数据,包括消息的回复(实际上,在使用中需要手动设置Reactor线程的…

C#生成多尺寸bmp格式ICO图标代码

代码取自deepseek,且已经过本地执行测试 //.cs 文件类型,便于外部编辑时使用 // 引用必要的命名空间 using System.Collections.Generic; using System.Drawing; using System.Drawing.Imaging; using System.IO; using System.Linq; using System.Runtime.InteropServices;/…

CTA:回测执行引擎BacktestingEngine

回测执行引擎BacktestingEngine 回顾前面的文章CTA:回测综述,那里提到,真正执行回测的逻辑,写在BacktestingEngine中。 代码解读 BacktestingEngine定义在vnpy_ctastrategy -> backtesting.py中。 package from collections import defaultdict from datetime import da…

CTA:回测快速示例

设置工作目录 VNPY程序启动后,会产生一个工作目录,程序运行产生的数据、系统配置都会放在指定的.vntrader目录当中。 这一设置在vnpy -> utility.py -> _get_trader_dir函数中可以找到,工作目录由TRADER_DIR, TEMP_DIR确定。 def _get_trader_dir(temp_name: str) -&g…

VNPY体系结构

整体架构每个层次的模块只调用下层功能,并对上层提供接口。接口层负责对接行情和交易API,将行情数据推送到系统,发送交易指令。 引擎层向下对接交易接口、数据库,向上服务于各种上层应用。 应用层主要是面向用户的可视化界面,这一部分的代码主要为引擎功能与界面的连接。功…

【ABP】项目示例(2)——聚合根和实体

聚合根和实体 在上一章节中,已经完成了项目搭建的前置准备,在这一章节中,实现领域层的聚合根和实体 创建名称为General.Backend.Domain的标准类库,分别新建名称为Entities、Services、IRepositories和Specifications的文件夹,用于存放实体和聚合根、领域服务、仓储接口和规…

P1896 [SCOI2005] 互不侵犯(状态压缩)

位运算符好麻烦,没打括号被卡了半天 #include<iostream> #define int long long using namespace std; int f[12][100][1<<11]; int s[1<<11]; int num[1<<11]; signed main(){int n,k;cin>>n>>k;int cnt=0;for(int i=0;i<(1<<…

200N03-ASEMI豆浆机专用MOS管200N03

200N03-ASEMI豆浆机专用MOS管200N03编辑:ll 200N03-ASEMI豆浆机专用MOS管200N03 型号:200N03 品牌:ASEMI 封装:TO-252 批号:最新 最大漏源电流:200A 漏源击穿电压:30V RDS(ON)Max:1.8mΩ 引脚数量:3 芯片个数: 沟道类型:N沟道MOS管、中低压MOS管 漏电流:ua 特性:…