面向對象不僅是語法糖,更是將業務複雜度轉化為可維護代碼結構的思維工具。本文以房貸業務為例,演示從需求分析到領域模型構建、服務層設計的完整 OO 建模過程,展示封裝、繼承、多態在實戰中的應用。
| 領域對象 | 核心屬性 | 業務含義 |
|---|---|---|
| Customer | username, money | 借款人,持有可用資金 |
| House | num, price, rate, years | 抵押物,含價格、利率、年限 |
| Credit | customer, houses[], backTime, toPay(Map<House,Double>) | 貸款合約,關聯客戶、房產、每月應還金額映射 |
| Agreement | credit, isContinue, amercement | 法律合同,狀態機:正常/壞賬/終止 |
public class Customer {
private String username;
private double money;
// getter/setter 省略
}
public class House {
private String num; // 房號 如 "3#116"
private double price; // 總價
private double rate; // 月利率
private int years; // 貸款年限
// getter/setter 省略
}
public class Credit {
private Customer customer;
private List<House> houses;
private int backTime; // 還款日
private Map<House, Double> toPay; // 每套房每月應還額
// getter/setter 省略
}
public class Agreement {
private Credit credit;
private boolean isContinue = true; // 合同狀態
private double amercement; // 累計滯納金
// getter/setter 省略
}
public interface BankService {
Credit buyHouses(Customer customer, List<House> houses);
Agreement signAgreement(Customer customer, Credit credit);
double repay(int period, Agreement agreement, double amount, Customer customer);
Map<House, Double> adjustRate(House house, double newRate);
Map<House, Double> adjustTerm(House house, int newYears);
}
public class BankServiceImpl implements BankService {
@Override
public Credit buyHouses(Customer customer, List<House> houses) {
Credit credit = new Credit();
credit.setCustomer(customer);
credit.setHouses(houses);
credit.setBackTime(1); // 默認每月 1 號
// 計算每套房月供(等額本息簡化公式)
Map<House, Double> toPay = new HashMap<>();
for (House h : houses) {
double monthlyRate = h.getRate();
int months = h.getYears() * 12;
double principal = h.getPrice();
// 等額本息:P * r * (1+r)^n / ((1+r)^n - 1)
double monthly = principal * monthlyRate * Math.pow(1+monthlyRate, months)
/ (Math.pow(1+monthlyRate, months) - 1);
toPay.put(h, monthly);
}
credit.setToPay(toPay);
return credit;
}
@Override
public Agreement signAgreement(Customer customer, Credit credit) {
Agreement ag = new Agreement();
ag.setCredit(credit);
return ag;
}
@Override
public double repay(int period, Agreement agreement, double amount, Customer customer) {
// 簡化:按 toPay 比例分配還款,計算滯納金
double totalDue = agreement.getCredit().getToPay().values().stream().mapToDouble(d -> d).sum();
if (amount < totalDue) {
agreement.setAmercement(agreement.getAmercement() + (totalDue - amount) * 0.01); // 1% 滯納金
}
customer.setMoney(customer.getMoney() - amount);
return agreement.getAmercement();
}
@Override
public Map<House, Double> adjustRate(House house, double newRate) {
house.setRate(newRate);
return recalculate(house); // 重算月供
}
@Override
public Map<House, Double> adjustTerm(House house, int newYears) {
house.setYears(newYears);
return recalculate(house);
}
private Map<House, Double> recalculate(House h) {
Map<House, Double> m = new HashMap<>();
// 同 buyHouses 計算邏輯
return m;
}
}
public class BankApp {
public static void main(String[] args) {
Customer customer = new Customer();
customer.setUsername("董林美美");
customer.setMoney(1000000);
List<House> houses = Arrays.asList(
new House("3#116", 1500000, 0.004, 20),
new House("4#112", 1100000, 0.0035, 30),
new House("1#001", 2000000, 0.003, 10)
);
BankService bs = new BankServiceImpl();
Credit credit = bs.buyHouses(customer, houses);
Agreement agreement = bs.signAgreement(customer, credit);
if (!agreement.isContinue()) return;
// 模擬第 1 期還款
double amercement = bs.repay(1, agreement, 111111, customer);
// 檢查壞賬:滯納金超過應還額 1/3 視為壞賬
for (Map.Entry<House, Double> e : credit.getToPay().entrySet()) {
if (amercement >= e.getValue() / 3) {
agreement.setContinue(false);
System.out.println(customer.getUsername() + " 賬戶壞賬,合同終止");
break;
}
}
// 利率/期限調整演示
bs.adjustRate(houses.get(2), 0.005);
bs.adjustTerm(houses.get(2), 15);
// 輸出每月還款明細
for (Map.Entry<House, Double> e : credit.getToPay().entrySet()) {
System.out.printf("%s 房子 %s 每月還款: %.2f%n",
customer.getUsername(), e.getKey().getNum(), e.getValue());
}
}
}
adjustRate/adjustTerm 可替換為 RateStrategy 介面,支持等額本息/等額本金/先息後本多種算法熱插拔。LoanTermsChangedEvent,通知風控、通知、報表等下游系統,避免服務層膨脹。isContinue 可擴展為枚舉 NORMAL, OVERDUE, BAD_DEBT, SETTLED,配合狀態模式處理狀態轉換邏輯。Money、InterestRate、Period 封裝驗證邏輯,消除基本類型痴迷。| 層 | 職責 | 本例對應 |
|---|---|---|
| 表現層 | HTTP/控制台交互 | BankApp.main |
| 應用層 | 用例編排、事務邊界 | BankService 介面 |
| 領域層 | 核心業務規則、不變式 | Customer, House, Credit, Agreement |
| 基礎設施層 | 持久化、外部服務 | (未實現)JPA Repository |
這個例子雖然簡化,但展示了 OO 建模的核心價值:用領域語言命名類與方法,將業務規則封裝在領域對象中,服務層只編排不包含業務判斷。隨著業務演進,可平滑引入聚合根、領域事件、CQRS 等進階模式,保持架構清晰可擴展。