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

演化架構與緊急設計(二):語言表達性與設計模式——以 Groovy/JRuby 重構 GoF 修飾器與適配器

     編輯:Java 相關知識

表達性對緊急設計的決定性影響

意圖與實現的鴻溝導致樣板代碼掩蓋設計意圖。表達性強的語言(Groovy、Ruby、Scala、Clojure)讓開發者聚焦本質,更易發現慣用模式。本文用 JVM 動態語言重構 GoF 修飾器與適配器模式,展示表達性如何改變設計實現。

修飾器模式

傳統 Groovy 實現(類結構)

class Logger { def log(msg) { println msg } }
class TimeStampingLogger extends Logger { ... }
class UpperLogger extends Logger { ... }
def logger = new UpperLogger(new TimeStampingLogger(new Logger()))
logger.log("Groovy Rocks") // 輸出帶時間戳的大寫

開放類 + 實例級元編程:就地修飾

Groovy ExpandoMetaClass:

def logger = new Logger()
logger.metaClass.log = { String m -> println m.toUpperCase() }
logger.log("upper case") // 僅影響該實例

JRuby eigenclass:

l = Logger.new
def l.log(m); puts m.upcase; end
l.log("upper case")

無需額外類,修飾邏輯內聚在使用點,閱讀即理解。

擴展內置類:為 ArrayList 添加 first()/last()

Groovy:

ArrayList.metaClass.getFirst { delegate.size>0 ? delegate.get(0) : null }
ArrayList.metaClass.getLast { delegate.size>0 ? delegate.get(delegate.size-1) : null }

JRuby:

class ArrayList
  def first; size!=0 ? get(0) : nil; end
  def last; size!=0 ? get(size-1) : nil; end
end

Java 做不到不修改源碼或字節碼織入。

通用修飾器:攔截所有方法調用

Groovy invokeMethod 鉤子:

class GenericLowerDecorator {
    private delegate
    def invokeMethod(String name, args) {
        def newArgs = args.collect { it instanceof String ? it.toLowerCase() : it }
        delegate.invokeMethod(name, newArgs)
    }
}
logger = new GenericLowerDecorator(new TimeStampingLogger(new Logger()))
logger.log('IMPORTANT Message') // 參數變小寫,時間戳不變

Java 需 AspectJ 等 AOP 框架才能達到同等效果。

適配器模式

Groovy 內聯適配器:Map + as 操作符

interface RoundThing { def getRadius() }
class SquarePeg { def width }
class RoundHole { def radius; def pegFits(peg) { peg.radius <= radius } }

def adapter = { p -> [getRadius: { Math.sqrt(((p.width/2)**2)*2) }] as RoundThing }
def hole = new RoundHole(radius:4.0)
(4..7).each { w ->
    def peg = new SquarePeg(width:w)
    assert (w<6) == hole.pegFits(adapter(peg))
}

as 將 Map 動態生成實現介面的代理類,方法查找 Map 鍵值執行閉包。

JRuby 開放類適配器:直接注入方法


class SquarePeg
  def radius; Math.sqrt(((@width/2)**2)*2); end
end
hole = RoundHole.new(4.0)
4.upto(7) { |i| peg = SquarePeg.new(i); assert (i<6) == hole.peg_fits?(peg) }

若 SquarePeg 已有 radius 會衝突。

JRuby 介面切換:同方法名不同語義共存


class SquarePeg
  include InterfaceSwitching
  def radius; @width; end
  def_interface :square, :radius
  def radius; Math.sqrt(((@width/2)**2)*2); end
  def_interface :holes, :radius
  def initialize(w); set_interface :square; @width=w; end
end
peg = SquarePeg.new(5)
peg.with_interface(:holes) { hole.peg_fits?(peg) } // 作用域內使用 holes 版本 radius

利用 Ruby 解釋期執行特性,為方法建立多個版本並通過作用域切換。Java 編譯期無法實現。

結論

  • 表達性 = 能力。動態語言讓修飾器、適配器等模式化為語言內建機制,消除樣板代碼。
  • 慣用模式發現依賴於語言表達性:若提煉模式成本過高(需大量樣板),團隊將背負技術債。
  • Groovy AST Transformations 嘗試將編譯期元編程引入,縮小與動態語言差距。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved