2016年7月29日 星期五

Windows程式畫面布局設計概念

這個布局部分有點多,所以可能的話會分幾次來講

基本上您會發現,相同應用的軟件,基本上畫面布局(Layout)都很像,佈局是指什麼,就是您在畫面上放置一些容器,讓框架內可以放置一些您的按鈕或者是輸入欄位,當然也可以是顯示圖片

大概長這樣
這是一個空白的Form

 如下,我加了一個Menu選單,一個狀態列,還有兩個Panel容器,其中Panel上面可以根據我的需要,放一些元件



















其中用Panel或者是其他容器(例如ScrollBox,Notebook),定出畫面的擺放,這叫做布局(Layout)

這裡就直接稱乎布局了

我們常常看到很多舊型的程式,都會有一些相似的布局
我們以C++ Builder為例(這是新版的)















最上方會有一個Menu,再來下面是一些快捷按鈕,左邊是屬性設計,右邊也是顯示一些資訊的地方,下方會有一個狀態列(其中他將狀態列換成自製的)
基本上C++Builder他的視窗可以到處搬動,這已經是屬於進階部份了,不過基本樣式是固定的

因此就我剛剛提到的那些基本元素,我大概拉一個基本樣式



















上方有一個Menu,下面是一些快捷按鈕,例如按完可以做計算還是幹嘛,總之就是一些按鈕,再來是最下方的狀態列,而中間兩個則是自己設計的框架,我的框架式左右兩個Panel,右邊是放參數的,左邊是主要顯示Client視窗,

基本上您會在很多古早的應用程式,大約Windows XP那個年代,大概都長這個樣子

這是最最最基本的,也就是您完成一個應用程式,假設小到不行,您可以參考這總方式做基本畫面布局,

總之就是很古老的做法就是了,基本上您看到現在的一些應用程式很花俏,充滿了無數的設計,所以這簡單的畫面布局基本上也不算什麼秘密了,要做到像一些大型軟件那樣,其實是很多設計的,絕對不是就這樣弄一個Form,然後全部東西都弄在這裡,就結束了

大型軟件是指什麼,例如您打開近代的WordC++ BuilderVisual Studio,他們會有一些框架,不同框架放不同的功能,其實您數一數可能也10~20種,您要在一個FHD的螢幕上,規劃10~20種框架,是不可能釘一釘Panel就處理完的,

因此這是一門學問也是一門藝術,但是基本上像這種大型軟件,框架可以到處釘,基本上這是模組化設計了(老實說我也不會),這就不再我們討論範圍,這一般沒有10來個人同時寫一支程式不太可能完成,我這裡會介紹的比較偏向於一個人能獨自完成的應用程式

如上面您看到我弄的框架,基本上很死,框架都釘好了,基本上是無法做任何變化的,

我們這裡談到的設計,牽涉到UX(User experience design ),也就是使用者體驗,
說他簡單,也很簡單,其實一個重要意義就是您的畫面如何設計,可以讓使用者操作起來最舒服,最方便
但是說他難,也很難,我們一支程式通常是從無到有,一般您在公司裡接案子,客戶不會一次就將所有要做的功能都跟您說,因此您的介面會一直增加新的東西

您會發現做一做,需求一直進來,您就加入很多東西,最後都塞到同一個地方,例如剛剛的圖,只有6個欄位,那要您的欄位一直加一直加,加到了20多個,通常來說,欄位太多要設定,如果您沒做分類處理,或者做一些分業,使用者會用起來很不舒服

假設您一支程式有一個功能,有18個參數可以控制,您以一個程式設計師來說最簡單的就是將欄位照順序分在一起


















這裡就不做命名了,總之就是不同參數的欄位,這一般有接過案子才能體會,我也不知如何表達,總之您就把他想成您有18個欄位可以控制就是了

基本上您沒做分類,要懂怎麼操作您的軟體看了就不想學了,
所以我們先做基本分類,好啦,就假設我這個程式可以用來設定動物身上的顏色,
假設相關的顏色欄位,狗有6個,貓有4個,鳥有8個,
我們做一個索引如圖


















這樣做簡單歸類,操作員就知道假設他要設定狗相關顏色參數,就改納六個欄位就好了,依此類推,我們程式設計目的做一支好用的程式給使用者,而不是只是為了完成功能賺錢而添加新的功能,這在一些比較落後的公司都不注重這個,往往就要啥參數就加啥,然後放一大堆,基本上要是我是使用者,我看到就不想用了,更別說對您的軟體有興趣

當然講這樣好像太淺了,很像我是白癡一樣,所以我就再衍伸出去

通常我們程式是可以縮放的,要是您把參數欄位放在Panel上面,您會發現縮小時,欄位被遮住了,如圖




















雖然看起來很弱智,但是假設是初學者對元件不熟,一定做到後來就會發生這個錯誤,基本上我們就使用ScrollBox,他會有ScrollBar可以拖曳,就不會擋到您的欄位了














這樣其實還不夠,通常使用者再看欄位時,可能一次10個以上就會抱怨了,所以除了分類,我們還要做分頁,最簡單的Solution就是PageControl,當然有很多替代的,也可以自己設計,這裡就不講自己設計的部分(因為要寫Code),其實自己設計的話會比較漂亮,所以我們將PageControl做三頁,將這些欄位分別放入PageControlTabSheet裡面


















您只要切換頁籤就可以設定不同的動物顏色,但是您會發現問題又來了,畫面縮小時又會欄位了















基本上就是
PageControl本身不會有ScrollBar,因此我們需要在它上面分別放上ScrollBox,再在ScrollBox上面放元件,這樣當縮小時就又會出現ScrollBar















基本上您會發現ScrollBox很好用,他會幫您自動產生ScrollBar但是必須是元件跑到ScrollBox外面才會出現,

原則上如果您沒做畫面設計的話您大多時間會做我上面在做的事情,就是畫面布局,及參數分類管理,原則上可用的元件還是要熟拉,假設您沒有要深入,會到這裡就可以了

偷偷給您看經過畫面設計會有啥不一樣,我大概弄給您看,大概就是將PageControl改成自己用程式碼設定呼叫頁面顯示,往後教程我會弄一個範例給您下載研究,您會發現其實軟體是有很多技巧的,而不是就只是拉拉元件,兜兜功能,寫寫核心演算法就沒了

這裡只是先秀給您看一張圖
















自製的PageControl,其中滑鼠放上去按鈕(其實他是Panel做的)他會變色,
這技術其實可以常常在網頁上看到,是一個模組化技巧(由於關鍵,就晚點再秀了(0 .0明年看有沒有機會),只要您會模組化,這種東西可以千變萬化)


先到這了

開發的函式物件導向化1

講到物件導向,其實就是把可以reuse的東西包起來,使他可以reuse,包起來不只是只能寫成一個function,您只要是一段程式碼可以重新呼叫,不管您怎樣包都可以,例如您可以裝在class裡或者是namespace裡也可以

我們舉我們在學校第一個學的演算法,也就是bubble sort(氣泡排序法)

當我們要處理一串數字依序排列,就要使用到他,下面會附一個我測試的程式碼壓縮檔在裡面,可以直接使用C++ Builder

首先我們先拉幾個欄位,使他可以輸入數字,這裡是示範,只使用五個













也就是左邊五個欄位輸入數字後,按下button,會依序排列在右邊

首先我們先定義接口,也就是將處理的程式碼拆成三段

/////////////////////////
將欄位存入陣列
處理片段
將處理結果存回欄位
/////////////////////////

這邊假設看的人都知道C++ Builder宣告一個陣列,或配置一塊記憶體,是不會初始化內容的,也就是裡面的值不會都是0,其實講細節要弄成影片比較好懂,儘管影片錄的不好,但是可以做一連貫的說明,其實是比較好的,這邊沒有弄成影片,所以就講得比較簡化一點

