2015年11月18日 星期三

以大三的角度看研究所這回事

關於升研究所這問題也討論到爛了,「我該不該念研究所」還有「念研究所對工作的幫助」這兩題,以台灣的環境來看要好薪水就該念研究所,而且念研究所會培養邏輯思考、思維辯證、文筆寫作等能力,很明顯地對工作一定會有幫助。

所以我想從另一個角度來看,對一個進大學的新鮮人,研究所對他的意義是什麼?大學生活對他之後升研究所會有什麼關聯?這篇文章會先舉大學生大一大二的生活寫照,之後帶到升研究所這回事,之後再談論台灣的就業環境是如何看待碩士生和大學生,並以我目前是大三生的角度來看待這議題,提出相關問題後再告訴大家結論。

案例一:
大學生A進入某國立大學後,生性活潑參與許多社團、活動,吃宵夜、夜唱、打LOL、夜衝都經歷過,大一過後成績普普,班上六十名排名40,沒有被當。

到了大二任職社團幹部,要籌畫活動、社團發表,也加入學生會,參與學校事務。可是大二課更重,被當了兩科主科,通識也掛了一科。有時候活動太忙就翹課,或是因為太累而上課在睡覺,之後成績掉到50名左右

不過因為他頗認真在經營活動,認識很多人,在學校小有名氣。後來大三後還是在跑活動,但是數量減少,成為半退休狀態。每天的生活就是上課、偶爾去跑跑活動。書開始唸得比較多,大三後成績班排維持在40-50

案例二:
大學生B進入同樣國立大學,延續之前高中的生活,很認真唸書,沒有像A參加社團,頂多就是去系上辦的活動,生活規律,不吃宵夜,也不去夜衝等比較會影響睡眠的活動。大一後成績班排第三。

大二時一樣延續大一的生活,生活規律,也很認真唸書,還會幫同學解答課業題目。大二過後班排二

大三時跟大二差不多,生活照常,學期過後班排第三。整體來說,是個很認真的學生,不過對於修課以外的事物比較沒興趣。

這時(兩位都是大三)A同學在臉書上發文:「明天就要期中考啊~兩百頁的課本還沒念,之後還要補修微積分嗚嗚~~是說後天還要去其他社團分享當主席的經驗>< 喔好懷念之前一起邊跑活動邊唸書的日子」。

B同學在臉書上發文:「嗚嗚上次只拿了85分,這次要好好準備,看之後班排能不能更好哈哈~」

對於A同學,可以想得到的是很多人給予安慰,並且祝他考試順利,然後說成績可以再加油就好;對於B同學,可以想得到的是會有人開玩笑地嗆他,或是說哇好厲害喔,但是可能會有酸人的口吻。

兩種生活寫照都會有人贊同,可是第一種能獲得較多關注是因為
  1. A同學認識很多人
  2. 我們傾向給予人安慰,尤其時看到別人出現困難時
換言之,我們可能認同跑很多活動但是成績顧不好是可容許的,可是當要你認同「一個人認真努力唸書」就會覺得他就很會唸書,兩邊的觀感就不太一樣。

之後在準備研究所時,A同學因為成績不好而放棄了推研究所的念頭,B同學則因成績好順利推上知名國立大學的研究所

我們都知道,現今研究所推甄不外乎就是看「從哪間大學畢業、在校的成績、在校排名、比賽經驗、特殊經歷」等,可是很多時候反而都是看中成績(之前跟系上許多教授提到是這樣)

而在台灣的產業界,假設是資訊、理工科系的出路,尤其是以大公司來說,因為履歷眾多,通常非台清交成的就都不看了(因為舉理工科系,所以先省略政大),而這些大公司又希望你有豐富的經驗,最好要能和產業結合,或是你有豐富的人脈、溝通技巧等,才比較受到青睞。

整理一下,我們目前有學生、研究所教授、產業界的三種視角:
  1. 學生:讀大學是一種探索的旅程,每一種選擇都是寶貴的經驗,也造就了往後的自己
  2. 研究所教授:你要讀好的研究所就要有好的成績
  3. 產業界:我要知名研所的碩士生,並希望會團隊合作、善於溝通、樂於學習、有豐富實作經驗
