菜雞與物件導向 (11): 開放封閉原則

開放封閉原則 (Open-Close Principle)

軟體實體(類別、模組、函式等等)應該對擴展開放,而對修改封閉

在我們了解什麼是「對擴展開放」和「對修改封閉」之前,先讓我們談談:什麼是擴展,什麼又是修改呢?

用白話一點的方式來形容,修改就是把東西拆開來改,像是手術;而擴展就是對東西額外加裝模組,像是添購設備。我們用飛行來舉例,像是鳥類直接用翅膀飛行,如果有需要修改飛行方法的話就得對鳥直接進行手術;但如果今天是一個裝備了噴射背包的人,我們只需要把噴射背包換成噴射鞋子、甚至噴射翅膀就可以了,不需要去修改人這個本體。

這邊可以發現開放封閉原則是針對「改變的時候」去做一個行動的建議,例如需求追加和變更等等。凡是變化都有成本,例如變動的難易度、變動造成的影響範圍等等都會影響到成本,若是程式碼冗長、內部邏輯複雜,類別之間互相耦合、影響範圍很廣,導致綁手綁腳或壞東壞西等等狀況,使得修改很困難,成本就會變高,進而使得開發效率變低。

然而,軟體並不是製造完畢就完工的東西,而是隨需求而生、隨需求而變的動態作品,因此程式碼的修改或重構相當頻繁。就像我們在 內聚耦合篇 提過的:軟體面對改變的能力,就像基因適應環境並生存下去的能力。因此,程式必須具有彈性,也就是需要盡可能降低修改的成本。

那麼讓我們回到前面:動手術跟換道具,哪個的成本比較高呢?

面對需求,對程式碼的改動是透過增加新程式碼進行的,而不是更改現有的程式碼  
(《大話設計模式》)

……

閱讀全文



菜雞的 Markdown 筆記

Markdown 是一種寫作用語言,特色是只要用簡單的符號就可以替文章進行排版,例如 # 就代表了標題,因此能相當簡潔迅速地應用 Markdown 語法來撰寫出文件,目前已經被廣泛使用在各個撰寫文章或是文檔的場景中。

例如 Github 用來說明專案的 Readme.md,從副檔名 md 就已經告訴你這是一篇 Markdown;又像是這個部落格的文章,也都是使用 markdown 來寫的。除此之外,像是 Facebook 和 Line 都開始支援簡單的 Markdown 語法了 —— 因為它實在是太方便好用了。

使用Markdown格式撰寫的文件應該可以直接以純文字發佈,並且看起來不會像是由許多標籤或是格式指令所構成 —— markdown.tw

既然用簡單的符號就能完成這些簡潔的排版,我們自然就能把專注的重心挪回到撰寫文章本身,這也就是 Markdown 最大的魅力:專注於內容

也因為 Markdown 的特色就是非常的簡潔乾淨,文檔本身的可讀性就相當的高,撰寫起來也很直覺容易。就如同其說明文件所說的:「Markdown 的目標就是實現『易讀易寫』」

這篇就來稍微紀錄一下 Markdown 的常用語法和好用的編輯環境吧!

……

閱讀全文



外接螢幕初體驗

這週入手了新玩具,在這邊記錄下~

我們家大神大大跟我說部落格就當推特發就對了,唉呀我也是深表認同吶

像我這種被實驗室和公司寵壞的人,已經習慣了雙螢幕的好。結果不管是在放不下兩台螢幕的家裡書桌,還是帶著筆電出門,尤其是一邊寫東西一邊查資料,需要來來回回切換視窗時,總是會想「唉呀真想把這丟到另一個螢幕啊!」

因此!幾經掙扎之後,還是入手了外接螢幕!

開場先說心得,雖然尚未有外出機會,但目前的使用相當滿意。這邊就說說個人體會的好壞:

2021.01.30 補充:外出也相當方便,只是桌面的空間就需要大一點

……

閱讀全文



菜雞與物件導向 (10): 單一職責原則

我們在前面的 內聚和耦合 有提到過,內聚並不是無腦把相關的程式碼都封在一起就好了,也有分成健康的和不健康的。但我們要怎麼知道這個類別是否足夠健康呢?單一職責原則就是很好的檢驗方式,這篇就讓我們來紀錄一下。

單一職責原則 (Single Responsibility Principle)

「單一職責」原則顧名思義,就是一個類別應該只負責一個職責

但是這樣太過籠統了,「職責」相當容易產生誤會,容易變成各說各話。畢竟咱們工程師最愛戰定義了嘛。

「你這類別不優,它有兩個職責!登入跟登出!」

『沒有啦,我這個類別就是負責帳戶管理的啊』

OSSO。乾脆你全部放一起,然後說是負責網站管理算了,呵」

『……你存心來找碴的是不是?』

為了避免像這樣產生職場糾紛,我們需要先定義一下什麼是「職責」。經過前輩們的努力(解釋)之後,單一職責的定義就成了:

