Image Credit: https://www.facebook.com/HackMeiChu
Peter, GitHub
活躍的開源專案貢獻者
也是一位講者
COSCUP、MOPCON、PyCon and so on.
也是一位工程師
DevOps
後端開發;PHP、Python與JavaScript
軟體工程、系統架構設計與分析
網頁應用程式安全
財團法人工業技術研究院
應用資通訊技術至研究智慧電網領域 (2017~2021)
財團法人資訊工業策進會
醫資、健康照護應用服務與碳排放等領域 (2021~迄今)
第一部分
軟體工程是什麼?我們為什麼需要它?
介紹軟體開發流程的步驟
介紹需求分析、軟體設計(C4 Model)、物件導向與UML
介紹物件導向設計原則
延伸閱讀
第二部分
什麼是碳排放量?碳排放量是怎麼來的?
淺談ISO-14064以及透過標準,碳排放量該如何進行計算與量化?
延伸閱讀
第三部分
運用第一部分與第二部分打造與開發碳排放計算器系統(包含網路爬蟲開發)
結論
第一部分
軟體工程是什麼?我們為什麼需要它?
介紹軟體開發流程的步驟
介紹需求分析、軟體設計(C4 Model)、物件導向與UML
介紹物件導向設計原則
延伸閱讀
第二部分
什麼是碳排放量?碳排放量是怎麼來的?
淺談ISO-14064以及透過標準,碳排放量該如何進行計算與量化?
延伸閱讀
第三部分
運用第一部分與第二部分打造與開發碳排放計算器系統(包含網路爬蟲開發)
結論
軟體工程是什麼?
軟體工程就是解決程式設計師的問題
利用工程、系統化與流程化的方法進行軟體系統的開發
透過設計合適的軟體流程讓程式設計師或工程師有效率的開發
軟體系統開發透過不停地迭代與演進的過程
A Anti Pattern: Big ball of mud
軟體開發流程的步驟如下:
分析
需求分析,例如:確定客戶的需求、系統範圍與作業流程等
設計
系統設計與軟體設計等
實作
透過程式碼進行應用程式與系統的實作
測試
設計測試案例
撰寫單元測試程式
透過測試找出錯誤並確保軟體系統的功能實際與預期一致
使得構建、測試、發布軟體能夠更加地快捷、頻繁和可靠
我們可以使用「User Story」使用者故事來幫助釐清使用者需求
如果沒有做好需求分析的情況如下:
使用者故事範例
使用者能夠搜尋工作職缺
公司能夠發布新的職缺
不理想使用者故事範例
若以黑客松來說
使用者需求訪談可能來不及或是不好做
或是是團隊自己想到的產品進行開發
因此開發最簡可行性產品為首要目標
最簡可行性產品,Minimum Viable Product
使用者需求分析應著重在各個小組團隊內部認為的產品發展
透過使用者故事釐清產品或服務的流程
請各位針對自己要開發的產品或服務進行使用者需求分析
並產生出使用者故事範例至少3項
討論時間:8分鐘
抽1~2位進行分享
泳道圖,是一種流程圖,能區分業務流程及工作分擔
透過泳道圖能夠了解使用者業務流程
請各位將前述完成至少三項的使用者需求分析抽出至少一項
將該項轉換成泳道圖描述該需求分析的業務流程圖
討論時間:8分鐘
抽1位進行分享
為什麼要先設計?
不好的設計就會像......
我們在設計上會希望達到與重視模組之間的:
高凝聚力,High Cohesion
模組內部功能或資料之間的相依程度
低耦合,Low Coupling
強調模組之間的獨立程度
將系統拆分成數個子系統/元件/模組
決定這些子系統/元件/模組如何互動
決定這些子系統/元件模組的介面
靜態面:展現系統切割成不同的子系統/元件/模組,以及之間介面連結
動態面:展現系統切割不同的子系統/元件/模組之動態互動行為
資料在各個子系統/元件/模組之間如何傳遞與處理以及其執行的動態行為等
C4 Model用途
是用來視覺化軟體系統架構,並主要分成4層
這層主要是在表示整個軟體系統最上層的架構
該層專注在人(角色)與各個系統之間的互動
而不是注重在底層技術、通訊協定或是更低的技術細節
與Docker Container不同
該層將Context層中
將各個內部系統進行拆分成資料庫與各個應用程式標示之間的互動
上述這些資料庫與應用程式會標註之間使用最主要的互動方式
例如:使用JSON/HTTPS、SMT或XML/HTTPS等
該層將Container層中的應用程式進行拆分
拆分成「數個元件」,也就是所謂的Components
同時標示每個元件它們負責功能、技術與實作等細節
該層將Component層中的每個元件往下展開
用來描述透過Codes如何進行元件的實作
使用UML類別圖、Entity–relationship model或其他類似示意圖
該層建議將最重要或最複雜的元件細節表示出來
請先安裝好Draw.io,或是直接使用線上版
https://www.drawio.com(建議)
Windows可以安裝:「windows-no-installer.exe」的版本
MAC OS版可以依照CPU不同架構安裝副名為「dmg」的版本
請各組將自身要開發的服務或產品透過C4 Model方式進行繪製
包含Context層、Container層與Component層
Container層至少展開一個子系統
Component層至少將一個Component進行展開
討論時間:8分鐘,抽1位進行分享
各個擊破法
透過抽象化的概念,將問題的解法透過由上而下的分解
將複雜的問題拆解成數個較小且容易的模組
經由小模組的解決,進而解決較為複雜的問題
紀錄系統設計過程中遭遇的議題
議題內容
現有會員註冊欄位過多,平均會花上5分鐘進行註冊
可能解決方案
透過第三方登入,例如提供Facebook或Google帳號登入
精簡註冊欄位的資料
最後解決方案
實作多功能註冊
紀錄系統設計過程中遭遇的議題
決策理由
最大彈性:提供使用者多種註冊選擇
資料一致性:社群註冊自動取得必要資料,不重複要求
安全性考量:保留手機驗證防止機器人註冊
轉換率優化:任一方式皆可在 1 分鐘內完成註冊
請各組回去練習
請各組回去將先前練習的C4 Model圖進行分析
找出在系統設計的過程中潛在的設計議題並透過前述方法進行分析
類別有父類別與子類別之間的關係,子類別會繼承父類別的屬性和方法
公開繼承,子類別能夠透過繼承父類別存取與修改公開的方法(public method)
私有繼承,子類別能夠透過繼承父類別存取私有的方法(private method)
保護繼承,子類別能夠透過繼承父類別存取與修改公開的方法(protected method)
較為少用且建議不用
不同的物件可以有相同的方法名稱,但呈現出不同的行為
多型是繼承可達成的重要效益
統一塑模語言
UML,Unified Modeling Language
用來說明、視覺化、建構與編寫
正在開發的、物件導向的、軟體密集系統的製品的開放方法
可用來表達物件內涵與物件間的互動關係,Class Diagram類別圖
Dependency相依;「uses a」
Association關聯;「knows a」
Composition組合;「has a」
Aggregation聚合;「has a」
Inheritance繼承;「is a」
前面有「+」代表是public方法或是變數名稱
前面是「-」代表是private方法或是變數名稱
Dependency相依;「uses a」
X使用Y,X呼叫Y的方法(method);熟識
Shorten Relationship短期關係
Dependency相依;「uses a」
X使用Y,X呼叫Y的方法(method);熟識
Shorten Relationship短期關係
Association關聯;「Knows a」
X熟識Y,X包含Y的reference(Y是X的一個資料成員)
Long Term Relationship長期關係
Association關聯;「Knows a」
X熟識Y,X包含Y的reference(Y是X的一個資料成員)
Long Term Relationship長期關係
關係中只有一方看得到另外一方,則用有箭頭的關聯線表達這樣的可視性(Navigation)
數量(Multiplicity)
關聯端點上可寫上數量,代表此參與此關聯關係之物件個數
例如:1個學生(Student)可以修0~10門課(Course)
class Student {
Course[] courses = new Course[10];
}
class Course {
}
組合(Composition)與聚合(Aggregation)
都是「has a」或是「part of」之關係
聚合(Aggregation )
在聚合關係中,若 Y 包含於 X ,Y 可再被其他物件包含
當大物件 X 消失,小物件 Y 仍然存在
組合(Composition )
在組合關係中,若 Y 包含於 X ,Y不可被其他物件包含
X 與 Y 之生命週期一致:若刪除 X ,則 Y隨之被刪除
組合(Composition)與聚合(Aggregation)範例
繼承與實作
繼承,Inheritance,「is a」
X is derived from Y
X(子類別)繼承Y(父類別)
實作,Implementation,「can do」
X can do (replace) the jobs of Y
X(類別) implements Y(interface介面)
實作Implementation範例
抽象類別
無法產生物件,但能夠被繼承
例如:
交通工具Vehicle可分為Bike和Car
是一種完全分類,不會有物件從Vehicle產生出來
抽象方法
宣告方法的介面(參數及回傳型態)
但是不具備實作Implementation
例如:
所有的交通工具都會向左轉與向右轉
但怎麼做由子類別自行定義
不同的物件可以有相同的方法名稱
但呈現出不同的行為
多型是繼承可達成的重要效益
以下是透過多型的特性進行動態綁定的方法
class Client {
double op1(Shape s) {
double area = s.getArea();
return area;
}
}
客戶有兩種,一類是公司客戶,另外一類為個人客戶
客戶有名稱、住址等屬性資料
個人客戶則額外有信用卡帳號資料
公司客戶額外有信用狀態資料
客戶會下訂單買產品
產品需記錄編號、名稱與價錢等資料
識別出所有類別的名詞
客戶、公司客戶、個人客戶、訂單與產品資訊
Customer、CorporateCustomer、PersonalCustomer
Order
ProductInfo
建立類別之間的關係
客戶有兩種,一類是公司客戶,另外一類為個人客戶
客戶會下訂單買產品
設定屬性(Attribute)
客戶有名稱與住址等屬性資料
個人則額外有信用卡帳號資料
公司客戶額外有信用狀態資料
產品需記錄編號、名稱與價錢等資料
設定方法(Method)
客戶會下訂單買產品
延伸C4 Model練習
請各位進行討論,時間:8分鐘,抽1位進行分享
利用先前C4 Model練習之產出識別重要的Component
將先前拆解出來的Component往下拆分一層Code
利用學到的UML知識將前述拆出的Code進行設計:
設定與識別所需要的各個類別
建立各類別之間的關係(關聯)
設定各個類別屬性與方法
這裡先解釋SOILD原則
適當使用介面設計能提高程式的擴展性
SOLID原則
單一職責原則Single Responsibility Principle, SRP
每個類別應具備單一功能
並且該功能由這個類別/模組進行封裝起來
可以視為「改變的原因」
一個類別或模組僅有一個「改變的原因」
初步設計
應用SRP之後
SOLID原則
開放關閉原則OpenClose Principle, OCP
一個模組必須有彈性的開放往後的擴充
並且避免因為修改而影響到大量程式
初步設計
應用OCP之後
SOLID原則
理氏替換原則,Liskov Substitution Principle, LSP
衍生類別物件可以在程式中代替其基礎類別物件
父類別的測試案例,子類別也要能通過
Example: Rectangle and Square
是繼承關係,但不吻合 LSP
SOLID原則
介面隔離原則,Interface Segregation Principle, ISP, ISP
ISP 拆分非常龐大的介面成為更小的和更具體的介面
初步設計
應用ISP之後
SOLID原則
反向依賴原則,Dependency Inversion Principle, DIP
從高層次的模組開始,再設計低層詳細的模組
高階模組不應該依賴低階模組,兩者必須依賴抽象層
初步設計(違反DIP)
應用DIP之後
在設計類別上運用SOLID原則,進行選用其中一種原則
以及說明運用該原則的原因
討論時間:7分鐘
抽1位進行回答
除了物件導向設計原則SOLID之外
還可以考慮運用設計模式
設計模式不是演算法
如果沒有設計模式...
A Anti Pattern: Big ball of mud
設計模式的內容
名稱(Name)、目的(Intent)、別名(Also Know As)
動機(Motivation)、應用性
結構,利用UML類別圖與互動圖表示物件之間靜態結構互動關係
結果,優缺點比較
實作,範例程式碼(Sample Codes)
知名案例(Known Cases)
其他相關的設計模式比較(Related Patterns)
設計模式的內容
設計模式有23種
創建型模式(Creational Patterns)
如何建立對象,其核心思想是要把對象的建立和使用分離
結構型模式(Structural Patterns)
藉由同種道理來了解元件間的關係,以簡化設計
行為型模式(Behavioural Patterns)
用來識別對象之間的常用交流模式並加以實現
可以透過Prompt的方式進行C4 Model的繪製
可以透過Prompt的方式進行UML的繪製
建議整個了解C4 Model與UML後再進行繪製
以Claude.ai為例,https://claude.ai
請繪製一個C4 Model之Context層的架構圖,包含:
「Personal Customer」去存取「Internet Banking System」
「Internet Banking System」能夠去存取外部的「Mainframe Banking System」
「Internet Banking System」能夠去存取外部的「E-mail System」
以Claude.ai為例,https://claude.ai
請繪製一個C4 Model之Context層的架構圖並以Darwio的XML檔表示,包含:
「Personal Customer」去存取「Internet Banking System」
「Internet Banking System」能夠去存取外部的「Mainframe Banking System」
「Internet Banking System」能夠去存取外部的「E-mail System」
以Claude.ai為例,https://claude.ai
請繪製一個C4 Model之Context層的架構圖並以Darwio的XML檔表示,包含:
「Personal Customer」去存取「Internet Banking System」
「Internet Banking System」能夠去存取外部的「Mainframe Banking System」
「Internet Banking System」能夠去存取外部的「E-mail System」
生成出來的XML檔案複製並匯入到Drawio
第一部分
軟體工程是什麼?我們為什麼需要它?
介紹軟體開發流程的步驟
介紹需求分析、軟體設計(C4 Model)、物件導向與UML
介紹物件導向設計原則
延伸閱讀
第二部分
什麼是碳排放量?碳排放量是怎麼來的?
淺談ISO-14064以及透過標準,碳排放量該如何進行計算與量化?
延伸閱讀
第三部分
運用第一部分與第二部分打造與開發碳排放計算器系統(包含網路爬蟲開發)
結論
溫室效應
溫室效應是指行星的大氣層因為吸收輻射能量,使得行星表面升溫的效應
溫室氣體
水蒸氣(H2O)、臭氧(O3)、二氧化碳(CO2)、氧化亞氮(N2O)、甲烷(CH4)、氫氟氯碳化物類(CFCs,HFCs,HCFCs)、全氟碳化物(PFCs)及六氟化硫(SF6)
為了要量化碳排放,因此需要有公式進行二氧化碳當量之計算
使用特定溫室氣體質量乘以全球暖化潛勢計算而得
全球暖化潛勢值(IPCC)
是衡量溫室氣體對全球暖化影響的一種手段
是將特定氣體和相同質量二氧化碳比較之下,造成全球暖化的相對能力。
碳排放量如何計算?
例如:排放係數法,將能源使用量,乘上相對應的溫室氣體排放係數
直接監測法
直接監測排氣濃度和流率量測溫室氣體排放量,準確度高但少見
質量平衡法
化學平衡法;質量守恆定律
舉例化學式:C+O2→CO2
燃燒1莫耳的碳會產生1莫耳之二氧化碳
燃燒1公斤的碳產生44/12 = 3.67 kg之二氧化碳(排放係數)
排放係數法
排放量 = 活動數據 * 排放係數 * GWP(全球排放潛勢)
PyCon TW 2024
那些關於我開發碳排放量計算系統的經驗談
第一部分
軟體工程是什麼?我們為什麼需要它?
介紹軟體開發流程的步驟
介紹需求分析、軟體設計(C4 Model)、物件導向與UML
介紹物件導向設計原則
延伸閱讀
第二部分
什麼是碳排放量?碳排放量是怎麼來的?
淺談ISO-14064以及透過標準,碳排放量該如何進行計算與量化?
延伸閱讀
第三部分
運用第一部分與第二部分打造與開發碳排放計算器系統(包含網路爬蟲開發)
結論
使用者需求分析
參考碳排金好算網站:https://pj.ftis.org.tw/CFCv2/CFC/Index
使用者有一個使用者介面進去之後,使用者能夠透過排放係數法進行計算
使用者只需要使用排放係數法進行計算
其中的電力碳排放進行計算
透過上述的類型分別填入相對應的活動量
使用者操作概念圖
以泳道圖表示使用者操作概念圖
由於使用者需要用到碳排放計算器介面上,有需要選擇碳排放係數的需求
因此需要以網路爬蟲進行自動化的碳排放係數收集
需要收集碳排放係數的標的網站如下:
能源署—電力排碳係數(目前該系統僅需要此資料)
https://www.moeaea.gov.tw/ecw/populace/content/ContentDesc.aspx?menu_id=26391
IPCC潛勢值
https://github.com/openclimatedata/globalwarmingpotentials
由於使用者僅需要一個頁面,因次前端使用單一頁面即可,使用技術如下:
jQuery 3.x版本並搭配Bootstrap 4
產生出的單一頁面搭配Nginx並部署到ColoCrossing的虛擬機器上
由於計算可能會稍微複雜,故需要透過後端API服務來完成,使用技術如下:
使用Python 3.12進行實作並搭配FastAPI框架進行開發
整體API服務以Docker容器化為主
並以docker-compose方式進行服務進行建置
部署方式將前述的容器化之後,透過Nginx反向代理的方式將服務曝露出來
開發碳排放係數模組時需要開發網路爬蟲模組自動化收集,使用技術如下:
Python 3.12並搭配requests的Python套件進行網路爬蟲開發
整體模組以Docker容器化為主
部署方式將前述的容器化之後,設定排程定期去收集碳排放係數
由於先前已經有現有的專案完成碳排放係數模組的網路爬蟲
故直接採用並與該系統進行整合
以C4 Model表示
Context
以C4 Model表示
Container
以C4 Model表示
Component
以C4 Model表示
Code
在碳排放計算器類別中,日後會有不同計算方法的議題
例如:質量平衡法與排放係數法等
不同的計算器有不同的計算方法
因此在實作該類別上,有兩種設計模式的方法可以達到前述的議題
依據依賴反轉(Dependency Inversion)的設計模式準則
設計基礎計算器(Base Calculator);建立抽象類別或介面
分別建立固定源、移動源與冷媒逸散碳排放量計算器
為日後實作計算器提供彈性的空間
標的網站
在GitHub上有個專案定期會更新IPCC潛勢值
globalwarmingpotentials.csv
解析專案裡面的CSV檔即可取得所需要的潛勢值
由於是使用既有的開源專案
因此需要對該專案進行理解
會用到的爬蟲模組如下:
electric,以電力排碳係數收集模組講解實作為例
gwp_factor,溫室氣體排放係數管理表6.0.4版
gwp_value,(IPCC AR4, AR5, AR6) 溫室氣體潛勢值
由於政府部門改組,從能源局變為能源署,標的網站內容有變
故參考與使用moeaea_handler.py
發送請求至:
將前述的電力排碳係數連結取出之後,再依序請求連結
每個連結對應的內容均有一個PDF檔,將該PDF檔案擷取連結出來,即可下載該PDF檔案
需要解析電力碳排放係數PDF檔案內容
需要解析電力碳排放係數PDF檔案內容
採用pdftotext進行PDF檔案之解析
將前述解析出來的內容進行字串解析
將所需要的電力碳排放係數給解析出來
收集到的係數資料應儲存到資料庫中
針對不同的資料特性,有選擇資料庫類型的議題
為了要儲存資料,因此需要設計資料表綱要
為了要儲存資料,需要設計資料表綱要
分析碳排放係數資料特性
有分年度之碳排放係數,具有時間性
(IPCC AR4, AR5, AR6) 溫室氣體潛勢值
係數資料彼此之間沒有關聯,但是為固定值
選擇資料庫
碳排放係數看起來沒有關聯性
可以考慮整理成JSON格式後儲存到MongoDB中
延伸問題與討論:其他特性的資料關於選用的資料庫
資料在什麼情況下需要使用關聯式資料庫?
例如?
有資料看起來像是沒有關聯但是每隔一段時間就會新增
例如:感測器資料、電表資料與氣象資料等
關聯式資料表綱要設計步驟
需求分析:了解資料的需求以及相關的使用者案例情境
識別實體(Entity):識別出最主要的實體與相關的屬性
定義關聯性:定義實體與屬性之間關聯性的集合關係
集合,Carnality,例如:1對1、1對多、多對1與多對多
常見的關聯式資料庫有:
MySQL
MariaDB
PostgreSQL
Microsoft SQL Server
......
有資料看起來像是沒有關聯但是每隔一段時間就會新增
例如:感測器資料、電表資料與氣象資料等
索引與timestamp時間戳記有關
資料量大小會持續的增大
在一個範圍中,通常會進行
聚合(aggregated)、降採樣(down-sampled)、查詢(queries)
有密集寫入的效能需求
常見的時序型資料庫有:
InfluxDB Time Series Data Platform | InfluxData
https://www.influxdata.com
TimeScaleDB
https://www.timescale.com
ClickHouse
https://clickhouse.com
延伸閱讀:My journey of the time series database
先安裝好Docker與Git指令
專案網址
$ git clone https://github.com/peter279k/hackmeichu2025
$ cd hackmeichu2025
$ docker compose up --build -d
$ docker compose ps
from app.modules.BaseCalculator import BaseCalculator
class CalculatorService:
def __init__(self, calculator: BaseCalculator):
self.calculator = calculator
def calculate(self, activity_value: float, factor: float):
return self.calculator.calculate(activity_value, factor)
from app.modules.BaseCalculator import BaseCalculator
class ElectronicCalculator(BaseCalculator):
def calculate(self, activity_value: float, factor: float):
return round(activity_value * factor / 1000, 2)
from abc import ABC, abstractmethod
class BaseCalculator(ABC):
@abstractmethod
def calculate(self, activity_value: float, factor: float):
pass
單元測試,每一個函式或方法視為一個最小單位
e.g. backend/app/tests/test_carbon_factor.py
開發產品所需要的軟體系統時,導入軟體工程的方法
能夠讓整體的過程有流程化,釐清目標以及需求
以由上到下(Top-down)的方式能夠理解整體軟體系統的樣貌
透過不停的迭代讓整理的軟體系統不斷的演進
透過單元測試去找出軟體系統上的錯誤
(非在黑客松階段)軟體系統考慮程式碼重構
若在需求不明確或式在找方向時,可以考慮MVP
想好再寫
TCSE 2024 Keynote Speech–軟體工程的人生哲學
使用者故事
物件導向基本概念
If you think good architecture is expensive, try bad architecture. —Brian Foote and Joseph Yoder
Modern Web Conference 2023
給開發者的進階網路爬蟲開發之旅