มาเขียน รถไฟ ฉึกฉัก แบบง่ายๆใน JAVA กัน

O'leang Sothon
odds.team
Published in
2 min readMay 20, 2024

A simple use case
สมมติว่าเรามี flow ที่ต้องการ update user ประมาณนี้

A simple flow

ในแต่ละ 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 เสมอ

Railway Oriented Programming (ROP)

ถ้าใน 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 !!!!!

--

--