就一個類別而言,應該只有一個引起它變化的原因

另外,我也看過「一個類別應該只對一個角色負責」的說法,這兩者的核心概念是一樣的。

……

閱讀全文



C#: 元組 (Tuple)

因為隔壁介紹原則的部分有點卡住了,所以這週來紀錄一下挺常用到的方便東西:Tuple

這篇的 Tuple 指的是 C# 7.0 後提供的 ValueTuple 和相關語法,舊版得用 Tuple.Create 建立,成員的名稱也只能使用 Item1, Item2…,實用性並不是很高。但新 Tuple 出現後,方便程度大大提升,這邊就稍作紀錄一下。

註:此處使用的 Dump 是 Linqpad 提供的輸出方法,把它當成 Print 就行了。

var student = (1, "王小明");
student.Item1.Dump(); // 1
student.Item2.Dump(); // 王小明

student.Dump();

可以看到 Tuple 的建立相當簡單,只需要用小括號 () 括選起來即可。建立後的內容就會像這樣:

但這樣使用就和之前一樣,取出來時只能拿 Item1, Item2,放個幾天根本就不記得 Item1 裡面是啥東西了。這時我們就可以替成員們取名字

(int ID, string Name) student = (1, "王小明");
student.ID.Dump(); // 1
student.Name.Dump(); // 王小明

如此使用的時候就和一般操作物件的習慣沒有差別,也增加了可讀性。

……

閱讀全文



菜雞與物件導向 (9): SOLID

終於進入了原則篇,接下來的幾篇我們會介紹幾個物件導向的原則(基本上就是指 SOLID 原則)。因此這篇就讓我 水一下 當成後半段的目錄,方便之後可以把相關的部分整理進來。

為什麼我們需要這些原則?

我們在前面的章節已經說明了一些物件導向的特性,例如繼承和多型等等。然而我們並沒有討論到怎麼運用、或是怎樣設計才能算是更好的、更優雅的、更符合物件導向精神的;我們並沒有提到一個評估的標準,或是指引一個更好的方向。

然而,混亂的使用物件導向對整個專案的毀滅性甚至比乾脆不使用物件導向還高。

這些特性使用起來很簡單,大多數語言只需要一個符號或標示就能完成繼承,把一堆東西全部塞在一起就可以說我在封裝。但怎麼使用得好,又該什麼時候使用呢?這就是難的地方吧。

例如說濫用繼承,或是封裝時完全不隱藏複雜度一路 Puuuuublic 到底,又或者是類別之間過於相互依賴,全部耦合成一團等等。如果隨便地使用物件導向的各項特性,就會讓整個架構變得僵化、脆弱、危險、充滿臭味。

更可怕的是,這個發臭的過程是每一次設計、每一次修改都會有所影響,所謂「持續發生,腐敗成真」,隨著物件導向的亂用、誤用、無腦用,軟體就會逐漸腐化。一組腐化的軟體可能會有以下特徵:大量的依賴使得修改變得困難、修改後看似不相干的各個地方發生問題、或是修改時沒辦法依循原本的設計、到處出現不必要的複雜性和不必要的重複,模組也變得難以理解等等。

阻止程式碼的腐化、追求更好的架構和設計、寫出更好的代碼,當然是我輩所追求的目標。儘管面對的可能是不同的問題和不同的環境,那些優質、穩固、具有反脆弱特質的程式碼也必然會有些共通之處。例如說:需要具有面對改變的能力、具有方便管理的能力、具有隱藏複雜性的能力。

因此,大前輩們整理並提出了一些可以致力的方向,也就是所謂的「原則」。如同心法、教義一般,只要實作的同時將其牢記在心,就能讓我們作為一些行動的準則和依據。

……

閱讀全文



菜雞與物件導向 (8): 內聚、耦合

做為前後段落的分水嶺,這篇文章我將紀錄一下 「內聚」(Cohesion)「耦合」(Coupling),這兩者是評估一個類別或元件的重要概念。

在實務上,為了提升擴展性,降低維護成本等因素,我們對於單個類別或元件,會有著 「低耦合」「高內聚」 的期待。例如我們在 菜雞與物件導向 (3): 封裝 中,我們就有提到封裝的好壞相當重要,其中也包含了「提高類別內的內聚性,降低對外的耦合性」。那麼,到底什麼是內聚,什麼又是耦合呢?

內聚

「把需要的程式和資料都包裝在同一個模組內,使得該模組能夠做為一個單獨的個體執行」

白話一點說,就是就是把用到的東西都打包到一處,該有的自己都有了,所以即使單獨一個人也能完成工作的能力、可以自己 Carry 整場不用看豬隊友臉色的能力。越能自己單幹,越不需要依賴其他類別的時候,內聚力也就越高。

也就是說:如果你的類別什麼都要依賴其他類別,像小嬰兒一樣需要呵護照顧,那內聚力就很低。反之,如果像野外求生大師,啥都靠自己,那內聚力就超高。