#define n 5
void __fastcall TForm2::BitBtn1Click(TObject *Sender)
{
    int src[n];
    // 對陣列初始化
    for(unsigned int i = 0;i < n; ++i)
    {
        src[i] = 0;
    }

    // 也可用memset處理
    //memset(src, 0, n * sizeof(int));

    // 讀入值
    src[0] = atoi(AnsiString(Edit1->Text).c_str() );
    src[1] = atoi(AnsiString(Edit2->Text).c_str() );
    src[2] = atoi(AnsiString(Edit3->Text).c_str() );
    src[3] = atoi(AnsiString(Edit4->Text).c_str() );
    src[4] = atoi(AnsiString(Edit5->Text).c_str() );
    
    ///////////////////////////
    // 由小排到大
    for(unsigned int j = 0; j < n; ++j)
    {
        for(unsigned int i = j + 1; i < n; ++i)
        {
             if(src[j] > src[i])
            {
                 // 交換
                 int temp = src[j];
                 src[j] = src[i];
                 src[i] = temp;
            }
        }
    }
    ///////////////////////////
    Edit6->Text = src[0];
    Edit7->Text = src[1];
    Edit8->Text = src[2];
    Edit9->Text = src[3];
    Edit10->Text = src[4];
}

如這樣排列,程式碼會比較好懂,我們必須分段處理並且加入適當的註解,這樣才比較清楚,也方便後面要做的動作

這樣是由小排到大,這我已經確認過了

也就是

/////////////////////////
將值讀入陣列
處理
存回欄位
/////////////////////////

這樣基本上就達到排序的功能了

再來我們演算法寫好後,通常為了往後方便(就是以後可以reuse),所以要將它物件化,一般用function就可以了

#define n 5
void bubble_sort(int* src)
{
    for(unsigned int j = 0; j < n; ++j)
    {
        for(unsigned int i = j + 1; i < n; ++i)
        {
            if(src[j] > src[i])
            {
                // 交換
                int temp = src[j];
                src[j] = src[i];
                src[i] = temp;
            }
        }
    }
}

void __fastcall TForm2::BitBtn1Click(TObject *Sender)
{
    int src[n];
    
    // 對陣列初始化
    for(unsigned int i = 0;i < n; ++i)
    {
        src[i] = 0;
    }
    
    // 也可用memset處理
    //memset(src, 0, n * sizeof(int));

    // 讀入值
    src[0] = atoi(AnsiString(Edit1->Text).c_str() );
    src[1] = atoi(AnsiString(Edit2->Text).c_str() );
    src[2] = atoi(AnsiString(Edit3->Text).c_str() );
    src[3] = atoi(AnsiString(Edit4->Text).c_str() );
    src[4] = atoi(AnsiString(Edit5->Text).c_str() );

    ///////////////////////////
    bubble_sort(src);
    ///////////////////////////

    Edit6->Text = src[0];
    Edit7->Text = src[1];
    Edit8->Text = src[2];
    Edit9->Text = src[3];
    Edit10->Text = src[4];
}

如上,您看這樣就變成一個可以reuse的函式,假設您要排五個數字,就可以用這個函式來處理,往後您只要將函式存在一個cpp,帶走就可以用了,就不用每次都在重寫,達到reuse的作用

那這個函式只能排五個,我們要是要排10個或20個呢,一個函式好不好用,在於它的彈性,也就是目前寫法他只能處理五個,再多就不能用了,這種東西就是太死,所以不是那麼好,因此做了一些修改

#define n 5
void bubble_sort(int* src, int m)
{
    for(unsigned int j = 0; j < m; ++j)
    {
        for(unsigned int i = j + 1; i < m; ++i)
        {
            if(src[j] > src[i])
            {
                // 交換
                int temp = src[j];
                src[j] = src[i];
                src[i] = temp;
            }
        }
    }
}

void __fastcall TForm2::BitBtn1Click(TObject *Sender)
{
    int src[n];

    // 對陣列初始化
    for(unsigned int i = 0;i < n; ++i)
    {
        src[i] = 0;
    }
    // 也可用memset處理
    //memset(src, 0, n * sizeof(int));

    // 讀入值
    src[0] = atoi(AnsiString(Edit1->Text).c_str() );
    src[1] = atoi(AnsiString(Edit2->Text).c_str() );
    src[2] = atoi(AnsiString(Edit3->Text).c_str() );
    src[3] = atoi(AnsiString(Edit4->Text).c_str() );
    src[4] = atoi(AnsiString(Edit5->Text).c_str() );
    
    ///////////////////////////
    /*
    // 由小排到大
    for(unsigned int j = 0; j < n; ++j)
    {
        for(unsigned int i = j + 1; i < n; ++i)
        {
             if(src[j] > src[i])
             {
                  // 交換
                  int temp = src[j];
                  src[j] = src[i];
                  src[i] = temp;
             }
        }
    }
    */
    //bubble_sort(src);
    bubble_sort(src, n);
    ///////////////////////////

    Edit6->Text = src[0];
    Edit7->Text = src[1];
    Edit8->Text = src[2];
    Edit9->Text = src[3];
    Edit10->Text = src[4];
}