換言之,假設你今天要有好工作應該會希望有好的研究所學歷(通常指第一份工作),而要有好的學歷就是要有好成績,可是產業界又希望你有「除了課業以外的經驗」,變成你需要在大學「不僅要成績好,又要去參加活動(假設參加活動能獲得團隊合作、溝通的經驗)」,無形之下希望學生要全能實在是有難度,畢竟那是少數,學生要嘛就是很會唸書,這就可以走研究路線,要嘛就是知道如何帶活動、團隊合作或是帶團隊,這就很適合專案管理、行銷、公關、公司管理階層。

可是在目前環境下,學士生和碩士生多半會先選擇碩士生,尤其是大公司的人資會收到很多履歷,時間不夠下很難一一看完,當然會先挑選碩士生,而且盡量以國立大學為優先,可是這樣就造成一些大學時某方面很厲害但成績卻不佳的人的曝光度低落,仔細想想後可以採用以下方式:
  1. 改善課程,把產業界的問題當作學生的課程專題,並適時地融入小組討論、報告,並且要求學生實作出來,模擬產業界的情況
  2. 以實作取代看學歷。以資訊產業為例,在投履歷後,都給求職者程式題目,並且要求在一定時間內寫完,甚至可以用電腦來檢驗程式效率。以此為基準,有好的學歷再做加分。
也就是說,今天學生B很會唸書,可以靠好學歷進公司;學生A雖然成績普普,但是有其他額外表現也有機會進去公司,然後學生B可以做理論研發等,學生A則進入管理或是行銷、人力相關部門,讓每人各司其職,以發揮所長。

到後來還是覺得,大家會有種放不開的感覺。企業會認為學歷就是好的選才標準,然後研究所的選才就是看成績,因此變成大家都追求成績,某方面上造成實作程度低落,出來反而被企業抨擊。這時候我也覺得乖乖唸書的學生沒錯,他們很認真地按照遊戲規則玩,怎麼到後來沒做錯事就被踢出去了?而那些認真實作的人卻在一開始就被研究所踢出去。

所以,若真要產出對應產業界的人才,真的很需要產學合作,諸如德國,不設立許多大學,反而培養許多技職生去產業合作。

可是現在廣設大學後已經很難走回頭路,家長們也認為讀大學就是好工作的保證,問題是有好大學不一定有好工作,有好工作不一定代表你找到了人生的意義和價值。今天假若大家都認為我就看你的專長來雇用你,那學歷也只是第一份工作的門票,大家還是會看你真正的實力。

可是現在變成我大學念完,就要念研究所,研究所念完就要去當兵、找工作,之後買房買車,似乎這就是人一生該走的路。為何一定要接受同樣的人生道路呢?在歐美也沒那麼在意你有沒有唸大學,反而在意你的特色、價值(不過現在他們的高等教育普及率也愈來愈高),之後還是可以回來補學歷。

找到自己的核心價值和天分,並且用心地挖掘它,我想這才是教育的目的吧。

2015年9月10日 星期四

軟體工程書摘與心得 Part 2:物件導向軟體工程之另類思考

延續上篇,當我們有完整的需求分析後,接下來就是要實作程式碼。這部分我跳過很多,直接把我認為重要的拿出來。當類別圖畫好後,接下來就是要畫出序列圖(Sequence Diagram),之後對使用者案例進行健全性分析,最後連接上對於該程式的領域模型,之後軟體內類別和類別的關係大抵上就確定了。

物件導向的另類思考流程圖
至於什麼是序列圖?就是在一個動作中,列出參與該動作的物件,隨著動作進行時,找出物件和物件之間的交互子動作,直接看例子:

Sequence_diagram_for_handling_a_browser_request_using_the_Model_View_Controller_MVC_pattern

上圖中顯示要「處理來自瀏覽器的請求」時,所需要做的動作和參與的對象,隨著動作開始進行,首先Browser會發出HTTP GET Request()給Tomcat,之後就持續做一連串的動作,直到整個流程被完成。序列圖的好處是(1)知道參與者有哪些,(2)子動作其實會對應到所實作的函數,能很直覺地反映出程式該怎麼寫。

