界面設計本應充滿創造性,實際卻常陷入「坐標地獄」:設計師腦中清晰的「按比例縮放」、「居中留白」等高層意圖,被迫降級為像素級坐標與尺寸。微調佈局往往引發連鎖修改,代碼脆弱難維護。可視化拖拽工具與傳統佈局管理器(如 GridBagLayout)均未根治:前者仍將語義降級為組件堆疊,後者擴展性差、描述能力弱。
不將設計概念降級至實現語言,而是**提供一種直接描述高層佈局意圖的語言**——佈局 DSL。設計師用代碼直接表達「並排、上下、居中、網格、留白」等領域詞彙,構建可復用、可組合的佈局樣式庫,實現「所想即所得」。
at(x,y,w,h) 定位、in(Container) 掛載、title() 等屬性鏈式調用(Fluent Interface)。// 居中佈局:水平/垂直留白比例
Component center(Component cp, float hRatio, float vRatio) {
float s1 = (1-2*hRatio)/(1-hRatio);
float s2 = (1-2*vRatio)/(1-vRatio);
Component u = above(Empty(), above(cp, Empty(), s2), vRatio);
return beside(Empty(), beside(u, Empty(), s1), hRatio);
}
// 水平/垂直序列
Component h_seq(Component[] cps) { ... } // 遞歸 beside
Component v_seq(Component[] cps) { ... } // 遞歸 above
// N行M列網格
Component block(Component[] cps, int N, int M) { ... }
// 帶留白的網格
Component block_with_margin(Component[] cps, int N, int M, float hR, float vR) {
Component[] ncps = Arrays.stream(cps).map(c -> center(c, hR, vR)).toArray(Component[]::new);
return block(ncps, N, M);
}
Component[] keys = { Button("0"), Button("1"), ..., Button("/") };
Component opLayout = block(keys, 4, 4); // 原版
Component opLayout = block_with_margin(keys, 4, 4, 0.02, 0.02); // 加留白,僅改一行
above(above(TextField(), beside(Button("Backspace"), Button("C"), 0.5), 0.5),
opLayout, 0.3).at(0,0,300,200).in(frame);
**佈局代碼即佈局規格說明**,修改佈局意圖僅需局部變更,實現「問題域微小變更 → 代碼微小變更」的健壯性。
JButton、setBounds 等)。Component、beside、above、Empty)。center、h_seq、block 等領域詞彙)。每層以對象組合構建上層原語,而非傳統繼承體系,具備更高抽象度與通用性。
// Fluent Interface:每個方法返回 this 支持鏈式調用
public class Button implements Component {
JButton btn = new JButton();
public Component title(String t) { btn.setText(t); return this; }
public Component at(int x,int y,int w,int h) { btn.setBounds(x,y,w,h); return this; }
public Component in(Container p) { p.add(btn); return this; }
}
// 組合子閉包:實現 Component 接口,內部委託子組件
public class beside implements Component {
Component left, right; float ratio;
public Component at(int x,int y,int w,int h) {
left.at(x, y, (int)(w*ratio), h);
right.at(x+(int)(w*ratio), y, (int)(w*(1-ratio)), h);
return this;
}
public Component in(Container p) { left.in(p); right.in(p); return this; }
}
為連接後端邏輯,引入「擁有者」概念:Button().title("btn").ownby(controller)。所有事件處理在 Controller 中完成,物理包含關係(Container)與編程語義關係(Owner)分離,徹底隔離 View 與 Presenter。
此設計思想可遷移至數據可視化、報表引擎、工作流建模等領域——**為問題域打造專屬語言,而非在通用語言中硬拼領域模型**。