iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 2
2
Modern Web

從比入門再往前一點開始,一直到深入React.js系列 第 2

【Day.02】為什麼需要框架 - 用工廠模式,開始重新思考共用架構

  • 分享至 

  • xImage
  •  

(2024/04/06更新) 因應React在18後更新了許多不同的語法,更新後的教學之後將陸續放在 新的blog 中,歡迎讀者到該處閱讀,我依然會回覆這邊的提問


在Day1,我們寫了一份很爛的程式碼,現在要來優化它。

認識Design Pattern

當程式碼規模變大,我們勢必要分檔才能讓程式碼更能共用、易讀。但是要怎麼分檔才會好呢? 長年下來,工程師們逐漸發現一些特定的架構劃分方式可以普遍套用在專案中,這些設計模式(Design Pattern)就被整理了下來。

而工廠模式就是的其中一種很著名Design Pattern。

工廠模式

同樣是給麵粉。細麵工廠只會生出細麵,刀削麵工廠只會生出刀削麵。

聽起來好像是廢話,但這就是工廠模式的意義:

「工廠只負責把正確的材料,加工成他會做的東西。」

工廠模式,指的是某一個程式模組只負責製造一種東西,不管使用者在哪裡製造這個東西、拿這個東西去做什麼,都不會影響這段code定義的「製造過程」。

我們現在就是要讓「製造、產生Menu」的過程變成「共同行為」。

如果你搜尋「工廠模式」,你會發現一般是希望多個相近類別的物件透過繼承一個interface來實作抽象化的相同行為來取得工廠的製造物。然而這是因為強型態語言中,我們如果不使用介面或多型,程式語言會沒有辦法主動知道一個未知類別是不是有共同的方法、屬性。而Javascript是弱型態語言,我們可以像這樣,在不知道A是誰的狀況下,直接呼叫參數物件的方法:

function handleUnknownClass( A ){
    A.getDomItem();
}

這樣雖然方便,但相對應的風險是當A沒有getDomItem()時,程式就會死掉。

那我們要如何實現工廠模式呢?

在大部分的物件導向程式語言中,我們可以把有相同性質的程式碼整理在一起,並把這一包模組稱為class

但Javascript在ES5之前,是不存在class這個東西的。如果想要產生class,必須要用function搭配new這個關鍵字使用(等等會講解)。

例如:

function Apple(){
    let AppleContainer = document.createElement('div');
    AppleContainer.textContent="蘋果";
    return AppleContainer;
}

let AppleInstance1 = new Apple();
let AppleInstance2 = new Apple();

這個時候 AppleInstance1AppleInstance2就會都會是內文是「蘋果」的div。

在JS中,如果function沒有return值,則new這個關鍵字可以用來創造「實體」。以人來比喻的話,我們會認為人類是一種「類別(class)」,小明和小華會是人類的「實體(Instance)」,雖然小明和小華都有人類的特性,但這兩個人都是獨立的個體。同樣的,Apple是一種「類別(class)」,AppleInstance1AppleInstance2則是Apple的「實體(Instance)」,他們都有Apple的特性,但這兩個都是獨立的個體。

在目前我們的範例中,因為有return一個DOM元素,所以效果和一般的函式差不多,不太算是Apple的物件實體,我們會在下一篇說明如何真正使用JS new出實體。

用工廠模式,開始重新思考共用架構

於是我們照著上面的方法,開始拆分結構,並把工廠模式加入其中,就變成了這樣:

  • js/component/menu.js(新創建)
function Menu(menuItemWording){
    let menuContainer = document.createElement('div');
    menuContainer.setAttribute('class',"menu-container");

    //藍色標題
    let title = document.createElement('p')
    title.setAttribute('class',"menu-title")
    title.textContent="Andy Chang的Like";
    menuContainer.appendChild(title);

    //列表的container
    let menu = document.createElement('ul');
    menu.setAttribute('class',"menu")

    menuItemWording.forEach((item)=>{
        let menuItem = document.createElement('li');
        menuItem.setAttribute('class',"menu-item");
        menuItem.textContent = item;
        menu.appendChild(menuItem);
    });

    //控制「列表的container」開關的按鈕
    let menuBtn = document.createElement('button');
    let isOpen = false;
    menuBtn.setAttribute('class',"menu-btn");
    menuBtn.textContent="V";
    menuBtn.onclick = function() {
        // 「!」會把true變false,false變true
        isOpen = !isOpen;

        if(isOpen){
            menu.style.display = "block";
            menuBtn.textContent="^";
        }
        else{
            menu.style.display = "none";
            menuBtn.textContent="V";
        }  
    }

    menuContainer.appendChild(menuBtn);
    menuContainer.appendChild(menu);

    return menuContainer;
}
  • index.html
    </body>
    <script src="./js/component/menu.js" type="text/javascript"></script>
    <script src="./js/index.js" type="text/javascript"></script>
</html>
  • js/index.js
// 文字
let menuItemWording=[
    "Like的發問",
    "Like的回答",
    "Like的文章",
    "Like的留言"
];

const MenuInstance = new Menu(menuItemWording);

document.getElementById('root').appendChild(MenuInstance);

看起來好多了,但還不夠好

如果class只能做這樣,那其實跟一般的函式沒甚麼差別。我們下一篇會繼續來談如何運用JS在ES5前的class讓程式更直覺。


上一篇
【Day.01】為什麼需要框架 - 從全部塞在一起開始頭痛
下一篇
【Day.03】為什麼需要框架 - 打造元件共同功能
系列文
從比入門再往前一點開始,一直到深入React.js30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言