N已經用掉了,所以我換成m,那個函式就可根據定義的n處理不同數量變數
其實這樣彈性不夠,現在是由小排到大,那要是我要由大排到小呢,我們再做這樣的修改

void bubble_sort(int* src, int m, bool direct) // 0是由小排到大 1是由大排到小
{
    for(unsigned int j = 0; j < m; ++j)
    {
        for(unsigned int i = j + 1; i < m; ++i)
        {
             if( (src[j] > src[i] && !direct) || (src[j] < src[i] && direct) )
             {
                 // 交換
                 int temp = src[j];
                 src[j] = src[i];
                 src[i] = temp;
              }
         }
    }
}

void __fastcall TForm2::BitBtn1Click(TObject *Sender)
{
    int src[n];
    // 對陣列初始化
    for(unsigned int i = 0;i < n; ++i)
    {
        src[i] = 0;
    }

    // 也可用memset處理
    //memset(src, 0, n * sizeof(int));

    // 讀入值
    src[0] = atoi(AnsiString(Edit1->Text).c_str() );
    src[1] = atoi(AnsiString(Edit2->Text).c_str() );
    src[2] = atoi(AnsiString(Edit3->Text).c_str() );
    src[3] = atoi(AnsiString(Edit4->Text).c_str() );
    src[4] = atoi(AnsiString(Edit5->Text).c_str() );

    ///////////////////////////
    /*
    // 由小排到大
    for(unsigned int j = 0; j < n; ++j)
    {
        for(unsigned int i = j + 1; i < n; ++i)
        {
             if(src[j] > src[i])
             {
                 // 交換
                 int temp = src[j];
                 src[j] = src[i];
                 src[i] = temp;
              }
        }
    }
    */

    //bubble_sort(src);
    //bubble_sort(src, n);
    bubble_sort(src, n, 0);
    ///////////////////////////

    Edit6->Text = src[0];
    Edit7->Text = src[1];
    Edit8->Text = src[2];
    Edit9->Text = src[3];
    Edit10->Text = src[4];
}

這樣function可以做兩種方向的處理了,往後就會非常方便了

下面我們要做一些轉換,將它換成vector,因為我這邊要改成動態無限擴增,並簡化函式,目前需要三個欄位,如果換成vector,那基本上我們只需要兩個欄位,因為vector可以查容器的大小(size())

由於我程式碼要存成範例,因此我再另外拉一組介面,重新弄一遍






















void bubble_sort(vector<int>& src, bool direct) // 0是由小排到大 1是由大排到小
{
    int m = src.size();
    for(unsigned int j = 0; j < m; ++j)
    {
         for(unsigned int i = j + 1; i < m; ++i)
        {
             if( (src[j] > src[i] && !direct) || (src[j] < src[i] && direct) )
             {
                 // 交換
                 int temp = src[j];
                 src[j] = src[i];
                 src[i] = temp;
             }
        }
    }
}

void __fastcall TForm2::BitBtn2Click(TObject *Sender)
{
    vector<int> src;

    // 讀入值
    src.push_back( atoi(AnsiString(Edit11->Text).c_str() ) );
    src.push_back( atoi(AnsiString(Edit12->Text).c_str() ) );
    src.push_back( atoi(AnsiString(Edit13->Text).c_str() ) );
    src.push_back( atoi(AnsiString(Edit14->Text).c_str() ) );
    src.push_back( atoi(AnsiString(Edit15->Text).c_str() ) );
    src.push_back( atoi(AnsiString(Edit16->Text).c_str() ) );
    src.push_back( atoi(AnsiString(Edit17->Text).c_str() ) );
    src.push_back( atoi(AnsiString(Edit18->Text).c_str() ) );
    src.push_back( atoi(AnsiString(Edit19->Text).c_str() ) );
    src.push_back( atoi(AnsiString(Edit20->Text).c_str() ) );

    ///////////////////////////
    bubble_sort(src, 0);
    ///////////////////////////

    Edit21->Text = src[0];
    Edit22->Text = src[1];
    Edit23->Text = src[2];
    Edit24->Text = src[3];
    Edit25->Text = src[4];
    Edit26->Text = src[5];
    Edit27->Text = src[6];
    Edit28->Text = src[7];
    Edit29->Text = src[8];
    Edit30->Text = src[9];
}

