สรุป Final วิชา 2110336 Software Engineering II โดย แสนยากร เสียงเสนาะ
ปล. เอาจริง ๆ ไม่ควรจะเรียกว่า Summary เพราะ detail มากเกินไป อีกเรื่องหนึ่งคือน่าจะมีพิมพ์ผิดเยอะด้วยความรีบ หรืออาจจะมีจุดที่ไม่ค่อยเคลียร์ หากเจอโปรดแจ้งให้ทราบด้วย
Part A (10 points)
Part B (25 points)
Part C (25 points)
Part D (10 points)
Refactoring คือการจัดเรียงหรือจัดระเบียบ code ใหม่ เพื่อป้องกันปัญหาที่อาจจะเกิดในอนาคต โดยเมืื่อแก้แล้วจะระบบจะต้องสามารถทำงานได้เหมือนเดิม
Bad Smell (Code Smell) คือเมื่อตรวจสอบ source code แล้วได้กลิ่นแปลก ๆ (สิ่งที่อาจจะก่อให้เกิดปัญหาในอนาคต) เมื่อพบแล้วจะต้องทำ refactoring ต่อไป
Bad Smell ถูกนิยามไว้ 22 แบบและ Refactoring ถูกนิยามไว้ 72 แต่จะเรียนแค่บางแบบดังนี้
"กลิ่นแรงสุด"
คือการเขียน code ซ้ำกันหลาย ๆ แห่ง
แก้ได้ด้วย Extract Method
Extract Method คือวิธีการนำ code ที่ซ้ำกัน แยกออกเป็น Method ใหม่แล้วเรียกใช้ Method นี้แทน
sum_a = 0
for i in range(len(arr_a)):
sum_a += arr_a[i]
sum_b = 0
for i in range(len(arr_b)):
sum_b += arr_b[i]
แก้เป็น
# extracted function/method
def get_sum(arr):
s = 0
for i in range(len(arr))
s += arr[i]
return a
sum_a = get_sum(arr_a)
sum_b = get_sum(arr_b)
คือ method หรือ function ที่ยาวมาก ๆ (ยาวมากแค่ไหนขึ้นอยู่กับผู้เขียน) ส่วนมากจะพิจารณาโดยคำนึงเรื่อง maintenance
หนึ่ง function / method ควรทำหน้าที่แค่อย่างเดียว
แก้ได้โดนการใช้ Extract Method
Extract Method คือวิธีการนำ code ที่ซ้ำกัน แยกออกเป็น Method ใหม่แล้วเรียกใช้ Method นี้แทน
คือมีการใช้ตัวแปรชั่วคราวมากเกินไป อาจทำให้เกิด dependency ได้
แก้ได้โดนการใช้ Replace Temp With Query
Replace Temp With Query คือการแทนที่ด้วยการ call ไปเลยไม่ต้องมีตัวแปรชั่วคราว
basePrice = quantity * itemPrice
if basePrice > 1000: return basePrice * 0.95
return basePrice * 0.98
แก้เป็น
if getBasePrice() > 1000: return getBasePrice() * 0.95
return getBasePrice() * 0.98
แต่อาจเกิดปัญหาเรื่อง Performance เพราะต้อง query ของเดิมซ้ำ ๆ
คือ method มี parameter เยอะเกินไป ซึ่งยากต่อการใช้งาน
แก้ได้โดนการใช้ Introduction Parameter Object หรือ Replace Parameter With Method
Introduction Parameter Object คือการเอา parameter รวมกันเป็น object
Replace Parameter With Method คือการตัด parameter บางอย่างออกแล้วไปคำนวณข้างในแทน
price = quantity * itemPrice
discountLevel = getDiscountLevel(price)
finalPrice = discountedPrice(price, discountLevel)
price = quantity * itemPrice
finalPrice = discountedPrice(price)
def discountedPrice(price):
discountLevel = getDiscountLevel(price)
# ...
คือคลาสใหญ่เกินไป มีหลาย method และตัวแปรมากเกินไป
แก้ได้โดนการใช้ Extract Class
Extract Class คือวิธีการนำ methods / variables ที่ไม่ค่อยเกี่ยวข้องกันแยกออกไปเป็น class หรือ class ย่อย
คือ method ใน class A ไปเรียกใช้ method ใน class B ทำให้ method ใน class A รู้สึก "อิจฉา"
แก้ได้โดนการใช้ Move Method
Move Method คือการย้าย method ที่มีการเรียก method จาก class อื่นเยอะ ๆ ให้ย้ายไปเป็น method ของ class นั้นแทนเลย
คือ class ที่ไม่ค่อยได้ทำอะไร
แก้ได้โดนการใช้ Collapse Hierachy
Collapse Hierachy คือรวมเข้ากับคลาสที่ inherit มา
โดยทั่วไป middle man หมายถึงคนกลาง ทำหน้าที่รับ จากนั้นแปลง แล้วค่อยส่งต่อ (อันนั้นจะเรียกว่า Adaptor) แต่บางทีอาจจะมี middle man ที่ไม่ได้ทำอะไรมากมายขนาดนั้น
แก้ได้โดนการใช้ Remove Middle Man
Remove Middle Man ก็คือให้มันต่อตรงเลย แล้วลบ middle man ออก
data class คือ class เก็บแต่ attributes หรือมีแต่ getter / setter
"กลิ่นน้อย"
เป็น bad smell ที่ data class มีหน้าที่อย่างอื่นมากกว่าแค่เก็บตัวแปร
แก้ได้โดนการใช้ Extract/Move Method
"กลิ่นน้อย"
คือ comment ทั่ว ๆ ไป แต่ถ้าหากมี comment นั่นหมายความว่ามันเริ่มซับซ้อนแล้ว นั่นคือจุดที่เราสามารถ extract ได้
แก้ได้โดนการใช้ Extract Method/Variable
CI (Continuous Integration) - ช่วยเรื่องการ build และ test ด้วย automation ซึ่งทำได้บ่อย ๆ และช่วยให้ได้ feedback จาก user ได้เร็วขึ่้น รวมถึงสามารถหาบัคได้เร็วขึ้นด้วย รวมกันเป็น Agile Menifesto (สรุป คือ Build, Test, Merge)
CD (Countinuos Delivery) - หลังจากผ่าน CI แล้วจะส่ง Code ที่ตรวจสอบ (หรือเรียกว่า Change) แล้วเข้าไปที่ Repository และ Deploy ขึ้น Production ได้เลย แต่ทีม Developer สามารถเลือกได้ว่าจะเอาขึ้นหรือไม่ขึ้น
CD (Countinuos Deployment) - ขยายมาจาก Countinuos Delivery คือทำหน้าที่ Deploy Code จาก Repository ขึ้นไปบน Production Environment แบบอัตโนมัติสำหรับทุก Change
คือการร่วมมือกันของ Dev กับ Ops โดย Developer จะหมายถึงคนเขียน Code และ Operator จะหมายถึงคนทำหน้าที่ Deploy ซึ่งโดยส่วนมาก เมื่อก่อน Dev กับ Ops ทำงานไม่คุยกัน ทำงานแบบ Silo ทำให้ทำงานได้ช้า ดังนั้นการทำ DevOps เลยพ่วงมากับการทำ CI/CD ด้วย
มีให้ใช้หลายอย่างมาก แต่การใช้จะได้ทั้งทีมต้องมี mindset แบบ DevOps เสียก่อน!!!
อ้างอิง CI/CD เรื่อง Docker จะเกี่ยวข้องกับ Phase Deploy and Operate
เวลาจะ build app ด้วย Docker เวลาเราสั่ง docker build .
มันจะไปอ่าน Dockerfile จากนั้นก็จะ execute ทีละคำสั่งใน Dockerfile (เรียกว่า Layer) จากนั้นจะได้ Docker Image ออกมา
จากนั้นเราจะสามารถเอา Docker image ไปรับจริง ๆ บนเครื่องของเราได้ ด้วย docker run <image-name>
ซึ่งจะได้ออกมาเป็น Docker container
นอกจากนี้เราสามารถเอา Docker image ไปขึ้น Hub เช่น Docker Hub, Github Container Registry ได้เพื่อให้คนอื่น ๆ มาใช้ image เราได้
Docker ช่วยให้เราสามารถรัน App โดยได้ผลลัพธ์เหมือน ๆ กันโดยไม่ขึ้นอยู่กับ OS ของ Host หรือก็คือเราสามารถเอา Docker image ไปรันได้ในหลาย ๆ Host Environment และไม่ต้อง Configure Host เลย
หลัก ๆ จะต่างกันตรงที่ Docker container จะรันบน Docker daemon ซึ่งต่อกับ Host OS โดยตรง ในขณะที่ VM จะสร้าง Guest OS (ของใครของมัน) ขึ้นมาและเอา app ไปรันบน Guest OS นั้นซึ่งใช้ทรัพยากรมากกว่า
จากรูป จะหมายความว่า Container Apache expose port 80 อยู่และ Container MySQL expose port 3306 ซึ่งเรียกอันนี้ว่า Container Port โดย user ได้ทำการ map Host Port 8080 ไปที่ Container Apache Port 80 และ map Host Port 6603 ไปที่ Container MySQL Port 3306
ดังนั้นโดยผลลัพธ์ user สามารถเข้าถึง Apache ได้ด้วย Host Port 8080 (e.g. localhost:8080) และ MySQL ได้ด้วย Host Port 6603 นั่นเอง (e.g. localhost:6603)
นอกจากนี้ ถ้าอยากให้แต่ละ Container คุยหากันได้ ทั้งสองอันจะต้องอยู่ใน Network อันเดียวกัน
from Martin Fowler
Reject unexpected form input
Encode HTML output
document.getElementById('name') = 'Sandra Day O' Connor' // unescapted string
Bind parameters for database queries
ระวังข้อมูลถูกขโมยผ่าน input ที่อันตราย
String query = "INSERT INTO students (last_name, first_name) VALUES ('" + lastName + "','" + firstName + "')'";
ถ้า firstName เป็น Robert'); DROP TABLE students; --
table student ก็จะหายไปเลย เรียกว่า SQL injection
Parameter Binding
PreparedStatement stmt = "INSERT INTO students (last_name, first_name) VALUES (?, ?)'";
stmt.setString(1, lastName) // binding
stmt.setString(2, firstName) // binding
clean and safe code
Common Misconceptions
$where
In Summary
Protech Data in transits
ระหว่าง Client & Server ถ้าไม่มีการเข้ารหัสอาจเกิดปัญหาได้เช่น
อาจเพิ่มความปลอดภัยได้จาก
ใช้ HTTPS กับทุกที่
HSTS (HTTP Strict Transport Secure)
Protect Cookie
Secure
, HttpOnly
เพื่อให้ส่งผ่าน HTTPS และ ไม่ให้ javascript ดึงข้อมูล Cookie ได้ตามลำดับCache Control
no-cache
คือต้อง revalidate ทุกครั้งที่จะใช้no-store
คือไม่ต้องเก็บเลยmust-revaludate
คือ local ใช้ได้จนกระทั่งหมดอายุแล้วค่อย revalidateVerify security
In Summary
Hash and salt username / password
Authentication and Authorization
Protect User Session
Testing คือกระบวนการเพื่อดูว่าเป็นไปตาม requirement หรือเปล่า ตรงกับความคาดหวังหรือเปล่า
Test Case ประกอบไปด้วย input และ expected output
Error (Mistake) หมายถึงสิ่งที่ software developer เข้าใจผิดหรือพลาดไป
Fault/Defect/Bug หมายถึงความผิดพลาดที่แสดงออกมาจาก Software
มีอีกชื่อว่า glass-box / structural testing
คือเราจะเห็นโครงสร้างของ Code ทั้งหมด และสร้าง test case ตามที่เห็น
C = #edges - #nodes + 2
C = #regions + 1
C คือควาซับซ้อนของ code ยิ่งซับซ้อนมาก ยิ่ง test ยาก C คือ lower bound number ของ independent path (ทางเดินที่มีอย่างน้อย 1 edge ที่ยังไม่เคยถูก traverse มาก่อน)
White-box ไม่สามารถดูว่ามันทำงานได้ตาม requirement หรือไม่
คือเราจะไม่เห็นโครงสร้างของ Code เลย และสร้าง test case ตาม requirement
การแบ่ง input / ouptut (ส่วนมากเป็น input) ออกเป็น set ของ equivalence class เช่น แบ่ง input เป็น 3 class ดังนี้ แล้วเลือกของมาแค่ 1 element ใน class มา test
EC: X < 0 (Invalid)
EC: X > 200 (Invalid)
EC: 0 <= X <= 200 (Valid)
เอาค่า "ริมขอบ" มาใช้เป็นตัว test ได้แก่ min-
, min
, min+
, max-
, max
, max+
เช่นช่วงที่ valid อยู่ที่ 0 <= x <= 200 จะได้ค่า test ดังนี้ min- = -1
, min = 0
, min+ = 1
, max- = 199
, max = 200
, max+ = 201
ประกอบไปด้วย 4 ส่วน
มี 2 แบบ
ปล. ควรจะมี error message ใน action ด้วย
Black-box ไม่สามารถดูว่ามีบางส่วนของ code ที่ยังไม่ได้ทดสอบหรือเปล่า
Verification คือกิจกรรมที่ทำให้มั่นใจว่า program ถูก implement แล้วถูกต้อง แต่ไม่รู้ว่าตรงตาม requirement ไหม
Validation คือกิจกรรมที่ทำให้มั่นใจว่า program เป็นไปตาม requirement
Verification -> build the product right
Validation -> build the right product
คือการทดสอบหน่วนที่เล็กที่สุดที่จะทดสอบได้ เช่น function / class โดยในการทดสอบจะมีส่วนที่เรียกว่า driver มาเป็นตัวใช้ทดสอบ unit ที่เราสนใจ หาก unit นี้มีการ import unit อื่นมาใช้ เราจะเรียก unit พวกนั้นว่า stub ซึ่งจะต้องทำการ mock ก่อน
คือการทดสอบเมื่อเอา unit หลาย ๆ อันมารวมกัน
มีหลายอย่างมาก แต่ในวิชานี้เราจะสนใจ
จะเป็นการ demonstrate ว่าทุกฟังก์ชันทำงานได้ตาม requirement ของระบบ
Req. No. | Req. Desc | Test Case ID | staus |
---|---|---|---|
1 | ชำระเงิน | 87,88,89 | P, F, P |
2 | จองเวลาเรียน | 81-88,102 |
คือจะทำเมื่อจะส่งมอบให้กับ Client แล้วโดยเอา test case จาก System Testing มาแสดงให้ Client เห็น ด้วยข้อมูลจริง ซึ่งจะต้องแสดงให้เห็นความสามารถของระบบ จะเสร็จสิ้นเมื่อลูกค้าพอใจ
ปล. test นี้จะทำเมื่อมี "ผู้ว่าจ้าง" (Facebook / Microsoft ไม่ต้องทำ)
ทำบนฝั่ง developer โดยมร interact กับ user
ทำบนฝั่ง consumer มี interact กับ user
คือการรัน test ใหม่ทั้งหมด เพื่อป้องกันว่า change ที่เกิดขึ้นจะไม่ไปพังของเก่าที่เคย test ผ่านไปแล้ว
Configuration ในที่นี้หมายถึงองค์ประกอบ
จะเป็นเรื่องเกี่ยวกับ "Change" โดย SCM activities จะมีดังนี้
SCM เกิดตั้งแต่วันแรกของ project จนถึงก่อนส่งมอบ SM เกิดหลังส่งมอบ
คือ Specification หรือ Work Product ที่เกิดขึ้นระหว่างที่เราทำระหว่าง process หรือตาม milestone และต้องถูก FTR (Formal Technical Review) ด้วย QA อย่างเป็นทางการ เห็นพ้องต้องกัน
แต่ Milestone != Baseline เพราะต้อง FTR ก่อน
Baseline != deliverable work product ขึ้นอยู่กับว่าจะส่งมอบหรือไม่
อะไรก็ตามท่ี่ถูกผลิดมาระหว่าง Software Engineering Process ที่เราต้อง Control เพราะอาจจะมี Change เกิดขึ้นได้
เช่น Project Plan, Requirement Spec, Source Code เป็นต้น
อะไรที่ไม่ใช่ SCIs เช่น Memo, Private Note
กระบวนการที่ทำให้ SCI กลายเป็น Baseline
Project Database คือถังที่คนอื่น ๆ สามารถเอาไปใช้วานต่อได้ ถ้าอยากเอามาแก้ไขต้อง extract Baseline ก่อน
SCI มีอยู่ 2 types ได้แก่
ต้องระบุ name, type, description และ identifier (version) Major.Minor.Patch
เป็นการตรวจสอบกระบวนการว่าเป็นไปตามขั้นตอนหรือไม่ ไม่ใช่อยู่ ๆ ก็ change เลย
เกิดหลังจากการส่งมอบ Software
Focus 4 อย่างใหญ่ ๆ
เพื่อควบคุมหรือเช็ค system แบบวันต่อวัน ซึ่งหากเกิดปัญหาจะต้องไปหาสาเหตุ และจากนั้นก็แก้ไข code เท่าที่จำเป็น โดยทั่วไปก็จะเป้นการแก้ไข เอาหน้ารอด ไปก่อนไม่ใช่ best fix เดี๋ยวระยะยาวจะไปแก้อีกที
ต้องทำให้เสร็จ
ถ้าเจอปัญหาแล้วต้องรื้ออะไรบางอย่าง ซึ่งกระทบหลาย ๆ จุด ซึ่งเป็นการ change เพื่อจัดการ error ให้เรียบร้อย (มองว่าเป็นการปรับปรุงก็ได้)
คือการแก้ไขเพื่อให้ system มีความสมบูรณ์มากขึ้นเพื่อให้ง่ายต่อ developer และดีต่อ user ในอนาคต เช่นการเพิ่ม log จะได้หา error ได้เร็วขึ้นในอนาคต
คือเรารู้ว่าแล้วว่าจุดนี้อาจจะเกิดบัคได้ในอนาคตเพราะ test ไม่ครบทุกกรณี เราเลยรีบตาม เก็บกวาด ให้เรียบร้อยก่อนเกิด bug ในอนาคต
ถ้าเป็นทีมเดิมที่ทำ system นี้ แม้ว่าจะคุ้นเคยกับ system แต่ก็อาจจะเกิดอาการ "มั่นใจมากเกินไป" ทำให้บางที้เราก็ไม่ค่อยคิดว่าจะเกิดบัค
คนที่เกี่ยวข้องทั้งหมด
โดยต้องมีคุณสมบัติดังนี้
จะแปลงจาก Diagram ไปเป็น Code
flowchart LR
Advertiser == 1-1 ==> Account
public class Advertiser {
private Account account; // consider here
public Advertiser() {
this.account = new Account();
}
}
flowchart LR
Advertiser <== 1-1 ==> Account
public class Advertiser {
private Account account; // consider here
public Advertiser() {
this.account = new Account(this);
}
}
public class Account {
private Advertiser advertiser; // consider here
public Account(Advertiser owner) {
this.advertiser = owner;
}
}
flowchart LR
Advertiser <== 1-* ==> Account
// One side
public class Advertiser {
private Set accounts; // consider here
public Advertiser() {
this.accounts = new HashSet();
}
public addAccount(Account a) {
a.setOwner(this);
this.accounts.add(a);
}
}
// Many Side
public class Account {
private Advertiser owner; // consider here
public Account(Advertiser owner) {
this.owner = owner;
}
public setOwner(Advertiser newOwner) {
if (newOwner == owner) return;
this.owner.removeAccount(this);
newOwner.addAccount(this);
this.owner = newOwner;
}
}
flowchart LR
Tournament <== * ordered -* ==> Player
public class Tournament {
private List players; // consider here, List for "ordered"
public Tournament() {
this.players = new ArrayList();
}
public addPlayer(Player p) {
if(player.contains(p)) return
player.add(p);
p.addTournament(this);
}
}
public class Player {
private Set tournaments; // consider here
public Account() {
this.tournaments = new HashSet();
}
public addTournament(Tournament t) {
if(tournaments.contains(t)) return
tournaments.add(t);
tournament.addPlayer(this);
}
}
Qualified association คือการทำให้ด้านที่เป็น Many ด้านหนึ่งกลายเป็น One
From
flowchart LR
League <== *-* ==> Player
To (Qualified Association on League)
flowchart LR
League <== nickname *-0..1 ==> Player
public class League {
private Map players; // consider here
public Tournament() {
this.players = new Map();
}
public addPlayer(String nickName, Player p) {
if(player.contains(p)) return
player.put(nickname, p); // consider here, put to Map, use nickName as key
p.addLeague(nickname, this);
}
}
public class Player {
private Map leagues; // consider here
public Account() {
this.leagues = new Map();
}
public addTournament(String nickName, League l) {
if(leagues.containsKey(l)) return
leagues.put(l, nickName); // consider here
l.addPlayer(nickName, this);
}
}
ที่เราเรียนจะเป็น SparxSystem
Application หมายถึง จุดเริ่มต้น
Resource หมายถึง ทรัพยากรใน Application นี้ที่ทำหน้าที่บริการอะไรสักอย่าง เช่น GET POST
Path หมายถึง Path name
Representation หมายถึง โครงสร้างข้อมูลที่ Resource อ้างถึง