มาเขียน รถไฟ ฉึกฉัก แบบง่ายๆใน JAVA กัน
A simple use case
สมมติว่าเรามี flow ที่ต้องการ update user ประมาณนี้
ในแต่ละ step ถ้ามี error เราต้องการที่จะ handle ด้วยว่า
ถ้า task ไหน fail อยากให้มัน return message error ของ task นั้นๆ และจบการทำงาน
เราก็จะได้ code ออกมาง่ายๆ ประมาณนี้
public String onSubmit(UserRequest userRequest) {
var isRequestInvalid = validateRequest(userRequest);
if (isRequestInvalid) {
return "Invalid request";
}
var isUpdateUserFail = updateUser(userRequest);
if (isUpdateUserFail) {
return "Update user fail";
}
var isSendNotificationFail = sendNotification(userRequest);
if (isSendNotificationFail) {
return "Send notification fail";
}
var isSendMailFail = sendMail(userRequest);
if (isSendMailFail) {
return "Send mail fail";
}
return "Success";
}
Code ก็ดูตรงไปตรงมานะ แต่ว่าถ้าเรามี function ที่ต้อง handle เยอะๆละ ?
if — return มันก็จะยาวไปเรื่อยๆเลย
จะดีกว่าไหม ? ถ้าเราทำให้มันอ่านง่ายเหมือน diagram ที่เราวาดไว้ แบบนี้
public String onSubmit(UserRequest user) {
return validateRequest(user)
.flatMap(this::updateUser)
.flatMap(this::sendNotification)
.flatMap(this::sendMail)
.fold(
error -> error,
success -> "Success"
);
}
public Either<String, UserRequest> validateRequest(UserRequest user) {
if (user.getUserId() == null) {
return Either.left("User ID is required");
}
if (user.getName() == null) {
return Either.left("Name is required");
}
return Either.right(user);
}
public Either<String, UserRequest> updateUser(UserRequest user) {
try {
boolean userNotFound = db.updateUser(user);
if (userNotFound) {
return Either.left("User not found");
}
return Either.right(user);
} catch (Exception e) {
return Either.left("Error updating user");
}
}
สิ่งนี้เราเรียกมันว่า Railway Oriented Programming (ROP)
เป็นวิธีจัดการ error โดยที่ ในแต่ละ function จะรับ parameter type อะไรก็ได้
แต่มันจะ return ออกมาเป็น Failure หรือ Success เสมอ
ถ้าใน Function เราอยากจะ return ออกมาเป็น 2 Type(Failure, Success) เราก็ต้องมี Wrapper สักตัว โดยในตัวอย่างนี้ เราใช้สิ่งที่เรียกว่า Either Monad ซึ่งเจ้า Either เขามี concept ว่า Either-Left หมายถึง Failure และ Either-Right หมายถึง Success และเราก็สามารถทำ Wrapper เองได้(ดูจากได้จาก Ref: 1 ครับ) แต่ในตัวอย่างนี้ ผมใช้ library ตัวนึงที่ชื่อว่า vavr (เวเวอะ Java กลับหัว) มาช่วยให้เราทำ Either ได้ง่ายขึ้นครับ
ถ้าชอบ…ลองเอาไปใช้แทน if — return กันดูนะครับ
ปล.
ฟังพี่รูฟอธิบายเพิ่มเติมละเอียดๆที่นี่เลยครับ
Ref:
Functional programming with handling errors
Railway Oriented Programming
Functional Programming in Python Part 4: Monad !!!!!