您看這樣就可以處理10筆了,這邊有個小技巧,可以用列舉的方式,來處理,往後您只要對陣列新增欄位名稱,就可以做處理了

為了程式碼簡潔,我們再拉個button,總之後面我會附一個專案檔,打開就可以研究了

void __fastcall TForm2::BitBtn3Click(TObject *Sender)
{
    vector<int> src;
    struct Object_Mapping
    {
        TEdit* src;
        TEdit* dst;
    };

    Object_Mapping om[] =
    {
        {Edit11, Edit21},
        {Edit12, Edit22},
        {Edit13, Edit23},
        {Edit14, Edit24},
        {Edit15, Edit25},
        {Edit16, Edit26},
        {Edit17, Edit27},
        {Edit18, Edit28},
        {Edit19, Edit29},
        {Edit20, Edit30},
    };

    int k = sizeof(om) / sizeof(Object_Mapping);
    src.clear();

    // 讀入值
    for(int i = 0; i < k; ++i)
    {
        int value = atoi( AnsiString(om[i].src->Text).c_str() );
        src.push_back( value );
    }

    ///////////////////////////
    bubble_sort(src, 0);
    ///////////////////////////

    for(int i = 0; i < k; ++i)
    {
        om[i].dst->Text = src[i];
    }
}

這邊用到一個列舉的小技巧,您可照綠色文字的方式建立一個對應,
也就是來源對應輸出,我們得到物件的指標後,可以用
int k = sizeof(om) / sizeof(Object_Mapping);

取得他的size,然後我們就可以用for迴圈處理了,基本上要再加一組欄位,
只要將對應的指標如下處理,非常的方便

struct Object_Mapping
{
    TEdit* src;
    TEdit* dst;
};

Object_Mapping om[] =
{
{Edit11, Edit21},
{Edit12, Edit22},
{Edit13, Edit23},
{Edit14, Edit24},
{Edit15, Edit25},
{Edit16, Edit26},
{Edit17, Edit27},
{Edit18, Edit28},
{Edit19, Edit29},
{Edit20, Edit30},
{Edit31, Edit32},
};

這是用C++的好處,我不確定C#是否有這種東西可以使用(因為C#沒有指標),或許有,或許沒有,

而且C#是沒有vector的,不太方便就是了

上面程式碼有點效率問題,就是vector在pushback時,要是超出他已經配置的大小,會重新配置記憶體,並搬移到新的記憶體位置(內部處理動作)

http://pingyeh.blogspot.tw/2011/09/stl-vector.html

至於我們怎麼知道他有做這個動作,其實很簡單,我們知道配置記憶體要時間,
所以我們測量您程式片段所需的時間就可以看出,這以後我再示範,總之記得
Vector如果預先就知道您要push_back的大小,要事先reserve,如下

void __fastcall TForm2::BitBtn3Click(TObject *Sender)
{
    vector<int> src;

    struct Object_Mapping
    {
        TEdit* src;
        TEdit* dst;
    };

    Object_Mapping om[] =
    {
        {Edit11, Edit21},
        {Edit12, Edit22},
        {Edit13, Edit23},
        {Edit14, Edit24},
        {Edit15, Edit25},
        {Edit16, Edit26},
        {Edit17, Edit27},
        {Edit18, Edit28},
        {Edit19, Edit29},
        {Edit20, Edit30},
        //{Edit31, Edit32},
    };

    int k = sizeof(om) / sizeof(Object_Mapping);

    // 將整個陣列丟掉
    vector<int>().swap(src);

    // 預配置陣列大小
    src.reserve(k);
    // 由於每個值都會蓋過去,所以我們不對vector初始化
    // 讀入值

    for(int i = 0; i < k; ++i)
    {
        int value = atoi( AnsiString(om[i].src->Text).c_str() );
        src.push_back( value );
    }

    ///////////////////////////
    bubble_sort(src, 0);
    ///////////////////////////
    for(int i = 0; i < k; ++i)
    {
        om[i].dst->Text = src[i];
    }
}

