惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

大猫的无限游戏
大猫的无限游戏
Security Archives - TechRepublic
Security Archives - TechRepublic
T
Threat Research - Cisco Blogs
A
Arctic Wolf
S
Securelist
O
OpenAI News
T
Threatpost
Forbes - Security
Forbes - Security
N
News and Events Feed by Topic
S
Secure Thoughts
H
Heimdal Security Blog
S
Security Affairs
P
Privacy International News Feed
C
Cisco Blogs
C
CERT Recently Published Vulnerability Notes
Cyberwarzone
Cyberwarzone
N
News and Events Feed by Topic
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
P
Palo Alto Networks Blog
S
Security @ Cisco Blogs
Hacker News - Newest:
Hacker News - Newest: "LLM"
博客园 - 三生石上(FineUI控件)
月光博客
月光博客
T
Tailwind CSS Blog
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Hacker News: Ask HN
Hacker News: Ask HN
T
Troy Hunt's Blog
S
SegmentFault 最新的问题
腾讯CDC
V
Visual Studio Blog
Last Week in AI
Last Week in AI
H
Hacker News: Front Page
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Project Zero
Project Zero
WordPress大学
WordPress大学
NISL@THU
NISL@THU
博客园 - 【当耐特】
博客园 - Franky
Webroot Blog
Webroot Blog
博客园_首页
T
Tenable Blog
雷峰网
雷峰网
Google Online Security Blog
Google Online Security Blog
阮一峰的网络日志
阮一峰的网络日志
V2EX - 技术
V2EX - 技术
Recent Commits to openclaw:main
Recent Commits to openclaw:main
L
Lohrmann on Cybersecurity
The Hacker News
The Hacker News

Posts on WKLKEN THINKING

apisix 中的 lrucache apisix 中的服务发现机制 apisix 中的负载均衡 apisix etcd机制 聊聊框架 关于 k8s 的 zero downtime deployment 一些建议 apisix 遇到的一些问题 关于在除夕前一天换了一个洗衣机的故事 Django DRF 性能优化 DRF 的一些实践 Part1: Serializer DRF继承关系图 Better Code: 关于接口的灵活性 新的仓库: wklken/naming 缓存使用的一些经验 Better Code: 异常时, 该提示用户哪些信息? Better Code: 更好的异常日志打印 Go: some libs Go: go-redis/cache升级的坑 Go: logrus性能提升 Go: gin validation 远程办公的一点总结 Go: 开发过程中的一些bug 项目管理实践: 风险驱动开发 Go: 一种error wrap调用链处理方式 漫谈技术选型 Go: 基于 apitest 做handler层单元测试 Go: go-sql-driver interpolateparams参数优化 [分享]深度工作 你需要更多的思考时间 Django项目重构小结 工作七年小结: 学习,生活及其他 [分享]bash日常: bash-utils 极客时间推广海报 2017总结: 予时光以意义 k8s APIServer源码: api注册详细细节 k8s APIServer源码: api注册主体流程 k8s APIServer源码: 服务启动 k8s APIServer源码: go-restful框架 重构 - 读书笔记(Python示例) 写给新人的沟通建议 vim 杂谈 - 关于快速编辑 vim 杂谈 - 关于移动 读书笔记-重构: 章11 处理概括关系 读书笔记-重构: 章10 简化函数调用 读书笔记-重构: 章9 简化表达式 读书笔记-重构: 章8 重新组织数据 读书笔记-重构: 章7 在对象之间搬移特性 读书笔记-重构: 章6 重新组织函数 Python 代码规范小结 [分享]关于vim ElasticSearch集群部署文档 Logstash+ElasticSearch处理mysql慢查询日志 [分享]关于代码调试DE那些事 Logstash+ElasticSearch+Kibana- 实现相对通用的数据收集分析 ELK维护的一些点(二) [分享]Python源码剖析-数据结构 一些Centos Python生产环境的部署命令 摘录<<6个月学会任何一种外语>> ELK 维护的一些点 也许是一个新的开始 一些vim的个性化配置 读书笔记-调试九法 这段时间的一些想法 Python 源码阅读 - 垃圾回收机制 我为什么要写博客 APUE笔记-第一章 UNIX基础知识 Python源码阅读-闭包的实现 Python源码阅读-内存管理机制(二) Python源码阅读-内存管理机制(一) Python-基础-数据结构小结 '活动'设计的一些trick 一些简单的Python测试题 我的tmux配置及说明【k-tmux】 Review and Restart 工作四周年小结 vim插件: surround & repeat[成对符号编辑] vim插件: gundo[时光机] vim插件: expand-region[区域选中] vim插件: quickrun[快速执行] vim插件: trailing-whitespace[行尾空格处理] vim插件: closetag[成对标签补全] vim插件: ctrlp[文件搜索] vim插件: airline[状态栏增强] vim插件: theme[主题] vim插件: tagbar[大纲式导航] vim插件: nerdcommenter[快速注释] vim插件: rainbow_parentheses[括号高亮] vim插件: syntastic[语法检查] vim插件: delimitmate[符号自动补全] vim插件: matchit[成对标签跳转] vim插件: easy-align[快速对齐] vim插件: multiple-cursors[多光标操作] vim插件: vim-signature[快速标记跳转] vim插件: easymotion[快速跳转] vim插件: vundle[管理插件] Elasticsearch几个问题的解决 分享一份 Vim 简介PPT k-vim 更新9.0版本 关于知识管理工具的思考 Logstash+ElasticSearch+Kibana处理nginx访问日志
Better Code: 抽象: 可扩展性与可维护性的抉择
2022-06-21 · via Posts on WKLKEN THINKING