另外還有一個是健全性分析(Robust Analysis),他是把「分析的模型轉換成設計的模型」。依據使用案例模型,輸入使用案例、使用案例的情況(Scenarios)、使用案例對應的活動圖(Activity Digram),以及領域模型,輸出成以UML表示的合作圖(Collaboration Diagram)來記載,裏頭會有3種主要的設計成員:
  1. 邊界物件(Boundary Objects):用來描述系統與角色之間的互動
  2. 實體物件(Entity Objects):用來描述系統中經常存在的資訊(Persistent Information)
  3. 控制物件(Control Objects):用來描述對於其他物件的控制、安排,或是協調
使用健全性分析是幫助釐清目前定義的類別是否足夠、角色定義是否明確,可能在進行健全性分析的過程中會發現更多的物件或是新的屬性類別,然後用來重新繪製序列圖,所以序列圖的建立不是一次就完成的,是需要經過多次迭代的。

我覺得最厲害的還是序列圖,他可以直接讓我知道怎麼寫程式,同時又能了解物件的互動,真的很值得學習。

下一篇要介紹當軟體完成時怎麼做系統組態的管理與維護,如何讓軟體能適時地反映使用者的成長


2015年9月9日 星期三

軟體工程書摘與心得 Part 1:為何需要軟體工程&軟體工程之簡介

最近閒來無事去看了Livecoding.tv上程式設計師表演打code,就覺得怪了為何你腦袋能源源不絕想出接下來要做什麼,從這頁面又跳到下個頁面,雖然我也能看懂你在做啥,可是自己就不一定能這樣行雲流水般地打code(苦笑)。後來想想應該是我缺乏軟體工程上的訓練。

不然別人是怎麼會想說這邊要用這個protocol?下一個地方要用這個函數銜接?在軟體工程中我從這本書找到了答案:

根據書中解釋,1960年代末期由北約召集科學家探討「軟體危機」的問題,討論如何有更好的軟體開發方法來提升開發的效率、節省開發成本,同時獲得良好的軟體系統品質,因而催生了軟體工程的發展。對我來說寫軟體就如同建造房子,從畫藍圖、打樑樁等都有一套步驟和分析的方法,都需要投入大量的精力來完成。

而這也是會和有些人能把程式寫得穩健、可靠、可擴充,就是有進行完整的系統分析、需求了解、介面設計等才能達到一定品質。隨著軟體漸趨複雜,光是作業系統就可高達上千萬行程式碼,如何做好軟體開發就需要系統工程的幫忙,並借助軟體系統開發方法論,然後選擇軟體開發程序:

  1. 傳統階梯式的軟體開發程序:做好需求分析後,設計系統與軟體的架構,之後進行系統製作與局部測試,最後整合測試後就可上線使用還有維護
  2. 漸進式的軟體開發:透過需求分析後快速產生原型,之後陸續迭代產生新產品
  3. 需求規格化的軟體開發程序:將軟體系統的規格以數學模型來表示,之後自動轉成程式碼
  4. 組合式的軟體開發程序:分別完成系統內的各個元件,之後再組合起來
而隨著物件導向的盛行,也陸續產生新的開發方法(其實也很久的東西了):
  1. RUP (Rational Unified Process):結合螺旋式、反覆式及漸進式的開發方法,主要分成「開始階段」、「細化階段」、「建置階段」、「轉換階段」四種
  2. 敏捷開發:像是XP(Extreme Programming)、Scrum等
方法演進後手上的工具也不能輸人啊!因此就有電腦輔助軟體工具(CASE, Computer-Aided Software Engineering)的出現,早期CASE工具出現時人們的期望很高,認為可以大幅下降軟體開發成本,但當時的CASE工具並不成熟,沈寂了一段時間後大家才開始陸續使用,大家常用的IDE就是一個典型例子。

仔細回想起來,目前有很多火紅程式教學的網站,大家都用得開心,可是真的要開發一套系統就只能坐在電腦前打不出任何東西,一部分可能是程式技巧不足,另一部分就可能是缺乏軟體工程的訓練。當了解軟體工程時,就能超越一般的程式設計師成為軟體工程師,就像是從一位工人進化到建築師,從只會寫程式變成能洞悉大局、了解如何規劃與參與軟體開發的各個階段的「高級程序員(猿)」。

