寫好軟件的訣竅
真實情況
真實情況是,計算機能正確的按照命令去運行。無論你寫的是“Hello World”,還是用無人飛機去殺死一個人。計算機都能精確的按照你的命令去做。
可我們的工作,我們的真正工作是:告訴程序員和我們自己:我們讓計算機做什么了。現(xiàn)代的軟件編程思想就是結(jié)構(gòu)化的、清楚的描述計算機將要執(zhí)行的任務(wù)。
事實上,計算機并不去閱讀你在程序里寫了什么,而人會。計算機把程序員寫的代碼編譯成字節(jié)比特,真正會去看你寫的是什么的只有人類。
寫軟件要像講故事
如果你對你的工作和你寫的代碼的行為有了新的認識,你會馬上很清楚的發(fā)現(xiàn),編程工作更像講故事。
想一想。你是如何知道一個人講故事沒人愛聽的?這很簡單,他老跑題,他老是糾結(jié)在不重要的細節(jié)上,他老是在故事場景中挑來跳去,等等。你立刻能知道故事被他講爛了。
雖然在***你能明白故事里發(fā)生了什么,你甚至能復(fù)述它,但你會喜歡這樣的故事嗎?你會有興趣轉(zhuǎn)述給別人或豐富故事內(nèi)容嗎?
相同的事情也發(fā)生在軟件開發(fā)中。如果你的代碼寫的含糊不清,亂七八糟,沒有人會愿意欣賞它。沒有人會愿意看它第二次。并且你是***個受它折磨的人。
訣竅
那么,現(xiàn)在你想要知道這個簡單的秘訣,不是嗎?下面就是代碼里的干擾因素越少越好
注意,我不是在討論明晰的代碼vs隱晦的代碼,不是在討論約定優(yōu)先,不是在討論元數(shù)據(jù)編程有害或其它類似的東西。
寫出好的軟件的訣竅是代碼里只寫那些能讓你的代碼講出的故事更有意義的內(nèi)容。如果它能讓你的代碼更清楚,那就這樣寫它。如果這個東西對故事沒有任何意義,那就扔了它。扔了它能讓故事更好。如果代碼耦合模塊不清,就用元數(shù)據(jù)編程和約定。
例子
有一些經(jīng)典的例子可以證明這一點。比如,描述一篇帖子和它的作者的關(guān)系。
- class Post < ActiveRecord::Base
- belongs_to :author, class_name: 'User', foreign_key: :authored_by
- end
看見了沒?所有關(guān)于類名,外鍵的信息都是干擾。去掉它們。
- class Post < ActiveRecord::Base
- belongs_to :user
- end
第二版中沒有好聽的“作者”字眼,但卻是更優(yōu)的,因為它直奔主題,用最簡短的語句告訴所有你想知道的。
另外一個例子,說一個類需要關(guān)聯(lián)那些創(chuàng)建/修改它的信息的用戶
- class Setting < ActiveRecord::Base
- belongs_to :creator
- belongs_to :editor
- attr_accessor :editing_user
- before_create :set_creator
- before_update :set_editor
- private
- def set_creator
- self.creator = @editing_user
- end
- def set_editor
- self.editor = @editing_user
- end
- end
干擾,所有的這些回調(diào)和attr_acessors都是干擾,都是垃圾信息,沒有任何價值體現(xiàn)在你想完成的任務(wù)中。更簡潔更好的方法是下面這樣寫:
- class Setting < ActiveRecord::Base
- belongs_to :creator
- belongs_to :editor
- def editing_user=(user)
- if new_record?
- self.creator = user
- else
- self.editor = user
- end
- end
- end
你可以看到它精煉的告訴了我們發(fā)生了什么。這段代碼說,這個類有一個記錄創(chuàng)建者,一個編輯者,我們用editing_user賦給它們值。沒有回調(diào)干擾。沒有幾個private方法的無用信息。
一個更經(jīng)典的例子。在controller里管理數(shù)據(jù)
- class PostsController < ApplicationController
- def create
- if params[:post][:text].present?
- if params[:post][:text] =~ /fuck|cock|shit/
- flash[:error] = "Be nice"
- @achtung = true
- end
- end
- if !@achtung
- @post = Post.new(params[:post])
- if @post.save
- flash[:success] = "Yoo hoo!"
- redirect_to :index
- else
- render :new
- end
- else
- redirect_to :index
- end
- end
- end
所有的這些條件邏輯跟你的controller實際上沒有任何關(guān)系。所有的這些邏輯判斷并不屬于controller層負責。當然,你可以這樣做,而其能正常的運行,但這不是好的軟件。
試試這樣寫
- class PostsController < ApplicationController
- def create
- @post = Post.new(params[:post])
- if @post.save
- flash[:success] = "Yoo hoo!"
- redirect_to :index
- else
- render :new
- end
- end
- end
- class Post < ActiveRecord::Base
- validate :bad_language_check
- private
- def bad_language_check
- if text =~ /fuck|shit|cock/
- errors.add(:text, "has some pretty bad language")
- end
- end
- end
現(xiàn)在你的controller能清楚的說明白發(fā)生了什么。你可以清楚的看明白當記錄可以創(chuàng)建和不能創(chuàng)建時會發(fā)生什么。跟Post類一樣,你可以清楚的理解它在過濾那些不干凈的文字。而且校驗器有自己單獨的地方。它的實現(xiàn)方式不會影響Post本身。
結(jié)論
其實很簡單。想寫出好的軟件嗎?別再給機器寫代碼,從此后為人寫代碼。
就這么簡單。
英文原文:The Trick To Good Software
譯文連接:http://www.aqee.net/the-trick-to-good-software/