if ("1".equals(subjectCode)) {
TestM m = new TestM();
m.setTeachingCode(teachingVersionCode);
m.setGradeCode(gradeCode);
m.setTermCode(Integer.valueOf(termCode));
m.setQuesTestTypeCode(Integer.valueOf(testTypeCode));
List list = testService.getTestMList(m, pageNo);
int count = testService.getPaperMCount(m);
}
if ("2".equals(subjectCode)) { ... TestE ... }
if ("3".equals(subjectCode)) { ... TestC ... }
每新增一科目就要複製一大塊,違反 DRY 原則,維護極其痛苦。
// 1. 抽象基類封裝共同屬性與構造
public abstract class BaseTest {
protected String teachingCode;
protected String gradeCode;
protected Integer termCode;
protected Integer testTypeCode;
public BaseTest(String teachingCode, String gradeCode, String termCode, String testTypeCode) {
this.teachingCode = teachingCode;
this.gradeCode = gradeCode;
this.termCode = Integer.valueOf(termCode);
this.testTypeCode = Integer.valueOf(testTypeCode);
}
// 共同 getter/setter...
}
// 2. 子類只需聲明差異屬性
public class TestM extends BaseTest {
private String mathSpecificField;
public TestM(String tc, String gc, String term, String type) { super(tc, gc, term, type); }
}
// 3. Service 統一接口
public interface TestService {
List getList(T param, int pageNo);
int getCount(T param);
}
// 4. 策略工廠路由
@Component
public class TestFactory {
private final Map> services = new HashMap<>();
@Autowired public TestFactory(TestMService m, TestEService e, TestCService c) {
services.put("1", m); services.put("2", e); services.put("3", c);
}
public TestService extends BaseTest> getService(String subjectCode) {
return services.get(subjectCode);
}
}
// 5. 調用端極簡
TestService extends BaseTest> svc = factory.getService(subjectCode);
BaseTest param = new TestM(tc, gc, term, type); // 或反射/構造器映射
List extends BaseTest> list = svc.getList(param, pageNo);
int count = svc.getCount(param);
// 通用構建參數對象
JSONObject json = new JSONObject();
json.put("teachingCode", teachingVersionCode);
json.put("gradeCode", gradeCode);
json.put("termCode", termCode);
json.put("testTypeCode", testTypeCode);
// 根據 subjectCode 確定目標類
Class extends BaseTest> clazz = Map.of(
"1", TestM.class, "2", TestE.class, "3", TestC.class
).get(subjectCode);
BaseTest param = JSONObject.toBean(json, clazz); // 自動 set 同名屬性
public class TestParam {
private String teachingCode, gradeCode;
private Integer termCode, testTypeCode;
// ... 差異字段用 Map 擴展
private Map ext = new HashMap<>();
private TestParam(Builder b) { this.teachingCode = b.teachingCode; ... this.ext = b.ext; }
public static class Builder {
// 必填
private String teachingCode, gradeCode;
// 選填
private Integer termCode, testTypeCode;
private Map ext = new HashMap<>();
public Builder teachingCode(String v) { this.teachingCode = v; return this; }
public Builder ext(String k, Object v) { ext.put(k, v); return this; }
public TestParam build() { return new TestParam(this); }
}
}
// 使用
TestParam param = new TestParam.Builder()
.teachingCode(tc).gradeCode(gc)
.termCode(Integer.valueOf(termCode))
.testTypeCode(Integer.valueOf(testTypeCode))
.ext("mathField", "xxx") // 科目特有
.build();
| 場景 | 推薦模式 |
|---|---|
| 層次穩定、共同屬性多、行為多態 | 模式一:父類 + 策略工廠 |
| 屬性名高度一致、臨時 DTO、無行為 | 模式二:JSON 反序列化 |
| 構建參數複雜、可選項多、鏈式調用 | 模式三:Builder + Map 擴展 |
核心思想:**將「不變的共同部分」上移到父類/基類/通用構建器,「變化的差異部分」通過繼承、反射、Map 擴展隔離**。消除 if-else 鏈,新增科目只需新增實現類並註冊到工廠,符合開閉原則。