最近在做一些项目重构的工作,看了不少脏乱差的代码,身心疲惫。本文将讨论如何编写整洁的代码,不求高效运行,只求可读性强,便于维护。
作为一个合格的程序员,写出简洁的代码是基本的职业素养。相信绝大部分的程序员都不会故意写恶心代码的,无论是对自己或者对别人都没有任何好处。那么,是什么阻碍我们写出优秀代码呢?有下面这么几种可能性:
出来混迟早要还的,无论是上述哪种原因,混乱代码一旦被写出来,代码作者肯定是要为其买单的,只是买单的方式会各有不同。可能是后期维护的时候边改边抽自己,也可能是别人改代码的时候边改边骂你傻x。
那么,代码写好了会有什么好处呢?起码有以下几方面:
既然有这么多好处,那到底怎么评判代码写得好不好呢?是自己觉得好就是好吗?显然不是。代码写得是否整洁是客观的,是 code review 的人或后期维护的人觉得好才是真的好。所以加强 code review 也是倒逼写出优秀代码的一种方式。
个人认为代码的优秀程度分以下几个层次:
层次越高,难度越大,挑战越大。作为一个有追求的程序员,我们应该不断突破自己的边界,追求卓越,更上一层楼。
要想写出优雅整洁的代码,就要遵循特定的设计原则。透彻理解这些原则后,还要结合具体的项目落地,不断的练习和重构。下面总结出的一些通用原则供参考。
KISS(keep it simple, stupid)
业务逻辑要直截了当,不要引入各种依赖,多层次调用。以 react 为例,常见的错误是将props在state里存一份,计算的时候再从state中取。这样带来的问题是要时刻监听props的变化,然后再同步到state中。这完全是多此一举,直接用props进行计算即可。
// bad
componentWillReceiveProps(nextProps) {
this.setState({num: nextProps.num});
}
render() {
return(
<div>{this.state.num * 2}</div>
);
}
/***************************/
// good
render() {
return(
<div>{this.props.num * 2}</div>
);
}
不用做机械式的复制粘贴,要锻炼自己抽象的能力,尽量将通用的逻辑抽象出来,方便日后重用。
Open/Closed (open to extension but closed to modification)
代码要对扩展开放,对修改封闭。尽量做到在不修改原有代码的基础上,增加新的功能。React 的容器组件和展示组件分离用的就是这种思想。当数据来源不同的时候,只需要修改或新增容器组件,展示组件维持不变。
function Comp() {
...
}
class ContainerComp extends PureComponent {
async componentDidMount() {
const data = await fetchData();
this.setState({data});
}
render() {
return (<Comp data={this.state.data}/>);
}
}
从架构层面说,微内核架构也是遵循这一设计原则。它能保证核心模块不变的情况下,通过插件机制为系统赋予新的能力。我们常用的 webpack 就是一个很好的例子,它通过引入 loader 和 plugin 的机制,极大的扩展了其文件处理的能力。
Composition > Inheritance
首先说一下类这个概念。本质上来说,定义类就是为了代码的复用,对于需要同时创建多个对象实例的情况下,这种设计模式是非常有效的。比如说连接池中就需要同时存在多个连接对象,方便资源复用。而对于前端来说,绝大部分的业务场景都是单例,这种情况下通过定义工具函数,或者直接使用对象字面量会更加高效。工具函数尽量使用纯函数,使代码更易于理解,不用考虑副作用。这里说的仅限于业务代码的范畴,如果是框架型的项目,场景会复杂得多,类会更有用武之地。
既然类都不需要用了,继承就更无从谈起了。继承的问题是多级继承之后,定位问题会非常困难,要一级一级往上找才能找到错误出处。而组合就没有这种问题,因为多个功能都是平级的,哪里出问题一眼就能看出来。比较一下下面 2 种风格的代码:
继承的写法:
class Parent extends PureComponent {
componentDidMount() {
this.fetchData(this.url);
}
fetchData(url) {
...
}
render() {
const data = this.calcData();
return (
<div>{data}</data>
);
}
}
class Child extends Parent {
constructor(props) {
super(props);
this.url = 'http://api';
}
calcData() {
...
}
}
组合的写法:
class Parent extends PureComponent {
componentDidMount() {
this.fetchData(this.props.url);
}
fetchData(url) {
...
}
render() {
const data = this.props.calcData(this.state);
return (
<div>{data}</data>
);
}
}
class Child extends PureComponent {
calcData(state) {
...
}
render() {
<Parent url="http://api" calcData={this.calcData}/>
}
}
哪种更易于理解呢?
Single Responsibility
遵循单一职责的代码如果设计得好,组合起来的代码就会非常的清爽。举一个注册的场景,可以划分为下面几个职责:
伪代码如下:
// UI.js
export default function UI() {
...
}
// api.js
export function regist(name, email) {
...
}
// validate.js
export function validateName(name) {
...
}
export function validateEmail(email) {
...
}
// Regist.js
export default class Regist extends PureComponent {
...
onSubmit = async () => {
const {name, email} = this.state;
if (validateName(name) && validateEmail(email)) {
const resp = await regist(name, email);
...
}
}
render() {
<UI onSubmit={onSubmit}>
}
}
可以看到聚合层的代码非常简洁,哪里出问题了就到相应的地方改就好了,即使是多人协作,也不容易出问题。
Separation of Concerns
关注点分离原则跟单一职责原则有点类似,但更强调的是系统架构层面的设计。典型的例子就是 MVC 模式,Model、View、Control 三层之间都有明确的职责划分,做到了高内聚低耦合。
React 的源码设计也是基于这一原则,分为ReactElement, ReactCompositeComponent 和 ReactdomComponent 三层。ReactElement 负责描述页面的 DOM 结构,也就是著名的 Virtual DOM;ReactCompositeComponent 处理的是组件生命周期、组件 Diff 和更新等逻辑;而ReactDomComponent是真正进行 DOM 操作的类。三层之间分工明确,紧密协作,共同组成了一个强大的前端框架。
Clean Code > Clever Code
提倡简洁易懂的代码,而不是晦涩难懂的“聪明”代码,如下面这种:
let a, b=3, t = (a=2, b<1) ? (console.log('Y'),'yes') : (console.log('N'),'no');
文件一旦超过 200 行,说明逻辑已经有点复杂了,要想办法抽离出一些纯函数工具方法,让主线逻辑更加清晰。工具方法可以放在另外的文件里面,减少读代码的心理压力。有一点需要说明一下,并不是所有的文件都不能超过 200 行,像工具方法这种,都是各自独立的逻辑,写多少行都无所谓。需要控制的是紧密关联的业务代码的行数。
上面提到要合理的拆分代码,那到底怎么拆呢?对于前端组件代码,有下面一些拆分点以供参考:
需要说明的是展现逻辑和业务逻辑是两回事,最好不要混在一起写。比如组件的显示隐藏是展现逻辑,而数据的校验就是业务逻辑。
本文讨论了书写整洁代码的必要性和重要性,结合实例列出了一些设计原则,还给出了组件代码拆分的方式。程序员的职业生涯是一个自我修炼的过程,时刻关注代码质量,是提高技术水平的重要一环。
来自:https://segmentfault.com/a/1190000019667433
一个系统可以维持5年,10年,甚至20年以上,但是代码和设计模式的生命周期非常短,当对一个解决方案使用不同的方法进行迭代的时候,通常只能维持数月,数日,甚至几分钟的时间
良好的编程习惯涉及到很多方面,但在软件行业内,大多数的公司或组织都不会把良好的编程习惯列为主要关注点。 例如,具有可读性和可维护性的代码比编写好的测试代码或使用正确的工具更有意义,前者的意义在于可以让代码更易于理解和修改。
减少嵌套会让代码可读性更好,同时也能更容易的找出bug,开发人员可以更快的迭代,程序也会越来越稳定。简化代码,让编程更轻松!
Google为了那些还不熟悉代码规范的人发布了一个JS代码规范。其中列出了编写简洁易懂的代码所应该做的最佳实践。代码规范并不是一种编写正确JavaScript代码的规则,而是为了保持源代码编写模式一致的一种选择。
程序员似乎忘记了软件的真正目的,那就是解决现实问题。您编写的代码的目的是为了创造价值并使现有世界变得更美好,而不是满足您对自我世界应该是什么的以自我为中心的观点。有人说:如果你拥有的只是一把锤子,那么一切看起来都像钉子一样
TinyMCE是一个轻量级的基于浏览器的所见即所得编辑器,由JavaScript写成。它对IE6+和Firefox1.5+都有着非常良好的支持。功能方强大,并且功能配置灵活简单。另一特点是加载速度非常快的。
函数式编程对应的是命令式编程, 函数式编程的核心当然是对函数的运用. 而高阶函数(Higher-order)是实现函数式编程的基本要素。高阶函数可以将其他函数作为参数或者返回结果。所以JS天生就支持函数式编程
朋友发表了一条说说:入职新公司,从重构代码到放弃”,我就问他怎么了?他说,刚进一家新公司,接手代码太烂,领导让我先熟悉业务逻辑,然后去修复之前项目中遗留的bug,实在不行就重构
页面实现关键词高亮显示:在项目期间遇到一个需求,就是搜索关键词时需要高亮显示,主要通过正则匹配来实现页面关键词高亮显示。在搜索结果中高亮显示关键词:有一组关键词数组,在数组中筛选出符合关键字的内容并将关键字高亮
软件工程学什么? 学计算机,写程序,做软件,当程序员。听说学计算机很辛苦? 是的,IT行业加班现象严重。在计算机世界里,技术日新月异,自学能力是程序员最重要的能力之一。选了这个专业,就要时刻保持好奇心和技术嗅觉,不能只满足于完成课内作业。
内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!