抽象程度

抽象程度高, 往往意味着: 灵活, 可扩展性高 但是, 这也同时意味着: 复杂, 可维护性低

而可维护性在一个项目的生命周期中又是非常重要的, 一次开发编写的代码, 可能有几十倍的阅读/变更/debug等等

所以, 需要取得平衡, 但是这个度应该如何平衡?

至下而上: 够用+保留一定可扩展

务实的做法:

  1. 实现功能, 简单/粗暴/丑陋, but it works!
  2. 明确分层/已知的概念, 然后进行抽象, 第一次重构; (分层/模块切分/类切分/方法挪动)
  3. 明确什么是当前的需求, 以及可见范围内的需求(要长远, 但是不能过远, 正常, 2-3年的量, 支持当前十倍作用的上层业务考虑)
  4. 明确变更点, 开闭原则, 变更点意味着未来的可扩展性; 抽出概念, 适当使用相关的设计模式, 进行第二次重构; 此时, 可能会多几个概念, 多一些模式.(但是不会多很多)
  5. 重新组织代码, 测试功能, 加单测等, 交付
  6. 每一次新需求或需求变更, 再来重新审视设计, 概念以及层的合并/拆分, 然后重构代码, 加新需求逻辑

特征: 代码中, 具体需求逻辑代码占比很大, 只包含少量必要的抽象/分层等

至上而下: 可能会过度设计

没有必要为不变的点抽出太多的概念/太多层, 没有必要为了未来几年都不会扩展的点增加某个设计模式;

注意: 是可能

容易过度设计的做法:

  1. 想要一步到位, 或者完美主义, 或者看得很远, 所以基于需求直接拆分出一堆层次和概念
  2. 基于概念, 通过组合/继承或者设计模式, 完成一堆父模块/基类/调用框架(此时没有具体代码)
  3. 实现一两个子类, 完成需求, 测试功能, 加单测, 交付

特征: 代码中, 抽象的代码占比很大, 大量的概念/抽象等, 只在几个具体实现类中包含实现代码

问题: 概念抽象真的有那么重要吗?

看场景

  1. 大型项目, 很重要
  2. 需求多变/变更频繁, 很重要
  3. 生命周期很长的项目, 很重要
  4. 其他, 不是那么重要

如果是一次性的任务, 那么quick and dirty搞完即可, 很少或根本没有抽象

如果是一个小型项目, 或者项目本身需求明确变更不频繁, 或者需求实现之后不怎么变半年一年一种实现. 那么, 概念抽象以及可扩展性并没有想象中那么重要, 而应该把可读性/可维护性放在前面, 保持适当的可扩展性, 某些极端场景, 甚至不考虑可扩展性

如果是中大型项目, 生命周期长, 需求复杂且多, 那么此时概念抽象就很重要了

怎么抉择?

看到这里, 并不是说, 一定要至下而上, 或者一定不能至上而下

而是需要根据很多因素决策: 看项目, 看需求, 看场景…..

  1. 项目大小
  2. 项目的生命周期
  3. 具体需求的复杂度
  4. 需求变更是否频繁? 新需求加入是否频繁? 是否有可见的需求?
  5. ……

例子: 一个需求及实现

需求: 从两个不同的服务, 同步数据到同一个表; 这两个服务的协议不一样, 获取的数据字段映射到数据库表字段也不一样, 并且不同字段有不同的处理规则

几种实现:

  1. 写两个脚本或者celery任务, 执行同步
    • 加字段/字段变更需要两边都改
  2. 写一个dataclass+两个client, client负责转换为统一格式
    • 加字段/变更字段, 改两个client即可
  3. 写一个dataclass+两个client+两个mapper, mapper 负责数据转换
    • 加字段/变更字段, 改两个mapper即可, 甚至mapper变更纯配置
  4. 抽象概念: syncer/fetcher/client/mapper/hook/helper/loader
  • 一次性的任务, 选 1(quick and dirty), 如果时间空余, 做到 2
  • 偶尔有字段变更, 选 2
  • 频繁字段变更, 选 3, 最好变成配置
  • 大型项目, N 个服务, N 种协议, N 种映射转换, 且不断有新逻辑, 初期还是最多做到 3, 逐步抽象, 最终可能演变成 4; 但是, 并不是一开始就那么多概念(至上而下), 而是不断迭代中演化出来的

所以, 以务实的方式来设计方案, 逐步随着项目迭代演化出最终的形态.

务实

从零开始学架构, 提到了三个原则: 合适/简单/演化

以务实的角度去思考

  • 合适, 但也不能太简陋/落后
  • 简单, 但是要能满足现在及未来可见范围内的需求
  • 演化, 但是底子要好, 避免无法演化只能重写