首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

演化架構與緊急設計(二):捕捉慣用模式——從 API 封裝到元程式設計

     編輯:Java 相關知識

系列前文已闡述「設計即代碼」與「表達性決定可發現性」。本文聚焦:發現慣用模式後,如何將其提取、固化、複用?介紹兩大主流路徑:API 框架化與元程式設計(Annotation/屬性)。

路徑一:將模式封裝為 API / 框架

最直觀的複用方式——將重複結構抽取為可呼叫的 API。開源框架本質上皆是慣用模式集:Spring 封裝 DI/IoC 模式,Hibernate 封裝 ORM 模式,Web 框架封裝 MVC 模式。

結構重複問題與匿名內部類緩解

直接使用 API 常導致「主機對象」到處傳遞,代碼噪音大:

// 噪音版
Car car = new CarImpl();
MarketingDescription desc = new MarketingDescriptionImpl();
desc.setType("Box");
desc.setSubType("Insulated");
desc.setAttribute("length", "50.5");
car.setDescription(desc);

Java 匿名內部類實例初始化器可「攜帶」主機對象:

// 較靜默版
MarketingDescription desc = new MarketingDescriptionImpl() {{
    setType("Box");
    setSubType("Insulated");
    setAttribute("length", "50.5");
}};

原理:雙大括號建立匿名子類,內部 `{ ... }` 為實例初始化塊,在構造函數前執行。代價是產生額外類文件、語法陌生易誤讀。

API 提取的負面效應

  • 模式代碼與業務代碼外觀一致,難以區分「框架意圖」與「具體實現」
  • 接手者易誤改框架核心邏輯,破壞模式約束
  • 缺乏聲明式語義,意圖隱藏在過程式調用中

路徑二:元程式設計——讓模式「看起來不一樣」

使用「關於代碼的代碼」表達模式,使模式在視覺與語義上脫穎而出。Java 主要手段:Annotation(註解)。

案例:驗證模式的聲明式捕捉

// 領域對象
public class Country {
    private List regions = new ArrayList<>();
    private String name;

    @MaxLength(length = 10)
    public String getName() { return name; }

    @Unique(scope = Country.class)
    public String getCode() { return code; }
    // ...
}

註解定義

@Retention(RetentionPolicy.RUNTIME)
public @interface MaxLength { int length() default 0; }

@Retention(RetentionPolicy.RUNTIME)
public @interface Unique { Class scope() default Unique.class; }

驗證引擎實現(模板方法模式)

// 抽象驗證器
public abstract class Validator {
    public void validate(Object obj) throws ValidationException {
        for (Method m : obj.getClass().getMethods()) {
            if (m.isAnnotationPresent(getAnnotationType())) {
                validateMethod(obj, m, m.getAnnotation(getAnnotationType()));
            }
        }
    }
    protected abstract Class getAnnotationType();
    protected abstract void validateMethod(Object obj, Method m, Annotation ann);
}

// MaxLength 具體實現
public class MaxLengthValidator extends Validator {
    @Override protected Class getAnnotationType() { return MaxLength.class; }
    
    @Override protected void validateMethod(Object obj, Method m, Annotation ann) {
        if (m.getName().startsWith("get")) {
            MaxLength ml = (MaxLength) ann;
            String val = (String) m.invoke(obj);
            if (val != null && val.length() > ml.length()) {
                throw new ValidationException(m.getName() + " exceeds " + ml.length());
            }
        }
    }
}

// Unique 具體實現(跨對象唯一性)
public class UniqueValidator extends Validator {
    @Override protected Class getAnnotationType() { return Unique.class; }
    
    @Override protected void validateMethod(Object obj, Method m, Annotation ann) {
        Unique u = (Unique) ann;
        try {
            // 透過 scope 導航取得父對象集合
            Method scopeGetter = obj.getClass().getMethod("get" + u.scope().getSimpleName());
            Object scopeObj = scopeGetter.invoke(obj);
            Method collGetter = scopeObj.getClass().getMethod("get" + obj.getClass().getSimpleName() + "s");
            List siblings = (List) collGetter.invoke(scopeObj);
            
            Object myVal = m.invoke(obj);
            for (Object sib : siblings) {
                if (sib != obj && m.invoke(sib).equals(myVal)) {
                    throw new ValidationException(m.getName() + " not unique in scope");
                }
            }
        } catch (Exception e) { throw new ValidationException(e); }
    }
}

元程式設計優勢

  • 意圖顯性化:`@MaxLength(10)` 瞬間傳達約束,無需閱讀實現
  • 關注點分離:驗證邏輯集中在 Validator 體系,領域對象純淨
  • 擴充性:新增 `@Email`、`@PastDate` 僅需新增註解 + Validator,零侵入
  • 工具友好:IDE 可識別註解提供提示、靜態分析、代碼生成

JVM 替代語言的更強表達力:JRuby 的 Sticky Annotation

Ruby 的「屬性」實為模組混入方法,支援作用域感知與粘性狀態:

# Ruby 測試類
class TestCalculator < Test::Unit::TestCase
  extend TestDirectives
  
  acceptance_only  # 粘性屬性:影響後續所有方法定義
  def test_complex_calculation
    assert_equal(4, Calculator.new.complex_calculation)
  end
end

# 實現模組
module TestDirectives
  def acceptance_only
    @acceptance_build = (ENV['BUILD'] == 'ACCEPTANCE')
  end
  
  def method_added(name)
    remove_method(name) unless @acceptance_build
    @acceptance_build = false  # 重置粘性狀態
  end
end

關鍵差異:Ruby 在解析階段執行 `method_added` hook,動態決定方法是否保留。Java 註解僅在運行時透過反射生效。Ruby 甚至用 Sticky Annotation 實現 `private`/`public` 等作用域修飾符——它們本質是關鍵字,在 Ruby 中只是普通方法。

總結:兩條路徑的選擇策略

場景推薦路徑理由
團隊以 Java 為主、無元程式經驗 API 框架化 認知負擔低、IDE 支援完善
橫切關注點多(驗證、安全、事務、審計) Annotation 元程式 聲明式、集中化、零業務侵入
需 DSL 風格、業務專家可讀 JRuby/Groovy 內部 DSL 語法可定製、可執行文檔
現有框架已提供擴展點(如 Spring AOP、Hibernate Interceptor) 框架原生擴展 避免重複造輪子、利用容器生命週期

下一期將探討:領域特定語言(DSL)作為慣用模式的終極捕捉形式——從外部 DSL(ANTLR)到內部 DSL(Groovy/Ruby),如何讓代碼直接「說業務語言」。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved