Define the Missing

在編寫程序時(shí),我給自己設(shè)立這樣的一個(gè)限制: 所有的程序都只可以編寫一次,當(dāng)你認(rèn)為程序?qū)懲瓴⑦\(yùn)行后,便不能再次修改并重啟了,然后,程序要盡可能對(duì)需求的擴(kuò)展做出正確的回應(yīng)。

場(chǎng)景1:

需要編寫一個(gè)名為 Config 的類,通過傳入 Hash 對(duì)象來實(shí)例化,傳入的 Hash 中規(guī)定了兩個(gè)鍵值對(duì)來代表程序所用的時(shí)間和空間復(fù)雜度。并且,實(shí)例可以用 "點(diǎn)" 的方式調(diào)用 Hash 中的值,即:

config = Config.new({time: "O(1)",  "space" => "O(N)"})
config.time # => "O(1)"
config.space # => "O(N)"

如果用靜態(tài)的眼光來考慮這個(gè)問題,可以把 Config 寫成這樣:

  • 方案一
class Config
  attr_reader :time, :space
  def initialize(hash)
    @time = hash[:time] || hash['time']
    @space = hash[:space] || hash['space']
  end
end

這樣做當(dāng)然沒問題,但假如這時(shí) config 要增加一個(gè)名為 version 的屬性來存儲(chǔ)語言的版本,依照上述這種方法就得在 attr_accessor 后加上 :version, 對(duì)應(yīng)的初始化方法再加上一行。如果再有更多的新屬性要添加,那就要不停地重復(fù)這樣的過程。需要注意的是,每次執(zhí)行這個(gè)過程程序是需要被重啟的,所以這種方案不符合我們的編寫目標(biāo),當(dāng)然,也不符合 DRY 的原則。

使用 method_missing 來實(shí)現(xiàn)。

  • 方案二
class Config
  attr_reader :hash_data

  def initialize(hash={})
    @hash_data = hash
  end

  def method_missing(method)
    # 可以通過正則檢查方法名稱是否攜帶 '=' 來生成 set 方法 
    # 本處只演示 get 方法
    hash_data[method.to_s] || hash_data[method]
  end
end

這段代碼也達(dá)成了場(chǎng)景1的需求,而在屬性值增長時(shí),使用 method_missing 代碼量始終可以維持不變,并且,在這一過程中,程序可以保持不重啟。

相對(duì)于 method_missing 在 Ruby 的名氣, const_missing 這個(gè)方法就顯得默默無聞了,當(dāng)然也因?yàn)槭褂玫膱?chǎng)景的確不多。這個(gè)方法是在當(dāng)前命名空間找不到對(duì)應(yīng)的常量名時(shí)會(huì)觸發(fā)的hook 方法,一般來說,若沒有做任何處理,解釋器便會(huì)返回 uninitialized constant,如:

module Asd
  A = 1
  class C
  end
end
Asd::A # => 1
Asd::C # => Asd::C
Asd::B #= uninitialized constant Asd::B

通過覆寫對(duì)應(yīng)命名空間的 const_missing 方法便可以對(duì)不存在的常量進(jìn)行操作,比如在文件變動(dòng)時(shí),通過 load 新文件來加載新的類(只是我這么用過)。

但 missing 方法其實(shí)不僅僅是方法,我認(rèn)為也是一種理念,就是用發(fā)展的眼光來看待程序,對(duì)未發(fā)生但可能發(fā)生的事件做統(tǒng)一的處理,以不變應(yīng)萬變。

場(chǎng)景2 :

編寫一個(gè) HTTP 的 API,使得 '.../xx/a' 作為客戶a提交的地址, '..../xx/b' 作為客戶b提交的地址(假設(shè)無法規(guī)定客戶提交的參數(shù)所以如此設(shè)計(jì))。

方案一, 依然先以只解決現(xiàn)有問題的靜態(tài)策略寫出這個(gè) API :

# Use Rack
class MyApi
  def call(env)
    req = Rack::Request.new(env)
    case req.path_info
    when '/xx/a'
      [200, {"Content-Type" => "text/html"}, ["Hello a!"]]
    when '/xx/b'
      [200, {"Content-Type" => "text/html"}, ["Hello b!"]]
    else
      [404, {"Content-Type" => "text/html"}, ["Can't find!"]]
    end
  end
end

run MyApi.new

大多 API 都會(huì)考慮這樣的設(shè)計(jì): 寫好特定的路由給與調(diào)用,否則的話就返回 404。但在這個(gè)場(chǎng)景中,有個(gè)潛在的需求,客戶(即a,b)的數(shù)量并不是不變的,可能會(huì)增加也會(huì)減少,而我們希望程序啟動(dòng)一次后就能適應(yīng)這些改變,該怎么做呢?

不妨按照上文中的 missing 理念,在找不到路由的時(shí)候去動(dòng)態(tài)的生成路由。而在這邊代碼中所謂的“找路由”,其實(shí)就是匹配 req.path_info 而已。我們可以在數(shù)據(jù)庫存儲(chǔ)每個(gè)客戶提交的路由地址,通過每次調(diào)用得到的 path_info, 尋找對(duì)應(yīng)的客戶是否存在,若存在,就可以給與對(duì)應(yīng)的響應(yīng)。

方案二:

class MyApi
  # 數(shù)據(jù)庫連接
  DB.connect! 
  def call(env)
    # 假設(shè)使用了 ActiveRecord 并建立了 Customer 的模型
    customer = Customer.find_by(path: req.path_info) 

   # 返回的內(nèi)容都可以在數(shù)據(jù)庫讀取,這樣更加靈活
    if customer
     [200, {"Content-Type" => customer.content_type}, [customer.response]]
    else
     [404, {"Content-Type" => "text/html"}, ["Can't find!"]]
    end
  end
end

run MyApi.new

這樣,現(xiàn)在這個(gè) API 便可以根據(jù)數(shù)據(jù)庫中客戶的信息‘動(dòng)態(tài)的產(chǎn)生路由’了。順便提及一下,使用 Grape 框架應(yīng)該怎么做到這點(diǎn),當(dāng)然思路還是一樣的,我們需要覆寫捕獲找不到路由的方法

# Rescue 404 Route In Grape 
route :any, '*path' do
  # do anything by req.path, database, etc..
end

結(jié)語: 對(duì)缺失的定義,可以很大程度提高程序、系統(tǒng)的適應(yīng)能力,減少代碼的數(shù)量。不僅只用在元編程中,在系統(tǒng)的各個(gè)環(huán)節(jié)都應(yīng)引入這種思想。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,506評(píng)論 19 139
  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 178,765評(píng)論 25 709
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法,類相關(guān)的語法,內(nèi)部類的語法,繼承相關(guān)的語法,異常的語法,線程的語...
    子非魚_t_閱讀 34,625評(píng)論 18 399
  • 最近部門有幾名年輕人陸續(xù)跳槽,問其原因,他們說自己懷才不遇,能力不比別人差,憑什么別人的待遇就比自己好呢? ...
    April會(huì)飛的豬閱讀 207評(píng)論 0 2
  • 我欠缺的就是這個(gè),最近這段時(shí)間挺煩的,歸根說還是自己能力不夠,處事還不夠好,總能把很好的想法表達(dá)出來成一種讓人討厭...
    徒步旅人閱讀 332評(píng)論 0 0

友情鏈接更多精彩內(nèi)容