其實很多技巧都牽涉到底層的硬體運作模式,所以寫程式不能只會軟體知識,到後期要高效能要了解一些硬體的東西,或是底層的處理方式,

但是基本上大型的程式有時候為了維護方便,基本上有時候會使用偷懶的方式,就是假設要設定一些指令,假設設定處理時間不會太久,會將設定指令都寫在一起,也就是我要設定指令時,呼叫相同一個函式就可以了,不用呼叫不同函式

最後,其實上訴這些東西算是一個概念,而要做排序在C++是不會使用bubble sort,會用STL的sort

Bubble sort要花O(n)
Sort指令只要花(nlogn)

基本上Sort會比較快就是了,當排序的東西比較多的時候,怎麼知道呢,就是量一量程式片段的時間就知道了,這裡一樣也不做示範

Sort用法如下

#include <algorithm>

void __fastcall TForm2::BitBtn3Click(TObject *Sender)
{
    vector<int> src;

    struct Object_Mapping
    {
        TEdit* src;
        TEdit* dst;
    };

    Object_Mapping om[] =
    {
        {Edit11, Edit21},
        {Edit12, Edit22},
        {Edit13, Edit23},
        {Edit14, Edit24},
        {Edit15, Edit25},
        {Edit16, Edit26},
        {Edit17, Edit27},
        {Edit18, Edit28},
        {Edit19, Edit29},
        {Edit20, Edit30},
        //{Edit31, Edit32},
    };

    int k = sizeof(om) / sizeof(Object_Mapping);

    // 將整個陣列丟掉
    vector<int>().swap(src);
    // 預配置陣列大小
    src.reserve(k);
    // 由於每個值都會蓋過去,所以我們不對vector初始化
    // 讀入值
    for(int i = 0; i < k; ++i)
    {
        int value = atoi( AnsiString(om[i].src->Text).c_str() );
        src.push_back( value );
    }
    
    ///////////////////////////
    //bubble_sort(src, 0);
    sort(src.begin(), src.end() );
    ///////////////////////////

    for(int i = 0; i < k; ++i)
    {
        om[i].dst->Text = src[i];
    }
}

:D簡單又方便,很多東西其實在C++都已經有現成的了,所以不用自己再寫,而且那些效率都是很高的,其實我也只會到這邊,還有一些特別的我也不是很清楚就是了

短短的一個bubble sort,可以牽扯到那麼多,這些都是基本的,不會真的不太容易寫好程式

而C#就我淺淺的知道,他是比較簡單,但似乎他很多東西都沒有(例如好用的vector和STL),所以到底好不好用就只能問會用的人了

這裡是範例連結
https://drive.google.com/open?id=0B6u5oifJv3EKYmxLY05TcjVJbkU



當程式設計師該有的覺悟

程式設計是很邪門的一門藝術,說他難,其實超級難,說他簡單,其實如果沒寫到Assembly Language,您會發現一個好的演算法,其實Code很少,並且易讀的,可能兩百行就達到您要的功能,並且超快,兩百行會很難打嗎,其實有寫好得Code就複製貼上就可以用了(笑)

當然如果是需要很多演算法的處理,也是數千行的程式碼拉,

就光學檢測這一塊,其實要找瑕疵就是二值化或Sobel然後Blob,找刮痕不外乎就是利用中值濾波找出背景,然後前景背景相減,剩下就是瑕疵了,上述演算法其實一般進去公司,公司都會有現成寫好的,就算沒有,其實有IPP(Intel的影像處理函式庫),您只要會呼叫他就可以了,而且速度超快,要寫的演算法通用的都有人幫您處理好了,不太需要自己寫過,