下一篇將會介紹軟體系統的需求工程,如何找到需求並把需求轉成軟體架構、規格就需要這篇啦~

2015年9月2日 星期三

iOS Push/Local Notification之原理

這次來介紹一下如何使用iOS上的Push Notification,舉凡Facebook上的訊息、Gmail的郵件通知都是此類。而首先就是要知道Notification有分成Local和Push兩種,前者像是行事曆的定時通知,後者就是前面講的。Local的通知好處理,可以指定日期和時間來通知:
 NSCalendar * calendar = [NSCalendar autoupdatingCurrentCalendar];

NSDateComponents * dateComponent = [NSDateComponents alloc] init];

[dateComponent setDay: item.day];  // item: self defined object

[dateComponent setMonth: item.month];

[dateComponent setYear: item.year];

[dateComponent setHour: item.hour];

[dateComponent setMinute: item.minute];

NSDate * date = [calendar dateFromComponents: dateComponent];



 UILocalNotification *localNotif = [[UILocalNotification alloc] init];

 if (localNotif == nil)

    return;

localNotif.fireDate =

[date
  dateByAddingTimeIntervalInterval:-(minutesBefore*60)];

 localNotif.timeZone = [NSTimeZone defaultTimeZone];

 localNotif.alertBody = [NSString stringWithFormat:NSLocalizedString(@"%@ in
  %i minutes.", nil),
           item.eventName, minutesBefore];



localNotif.alertAction = NSLocalizedString(@"View Details", nil);

localNotif.alerttitle = NSLocalizedString(@"Item Due", nil);

localNotif.soundName = UILocalNotificationDefaultSoundName;
      localNotif.applicationIconBadgeNumber = 1;

NSDictionary *infoDict = [NSDictionary dictionaryWithObject:item.eventName
  forKey:ToDoItemKey];

localNotif.userInfo = infoDict;

[[UIApplication sharedApplication] scheduleLocalNotification:localNotif];

那麼要設定Push Notification要怎麼做?首先要用Apple的Push Notification機制(簡稱APN),自己的Server發出通知後會傳到Apple的Server,之後再從Apple傳給使用者的App:


我覺得這樣做的原因是要保持通知的完整性和安全性,假如說你傳送有惡意訊息的封包給使用者豈不是不好?而建立整個連線流程如下圖所示,裝置先和APN建立TLS(Transport Layer Security)連線,之後取得憑證後TLS就算建立完成:

而Provider也是同樣要建立TLS連線和取得憑證:

而當憑證建立完成後App和APN, Provider的互動就是靠Token來完成,App若要使用Push Notificaition必須先行註冊至APN(從使用者那邊獲取通知許可),然後APN再回傳加密過的Token給Provider,之後該Token再從Device回傳到Provider,如下圖所示:
而在傳送Push Notification時會連同Token,並藉由裝置ID來回傳加密的通知內容給裝置:

到這邊整個Push Notification就算完成,接下來就是在AppDelegate.m加上程式碼:

// Remote notifications
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken{
    NSLog(@"Receive deviceToken: %@", deviceToken);
}
- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error{
    NSLog(@"Remote notification error: %@", error.localizedDescription);
}
另外當收到了Notification後要做什麼事就先靠didReceiveRemoteNotification來完成,而handleActionWithIdentifier則是當使用者對Notification做出回應後你要採取什麼動作時,可用completionHandler的block來完成:

// When app is waked up, this method will be called
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler{
    // with push notification from remote server
    // When app is running in the foreground, this method will be called
}
- (void)application:(UIApplication *)application handleActionWithIdentifier:(NSString *)identifier forRemoteNotification:(NSDictionary *)userInfo completionHandler:(void (^)())completionHandler{
    // Test for identifier with a sample indentifier
    if ([identifier isEqualToString:@"ACCEPT_IDENTIFIER"]) {
        [self handleAcceptActionWithNotification:userInfo];
    }
}
以上就是整個流程,至於說要註冊憑證的方法可參考這篇:popcorny的碎碎念

2015年8月31日 星期一

NSOperation及NSOperationQueue與GCD之比較

之前上Stanford iOS 7的課程時有介紹到GCD(Grand Central Dispatch),是一種比較低階的函數,讓code可以在不同的thread中跑。可是假如說我今天要檢查該thread的運作情況,好比說他的優先順序、是否還在運行等,這時候GCD能提供的設定就比較少,於是就需要NSOperation和NSOperationQueue啦~

在Mac OS X 10.6之前,NSOperation跟GCD是採用不同的運作模式,10.6之後NSOperation就改成運作在GCD之上了,換言之他是比較高階的API,但是同時也保有GCD的效能。使用NSOperation時有以下步驟:

  1. 繼承自NSOperation
  2. 覆寫main()
  3. 在main中建立autorelease pool(自動釋放池)
  4. 把code放到autorelease pool中
  5. 針對需求,在外部檢查isCancelled等屬性
至於NSOperation中有以下method:
  1. start:呼叫該函數來執行該operation,但如果沒有特別指定去哪個queue時將會在main queue執行,要特別注意
  2. dependency:可以設定兩個operation的相依性來設定先後順序。舉例來說我今天下載了一段音訊,要等下載完成後才能進行裁剪,GCD中的dispatch_barrier_async()有提供類似的功能
  3. priority:這點是我覺得比較有趣的地方,可針對特定的operation來設定setQueuePriority:,像是NSOperationQueuePriorityVeryLow
  4. completion block:當operation完成後可以在setCompletionBlock:^{}去做其他事情,好比說存檔等等
另外NSOperationQueue相較於NSOperation就更為簡單,僅需要alloc並init即可,之後可調用以下方法:
  1. concurrent operations:這點比較複雜,簡單來說一個queue可以有多個thread,而每個operation會分配到一個thread來執行。舉例來說我有一個queue,然後加入2個operations,這樣建立兩個thread對應到每個operation,所以我這個queue就會有兩個thread
  2. maximum number of concurrent operations:設定每個queue中最多只能有幾個operations來執行
  3. add operation:增加operation到queue,如果要釋放的用使用release方法
  4. pending operation:查詢在queue中有哪些operations。只有「待執行」和「正在執行」的operation才會在queue中。這部分可以用NSArray來保存operations,或是使用NSDictionary來查詢相關的operation
  5. pause(suspend)queue:就是停止queue
  6. cancel operation:如果說該operation尚未執行,呼叫cancel會把operation從queue中移除;如果是正在執行的operation,就要看該operation會不會去檢查isCancelled
  7. addOperationWithBlock:如果當你不想要有NSOperation的子類別卻又想要來queue中執行,就可在該方法中的block插入程式碼。這有點像GCD的block。不過要注意的是若要參考block外的物件,就必須傳入weak屬性的物件
最後綜合比較NSOperation和GCD,我覺得GCD比較簡單、直覺,但是可以操作的範圍不大(因為就是函數),可是其他物件可以繼承NSOperation,獲得了使用多線程的能力。所以我想當要操作物件時就用NSOperation,然後某些片段(下載等需求)就可以用GCD來解決。總之在iOS中多線程是個很重要的觀念,有這麼好用的功能就要好好用它啊~~

Protocol v.s. Delegate

最近剛好寫了一些跟NSOperation的東西,裡面用到許多的Protocol和Delegate,之前學到我們可以宣告一個Protocol,然後讓class去遵循它,可是那為何我們還需要有Delegate? Delegte不也是讓我們去遵循其中的方法來實作嗎?就像是我們在寫UITableView時都要有UITableViewDataSource和UITableViewDelegate來對資料和cell進行設定(因為View無法擁有自己的資料)。那麼兩個這麼像,Protocol和Delegate又有什麼差別呢?

答案是一定有差別的,不然你就不會一直看到這兩個名詞。先複習一下什麼事Protocol,假設當我宣告MyProtocol中有個aMethodInMyProtocol時,任何一個遵循MyProtocol就可調用該方法;另外也可讓實體變數遵循Protocol,譬如我的MyClass中有以下變數:

@property (nonatomic, strong) id  <myprotocol>  instanceThatImplementMyProtocol;

此時該instanceThatImplementMyProtocol就可調用MyProtocol的方法。此外宣告Protocol時也可再遵循其他Protocol,例如:

@protocol Foo
...
@end

只有當你實作了anotherProtocol後,然後你再實作Foo後你才可以說已經實作好該Foo Protocol。那麼在Delegate又是怎麼樣呢?

Delegate說起來是一種設計方法,全名叫做Delegation Design Pattern,Delegate要求遵循該delegation的class去實作Protocol的方法(可以說是一種「方法」),使得該class可以在特定時候使用該Protocol的method。對我來說,Protocol提供一個介面來讓大家共享裡面的方法,然後Delegate會讓用它的class去實現該方法,使用Delegate的好處是可以減弱耦合,達到共享的目的。

另外取自StackoverFlow的解答,我覺得看英文比較能懂:)
The class that uses the delegate knows that its delegate coforms to the protocol, so it knows that it can call the implemented methods at given times. 

