【譯】和整潔代碼說再見

本文是我在 Hacker News 上看到的一篇文章。原文講述了做者對整潔代碼的一些思考。本人在學生時期看過一些關於代碼風格的書,好比《高質量程序設計指南》、《代碼大全》、《代碼整潔之道》等等。也養成了本身對優雅、簡潔代碼的偏執,相信不少程序員小夥伴都有這種偏執。但工做後,隨着代碼經驗的積累和同事的打臉,開始反思本身堅持的風格。偶然看到 Hacker News 上的這篇文章,和做者有點感同身受,不由自主想把這篇文章分享給你們。react

原文地址:https://overreacted.io/goodbye-clean-code/程序員

原文在 Hacker News 上引起的討論:https://news.ycombinator.com/item?id=22022466微信

如下是譯文。編輯器

那是一個深夜。ide

個人同事們剛剛提交了他們花了整整一週編寫的代碼。咱們的需求是在圖形編輯器畫布上工做,同事們剛提交的代碼實現了經過拖動圖形邊緣的小手柄來調整矩形、橢圓等形狀。函數

代碼工做良好。可是有重複代碼。每一個形狀(例如矩形和橢圓)都有一組不一樣的手柄,在不一樣的方向上拖動每一個手柄,會以不一樣的改變形狀的位置和大小。若是用戶按住 Shift 鍵,咱們還須要在調整形狀大小時保持原來的比例。這裏面有一堆數學問題。工具

代碼大概是這樣的:翻譯

let Rectangle = {
  resizeTopLeft(position, size, preserveAspect, dx, dy) {
    // 這裏省略 10 行重複的數學代碼
  },
  resizeTopRight(position, size, preserveAspect, dx, dy) {
    // 這裏省略 10 行重複的數學代碼
  },
  resizeBottomLeft(position, size, preserveAspect, dx, dy) {
    // 這裏省略 10 行重複的數學代碼
  },
  resizeBottomRight(position, size, preserveAspect, dx, dy) {
    // 這裏省略 10 行重複的數學代碼
  },
};

let Oval = {
  resizeLeft(position, size, preserveAspect, dx, dy) {
    // 這裏省略 10 行重複的數學代碼
  },
  resizeRight(position, size, preserveAspect, dx, dy) {
    // 這裏省略 10 行重複的數學代碼
  },
  resizeTop(position, size, preserveAspect, dx, dy) {
    // 這裏省略 10 行重複的數學代碼
  },
  resizeBottom(position, size, preserveAspect, dx, dy) {
    // 這裏省略 10 行重複的數學代碼
  },
};

let Header = {
  resizeLeft(position, size, preserveAspect, dx, dy) {
    // 這裏省略 10 行重複的數學代碼
  },
  resizeRight(position, size, preserveAspect, dx, dy) {
    // 這裏省略 10 行重複的數學代碼
  },  
}

let TextBlock = {
  resizeTopLeft(position, size, preserveAspect, dx, dy) {
    // 這裏省略 10 行重複的數學代碼
  },
  resizeTopRight(position, size, preserveAspect, dx, dy) {
    // 這裏省略 10 行重複的數學代碼
  },
  resizeBottomLeft(position, size, preserveAspect, dx, dy) {
    // 這裏省略 10 行重複的數學代碼
  },
  resizeBottomRight(position, size, preserveAspect, dx, dy) {
    // 這裏省略 10 行重複的數學代碼
  },
};

這些重複出現的數學運算讓我心煩。一點都不整潔。設計

大多數重複代碼都發生在類似的方向上。例如,Oval.resizeLeft()Header.resizeLeft() 的代碼相似。由於它們都要處理左邊的拖拽操做。code

另外,同一形狀的不一樣方法也有類似之處。例如,Oval.resizeLeft()Oval 的其餘方法也有類似之處。由於它們的處理對象都是橢圓。矩形、HeaderTextBlock(文本塊) 也有重複的地方,由於文本塊也是矩形。

我想到了一個好主意。咱們能夠經過如下代碼分組來消除全部的重複:

let Directions = {
  top(...) {
    // 這裏省略 5 行獨特的數學代碼
  },
  left(...) {
    // 這裏省略 5 行獨特的數學代碼
  },
  bottom(...) {
    // 這裏省略 5 行獨特的數學代碼
  },
  right(...) {
    // 這裏省略 5 行獨特的數學代碼
  },
};

let Shapes = {
  Oval(...) {
    // 這裏省略 5 行獨特的數學代碼
  },
  Rectangle(...) {
    // 這裏省略 5 行獨特的數學代碼
  },
}

而後組合它們:

let {top, bottom, left, right} = Directions;

function createHandle(directions) {
  // 這裏省略20行代碼
}

let fourCorners = [
  createHandle([top, left]),
  createHandle([top, right]),
  createHandle([bottom, left]),
  createHandle([bottom, right]),
];
let fourSides = [
  createHandle([top]),
  createHandle([left]),
  createHandle([right]),
  createHandle([bottom]),
];
let twoSides = [
  createHandle([left]),
  createHandle([right]),
];