內聚代表的是該模組的獨立性,當這個模組可以獨力完成工作,就代表我們能夠重複使用它,且不需要擔心影響到其他模組。

並且也基於這點,我們不用擔心變動這個模組時需要先處理其他的模組,因為這個工作所需的都包含在模組內了,這樣就可以單獨修改該模組,減少維護成本。

例如你的筆已經包含了所有寫字工具的條件,具有墨水跟筆芯等等,可以只使用筆就完成寫字這個工作。那麼我們就可以隨身帶著,在任何需要的時候重複使用它,而不用擔心我們會不會漏了什麼必要零件沒有帶出門。同時,如果我們需要換筆芯或墨水,我們也知道要更換的部份就在筆裡面,不需要去找鉛筆盒中別的地方。

……

閱讀全文



讀《先問為什麼》

如果你樂於接納新事物,希望成功能持久,也相信自己的成功需要別人的幫助

我向你提出一個挑戰 ——

從今天起,做任何事情之前,請先問自己「為什麼」。

這本書的中文副標很好地點出了本書的重點:顛覆慣性思考的黃金圈理論,啟動你的感召領導力。大多篇幅用在舉例以及逐步說明何謂感召,以及黃金圈。

本書的想法和一些例子,尤其是書中最重要的黃金圈理論,在作者上 TED 的影片「偉大的領袖如何鼓動行為」都有說明,有興趣的朋友可以直接看演講影片,足夠掌握到黃金圈理論的核心。

(偉大的領袖如何鼓動行為 - TED)

……

閱讀全文



C#: 時區轉換、民國西元、國曆農曆、中文月份週期

聊到將時間從 UTC 轉到台灣時間,居然還是聽到朋友表示使用 +8 小時的做法,驚為天人。這種做法可能會造成後續的問題,例如時區並不會跟著變動,或是遇到日光節約等特殊狀況就容易出事。和西元民國轉換直接 -1911 一樣不穩定。

這篇就用來記錄一下之前看過比較優雅的時區轉換方式,順便將先前存著的時間處理相關資料整理一下,方便之後需要時可以馬上回來查詢。

TimeZoneInfo: 時區資訊

轉換方式主要參考自 [食譜好菜] DateTime 具有文化特性的格式化及時區的轉換在各時區間轉換時間,感謝前人的指引。

關於文化特性,也可以參考本站的 菜雞抓蟲: DateTime.ToString() 之我們不一樣 & CultureInfo 文化特性小筆記 呦。

// 假設現在是要從標準時區 +00:00 轉換到台灣時區,故這邊使用 UtcNow 先取標準世界協調時間
var nowDateTime = DateTime.UtcNow;

nowDateTime.ToString("yyyy/MM/dd H:mm:ss zzz").Dump();
// 2020/08/30 15:56:05 +00:00

// ==================================================

// 傳統的 直接對時間做計算的方式…
var addedDateTime = nowDateTime.AddHours(8);

addedDateTime.ToString("yyyy/MM/dd H:mm:ss zzz").Dump();
// 2020/08/30 23:56:05 +00:00
// 可以看到儘管時間變動了,時區仍然還在 +00:00

// ==================================================

// 使用 TimeZoneInfo 先取得台北時區
var timeZone = TimeZoneInfo.FindSystemTimeZoneById("Taipei Standard Time");

// 再使用 TimeZoneInfo 來變更時間
var convertedDateTime = TimeZoneInfo.ConvertTime(nowDateTime, timeZone);

convertedDateTime.ToString("yyyy/MM/dd H:mm:ss zzz").Dump();
// 2020/08/30 23:56:05 +08:00
// 可以看到除了時間變更以外,時區也切換到 +08:00 了!

上面取得台北時區的步驟,可以參照 Time Zone IDs 來查詢想要的時區。這樣的時區切換方式,不僅副作用少,不會因為時區沒轉雷到後續接手的人,也省卻了擔心日光節約等等問題,這種事就交給微軟去煩惱吧!

……

閱讀全文



Powershell 美化作戰 —— 字型、執行原則和 oh-my-posh

最近在兩天內經歷了記憶體死去、系統毀損、機殼碎裂等等,終於電腦重灌。一堆設定都要重弄,正好也是個機會,這篇順手記一下常用好幫手 Powershell 的美化步驟。

先放一張施工後的圖鎮樓:

可以在開始圖示上用右鍵打開選單,之後點選 Powershell;或是 Win + X 打開選單,然後按 I 或 A (後者會以系統管理員身分開啟)就能開啟了。

如果選單打開還是 CMD 而不是 Powershell 的,可以先去切換成 Powershell,真的是比較好用啦(Windows Terminal 笑而不語)

剛打開的畫面是這樣的:

抱歉,光細明體我就不太行了。所以接下來就從字型這些內建設定開始!

……

閱讀全文



系列文

轉貼文

最近文章

分類

友鏈

標籤

統計資訊

工商服務

    DDDTaiwan