2015年5月10日 星期日

The Pragmatic Programmer : from journeyman to master 書摘


我們,採集的只是石頭,卻必須時時刻刻展望未來的大教堂。
                                                                                                                —採石工人的信條

  1. 第一章:注重時效的哲學
    • 負責你的代碼,不要找蹩腳的藉口
    • 不要容忍破窗戶:有問題就及時修改,別累積技術債
    • 做團隊裡「做變化的催化劑」:提供大家改變的契機,讓大家追隨你而改變
    • 永遠記住大目標,要見樹並見林
    • 使質量成為需求問題:讓你的用戶參與權衡
    • 定期為你的知識資產做投資
    • 批判地分析你讀到的和聽到的
    • 你說什麼和你怎麼說同樣重要:交流、知道你要說什麼、瞭解聽眾、選擇文件的風格和美觀
  2. 第二章:注重實效的途徑
    • 不要重複你自己:和重用(Reuse)差別在,重複(Repeat)在於你做了同樣的事情,你應該只做一次卻做了很多次,造成時間和成本浪費;而重用則是再次利用優秀的程式碼來達到效率上的精進。
    • 讓重用變得容易(Make It Easy to Reuse)
    • 消除無關事物之間的影響:提升系統各組件的正交性(即降低耦合性)
      • 有正交性的好處:提高生產率、降低風險、項目團隊、設計
      • 編碼:讓代碼解藕、避免使用全域變數、避免相似的函數
    • 可撤銷性:不要存在最終決策,你的產品隨時隨地都有可能砍掉重練
    • 使用曳光彈來幫助你找到目標:和原型製作的差異在於曳光彈代碼是完整的,之後會構成最終系統的骨架的一部分
      • 用戶能及早看到能工作的東西
      • 開發者建構了一個他們能在其中工作的結構
      • 有了集成的平台
    • 為了學習而製作原型:原型所具備的元素有
      • 正確性
      • 完整性
      • 健壯性
      • 風格
    • 靠近問題領域編寫程式(Program Close to the Problem domain):選擇特定語言或是編寫特別定義過的程式來解決問題
    • 估算以免發生意外
      • 理解提問內容
      • 建立系統的模型
      • 把模型分解為組件
      • 給參數指定值
      • 估算項目進度
  3. 第三章:基本工具
    • 用純文本保存知識(Keep Knowledge in Plain Text)
    • 利用命列Shell的威力(Use the Power of Command Shells)
    • 用好一款編輯器:好的編輯器應該具有以下特性
      • 可配置:可高度客製化
      • 可擴展性
      • 可編程性
    • 總是使用原始碼控制工具
    • 要修正問題,而不是發出指責
    • 不要恐慌
      • 將你的數據視覺化
      • 跟蹤
      • 使用橡皮鴨:向別人解釋你的程式碼,讓別人像鴨子一樣一直點頭,直到問題出現
      • 消除過程
    • Select沒有問題
    • 不要假定,要證明
    • 學習一種文本操縱語言
    • 撰寫能撰寫程式碼的工具
  4. 第四章:注意時效的偏執
    • 你不可能寫出完美的軟體
    • 使用DBC按照合約設計:
      • 前條件
      • 後條件
      • 類別不變項
(未完.....)