去哪間公司,其實到最後都一樣的,這邊提到IPP很快,那假設自己寫了5~6年某演算法處理速度已經到極限了,您知道有IPP這種東西比較快,您如果會使用的話,不會去用他嗎,怎麼可能?,最後就變成大家都用一樣的東西,每間公司有獨門的技術嗎?都是呼叫IPP,技術的不是技術了,

當然這裡只是指常用的演算法,會有一些函式庫可以呼叫,一些特殊的處理還是要自己寫的,不過就像提到的,其實很多東西都人家幫您寫好了,您只要拿的到並且會用,都不用自己寫的,所以說實在的也等於處理一下UI的東西,就可以賣了

其實就是因為很多東西被物件導向化(就被打包成一個函式),省下工程師的很多力氣,但是變成門檻太低了,因為大家都會做,導致薪水下降,要追求高薪必須越做越多,這裡是說有拿到一些現成的東西的,

那要是沒有一些現成函式呢,例如您學校剛畢業,什麼都沒有,那人家有現成函式的產品一下就做完了,您全部自己寫就會變成,累得要死,難易度超高,所以說寫程式也可以說非常難

看到這邊,通常我們都是屬於沒有自己東西的人,所以要自行創業的話,什麼東西都沒有,簡直是天方夜譚,不可能的任務

所以我們會遇到的狀況變成沒有資源,要花比人家多好幾倍的力氣,達到跟人家一樣的水平

總之就跳針一下,就是會有很多問題及工作量拉,所以要有以下覺悟

1. 寫Code通常是一翻兩瞪眼,通常一個地方出錯,整支程式就會一直當機,導致不能用,所以一定會遇到一直查問題的狀況,通常查問題會花很多時間,查不到或不會解決,就會一直卡在那裏,這種狀況是正常上下班不可能解決的,有時候沒查到Bug就只能沒日沒夜的查,所以如果您想做好,一定要有加班的心理準備

2. 如前面所提,我們是屬於沒有資源的那一群,所以如果真的要做程式設計師,最好學得差不多時,要開始累積自己的東西,例如寫一個函式要1個月,假設您是人家的員工,通常有簽一些條款,就是做出來的東西是公司的,這樣就變成,你做出來的程式您換公司不能拿來用,也就是就算您有能力再寫一份出來,您也要浪費一個月,

因此當覺得自己能力夠時,最好要開始累積自己的東西,一個函式也是東西,往往薪水和自己的東西比較起來,自己的東西比較重要,因為如果您想永續的發展,絕對要累積一些自己的東西,並在進入下一家公司時,做一些契約上的協調,也就是東西我在這家公司用完了,不管換公司還是自己創業,這些東西我必須可以直接拿來用,不然您會陷入一直打掉重練的狀況

3. 寫Code是很累的一件事,絕對不要想要偷懶,他有一套標準做法,例如class複製一定要寫Copy Constructor你如果偷懶沒寫,他在一些狀況下會出錯,這個以後我再做描述,總之不能為了省力,採取偷雞的作法,這樣您的東西會有問題,

4. 一些AOOP的東西是有用的,其實您只是沒看過人家怎麼用,它可以幫您省下很多Code,所以不要只停留在C語言的境界,您寫久了會操死,又寫不好

5. 一定要養成定期註解,通常趕案子不會一直註解,但是一段時間一定要註解一下,不然很容易忘記,也是不能為了省打字,就都不註解

6. 通常一個東西要賣錢,一定會有相關應用,像AOI程式就牽涉到做設備,通常要做軟體整合,工作量是很大的,而且往往您多想5分鐘做一些預先規劃,就像是模組預先規劃可以擴充,往後您修改時會比較好改,要是寫得太死,往後您要改等於是打掉重寫,一樣您會累死


總之看到這裡,結論就是不要懶,多想兩分鐘做規劃,往往比人家做事慢一點,但是往後您會省下很多力氣,真得要多想多練習,主管常常會亂追進度,寫Code不是賣東西,賣越快做越好,所以要按照自己步調來寫,不然通常往往會寫不好的(就是照主管的想法做事),並且不要太怕加班,因為有些東西每天只做8小時是做不好得,何況一般人上班會偷懶,可能一天扣掉開會,打雜,不知道花在寫Code上面有沒有4小時(這是真的喔,通常能持續寫6小時算很認真了)