function createBox(shape, handles) {
  // 這裏省略20行代碼
}

let Rectangle = createBox(Shapes.Rectangle, fourCorners);
let Oval = createBox(Shapes.Oval, fourSides);
let Header = createBox(Shapes.Rectangle, twoSides);
let TextBox = createBox(Shapes.Rectangle, fourCorners);

代碼量減小了一半,徹底沒有重複代碼!很整潔。若是想要在某個特定方向或針對某個特定形狀作修改,只須要修改一個對應的方法,而不須要修改每一個形狀的對應的方法或者某個形狀全部的方法。

夜已經很深了。我向 master 分支提交了我重構後的代碼,而後就上牀睡覺,我爲本身解決了同事雜亂的代碼而感到自豪。

次日早晨

...沒有發生我預期的劇情(爲何沒人誇我)。

老闆找我進行了一對一的談話,他們(老闆和同事)很禮貌的要求我撤銷我提交的代碼更改。我驚呆了。舊代碼一團糟,個人代碼多整潔!

我勉強答應了,可是過了幾年我才發現他們是對的。

一個必經階段

癡迷於「整潔的代碼」和刪除重複代碼是咱們不少人都會經歷的一個階段。當咱們對本身的代碼不自信時,很容易將自我價值和職業自豪感與一些能夠衡量的東西關聯起來。好比,一套嚴格的 lint 規則,一個命名模式,一個文件結構,消除重複等等。

譯者注:Lint 指一些列靜態代碼分析工具,可以發現代碼中潛在的缺陷和質量問題。

你不能自動刪除重複代碼,但經過實踐會變得愈來愈容易。你一般能夠分辨每次更改後代碼是多了仍是少了。所以,刪除重複代碼感受就像提升了某些和代碼相關的客觀指標。更糟糕的是,它與咱們的認同感交織在一塊兒:「我就是那種編寫簡潔代碼的人」。這種認同感和任何形式的自我欺騙同樣強大。

一旦咱們學會了如何建立抽象,就很容易對這種能力產生很高的指望,而且每當咱們看到重複代碼時就會憑空想象出抽象。寫幾年代碼後,咱們發現處處都是抽象——抽象就是咱們新的超能力。若是有人告訴咱們抽象是一種美德,咱們就會全盤接受,而且會批評那些不崇尚「整潔」的人。

如今我發現我以前的「重構」在兩方面作的很糟糕:

  • 首先,我沒有和寫代碼的人溝經過。沒有了解他們的想法和意圖就本身重寫了代碼並提交。即便我提交的代碼是一種進步(我如今不信了),這也是種糟糕的方式。一個健康的工程團隊須要不斷創建信任。在沒有溝通的狀況下重寫隊友的代碼,極大的下降你在代碼庫中和隊友有效協做的能力。
  • 其次,任何事都有代價。個人代碼減小了重複代碼,但犧牲了應對須要變動的能力,明顯不划算。例如,咱們後續須要爲不一樣的形狀上不一樣的句柄添加不少特殊的狀況和行爲。個人抽象代碼必須變得複雜好幾倍才能應對這種需求,而在最初「混亂」的版本中,這種更改就像切蛋糕同樣簡單。

我是在說你必須寫「髒」的代碼嗎?不,我是在建議你認真思考,當咱們討論「整潔」和「髒」時,咱們究竟是在討論什麼。你有反抗的感受嗎?正義?美麗?優雅?你肯定你能列出和這些品質相對應的具體工程成果嗎?它們究竟佮如何影響了咱們編寫和修改代碼的方式?

我以前確實沒有深刻思考過這些東西。我對代碼的外表思考了不少——但沒有考慮它是如何在一羣糊塗的人手中演變出來的。

寫代碼就像一段旅程。想一想你從第一行代碼到如今已經走了多遠。我理解第一次看到如何提取一個函數或重構一個類能夠簡化複雜的代碼時是多麼有趣。若是你對本身的手藝感到自豪,追求誘人的整潔代碼。你能夠逗留一段時間。

可是不要止步於此。不要作一個整潔代碼的狂熱分子。整潔的代碼不是目標。整潔的代碼只是咱們處理極其複雜的系統時的一種嘗試。當你不肯定更改將對代碼庫形成什麼樣的影響時,整潔的代碼風格是一種防護機制,指導咱們在未知的海域航行。

讓整潔代碼風格指導你。該忘記的時候就忘記吧。

譯者後記:翻譯完這篇文章,發現原做者的思惟有點跳躍。從一次擅改代碼被打臉,到後面關於整潔代碼一大段思考,中間過了幾年時間,這幾年時間做者確定還經歷不少其餘事情,這些事情加在一塊兒才能得出本文後面一大段思考和結論。不知道你有沒有相似的經歷和感悟,歡迎討論。

若是習慣在微信公衆號上閱讀,歡迎關注個人公衆號:不僅是Python 之後會繼續在開源中國和微信公衆號上同步更新文章 微信公衆號:不僅是Python

相關文章
相關標籤/搜索