iT邦幫忙

2019 iT 邦幫忙鐵人賽

DAY 10
2
Software Development

可不可以不要寫糙 code系列 第 10

「聰明」與「自作聰明」的 code

良好程式碼的優點大同小異。
不好的程式碼的糙點卻各有巧妙之處。


Photo by Alec Foege on Unsplash 使用巨石陣圖是對《人月神話》, Ch15 致敬

你是否知道,眼前的這一切是什麼作用?
你再用心、再用力一點,花點心思,是否可以看出一點端倪

沒有任何說明,也沒有任何文件的高科技精品,也就就像現在人觀看古文明一樣,充滿待人解開的謎團。(以後問人家「你在寫什麼糙 code 」可以用「你怎麼做了一個巨石陣(或金字塔)」取代(誤))

很多糙點本身,其實是來自同事的才能,他有用心而不是不用心。但是用心的地方,總是充滿創意,只是難懂!難懂就會讓人覺得糙,這種心思不被懂,雙方都無奈呀

短碼

這是一個來自 1988年 第五屆 國際混碼大賽的 westley 寫的[1]

#define _ -F<00||--F-OO--;
int F=00,OO=00;main(){F_OO();printf("%1.3f\n",4.*-F/OO/OO);}F_OO()
{
            _-_-_-_
       _-_-_-_-_-_-_-_-_
    _-_-_-_-_-_-_-_-_-_-_-_
  _-_-_-_-_-_-_-_-_-_-_-_-_-_
 _-_-_-_-_-_-_-_-_-_-_-_-_-_-_
 _-_-_-_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_
 _-_-_-_-_-_-_-_-_-_-_-_-_-_-_
 _-_-_-_-_-_-_-_-_-_-_-_-_-_-_
  _-_-_-_-_-_-_-_-_-_-_-_-_-_
    _-_-_-_-_-_-_-_-_-_-_-_
        _-_-_-_-_-_-_-_
            _-_-_-_
}

它的它的美好,但是請止於研究與自我滿足。
不要在協作時,展現這份美好

甚至於有日本人出書在介紹[2]

用文學來譬諭短碼,就像是華麗詞藻

  • 「手機」有「噩耗宛如春雷般晴天霹靂地打響了悲哀的黃鐘」
  • 描述「捨不得他人捨不得」有「不是我自居聖人,將自己優雅地安置高處;也不是因為我刻意美化自己,要自己鶴立雞群;更不是因我過度的浪漫,脫離世俗,悠然地嘲笑他人的愚昧」
  • 描述「捨不得捐出藏書」的心情「世界恰似一座巍峨輝煌的宮殿,眾多瑰綺珍寶,鵝黃,鴉綠、紫豔、浮金……樣樣五光十色,耀人耳目,令人款款眷戀」。

別這樣,好嗎?

過度抽象語法表現具體的事

幫狗和貓...(假設有 n 種)類別加上跑步的需求

class Dog {
public:
  int location;
  Dog (const string& name): location(0) {}
  // void run (int distance); <- 想要具備的功能
};

class Cat {
public:
  int location;
  Cat (const string& name): location(0) {}
  // void run (int distance); <- 想要具備的功能
};

該用擁有的情況,使用繼承

繼承,只有兩種使用方式,小心使用,以及禁止使用
繼承會增加程式複雜性
如果繼承符合 Liskov 替代原則 (LSP) 就可以降低因繼承而衍生的複雜性
--《Code Complete 2/e》, Ch6 Working Classes, 6.3 Design and Implementation Issues, Inheritance (“is a” relationships)

之前《Code Complete 2/e》的讀書會的討論中有個小小的精要心得:

  • 要介面 → is a → 繼承
  • 要實作 → has a → 包含 (用 property)

在這個例子,適因為想要的是 run() 所以適合使用繼承

該用繼承的情況,使用 template

用泛型 function
讓你們通通可以用泛型的「跑步魔法」來移動距離

template<typename Animal>
void run(Animal* animal, int distance) {
    animal->location += distance;
}

main()

Dog *lucky = new Dog("lucky");
run(lucky, 20);
cout << lucky->location; //20

但是,這時應該使用父類別來繼承,利用物件導向的語意來實作會更適合。
這種情況,常見於資料表的操作,有些資料表具備某個共同的功能,就寫一個抽象到不行的來套用,完全不管語意的問題,這樣協作的伙伴,怎麼透過語言本身來了解這樣巧妙之處呢?

這就是就是華麗詞藻!!

該用繼承的情況,使用 delegate

就...C# 的用法。

和短碼一樣,這些都是屬於炫技的用法。而不是為了夥伴而寫的 code 。未來會讓你召來負面情緒,也是自己造的.....(XD)

要懂用語法內含語意來得取捨

這些要有適合的取捨,就要看開發者是否了解語法中,內含的語意與語言表達極限。

物件導向,不適合用來描述非同步問題,也許這樣的情況之下,才適合使用泛型或 delegate,而 JavaScript 將這兩個用法視為一般用法,反而不強調物件導向,原因也許是 JavaScript 天生就是為了非同步問題而生。

非必要時使用 pointer

這指的是 C++ 的時候,常見的問題
有時,在 class 裡的 member variable,也沒有繼承關係,但是硬生生的就是用了 pointer 。其實,不用 pointer 也可以做到一樣的事情時,盡量別用 pointer ,在 C++ 中沒有垃圾搜集器,所以忘記 delete 就真的一直存在湯瑪森[4]了。

class A
{
  int* m_ptr_i;
public:
  A():m_ptr_i(new int()){
  
  }
  
  ~A(){
    delete m_ptr_i;
  }
};

C++ 的初衷之一也是想要開發者隱藏 pointer 這個複雜度。
一般的宣告方式,變數會隨著類別啟動解構式時,自動消滅,這個特性可以用來隱藏一些「容易忘記」的相對做法。

例如:

  • pointer 的 new/delete
  • critical section 的 lock/unlock[5]

說的有點難懂

寫硬體描述語言時,就有聽過

  • 控制與運算要分開
  • 控制和資料要分開

寫軟體時,就聽說更多的原則

  • 資料和運算要分開
  • 介面和運算分開

這麼說吧!
能用最簡單的語法表達,就用簡單的語法。
能拆分權責就拆分權責。
能寫成程式碼就不要用註解。
能少給權限,就少給權限。

再不行就常常 code review 吧!
高手同事說 ok 就是 ok 滴!!!! (好糙的標準!!)

參考資料

[1]: 5th International Obfuscated C Code Contest, 1988 - westley.c
[2]: Short Coding 寫出簡潔好程式-短碼達人的心得技法
[3]: 教育讓作文變得矯情:真正的「語言癌」,在國中會考的答案卷上
[4]: 台北路上觀察學會, 【主題2】印象最深的觀察, 小知識 * 何謂湯馬森?
[5]: API Design for C++


上一篇
不要造神 (神一般的物件)
下一篇
不依照文件寫 code
系列文
可不可以不要寫糙 code30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 則留言

0
turtle0617
iT邦新手 5 級 ‧ 2018-10-25 08:47:06

大哥說oK,就是好的程式碼!
這一天感覺離我還很遠/images/emoticon/emoticon02.gif

我要留言

立即登入留言