第五章 IFC 實例查找
5.2 查詢模式 Planning
本研究參考了 LINQ 和 Java Stream 的設計模式,引入了一種邏輯上相對簡單 的查詢模式,稱之為 Planning,其既可以實現按 ID 查找,按類別過濾等基本功能,
也可以實現按樓層劃分,按屬性劃分等較爲複雜功能。Planning 基於 Java 實現,
所以市面上任何 Java IDE 都可以對其提供優秀的支持。如:
for (Element elm : Planning ___________________.from(model)
___________________.select(Wall.class)
___________________.withProperty("ExtendToStructure")) { ____// do something with elm
}
表示選中模型中所有帶有 ExtendToStructure 屬性的牆,而:
26
for (Element elm : Planning ___________________.from(model)
___________________.select(Wall.class, Slab.class) ___________________.onStorey("b1")) {
____// do something with elm }
表示選中所有在“b1”樓的牆和樓板。兩個 Planning 亦可以鏈接起來,如:
Planning p1 = Planning.from(model).select(Wall.class);
Planning p2 = Planning.from(model).select(Slab.class);
Planning p3 = p1.or(p2);
表示在執行 p3 時,過濾條件是選擇牆或(or)板。
Planning 裏涉及到名稱查找時均使用了基於 Levenshtein Distance 的字串模糊 匹配的方式進行搜索,即 Planning 首先以全文匹配的方式查找元件,失敗後使用 模糊匹配的方式重新查找,這樣用戶不必嚴格輸入文本也可以獲得大概結果,如
“b1”、“b-1”、“B1”等輸入都可以指代名字為“b1”的地下一樓樓層。
Planning 本身是一個疊代器,其默認疊代模型里的所有實例,而用戶可以向其 中串聯過濾條件來縮小疊代範圍(交集),多個 Planning 可以并聯爲一個 Planning
(聯集),亦可以對單個 Planning 取反(差集)。Planning 的設計理念是鼓勵使用 者構建一個完整的 Planning(選擇邏輯),執行并得到自己想要的物件,然後對這 些物件操作。這樣用於獲得物件的程式碼和用於編輯物件的程式碼不會混在一起,
從視覺觀感和可維護性的角度來説更好。
27
所以說 Planning 的設計就是其内部提供的過濾條件的設計,簡單的條件比如 按 ID 過濾,按名稱過濾等不必贅述,對於非常複雜的過濾條件該如何設計是本研 究的討論重點:根據“複雜”的程度不同,有些過濾條件并不適合 Planning。比如:
// In one query expression but it is complex for (Element elm : Planning
___________________.from(model)
___________________.select(Wall.class)
___________________.withMinimumWindows(1)) { ____// do something with elm
}
// We think this is more expressive for (Element elm : Planning
___________________.from(model)
___________________.select(Wall.class)) { ____Wall wall = (Wall) elm;
____if (wall.getWindows().length > 0) { ________// this wall has windows inside ____}
}
上述兩者是針對“牆體內部是否開窗”來過濾的高級判別式的不同設計,本研究 傾向于後者的設計思路,因爲前者中的 withMinimumWindows 存在過度設計
(Overengineering)的問題,具體原因有兩點:
1)該函數存在兩個條件:最小值(Minimum)和窗體(Window),這樣勢 必意味著 Planning 需要設計 withMaximumWindows,考慮到墻體内還會存在門,
28
洞,管綫等構件,所以還必須設計 withMaximumDoors,withMaximumHoles……這 樣 Planning 的功能過於臃腫。
2)不僅是墻内可以存在窗體,板也可以(天窗),房間也可以(房間包含墻),
而事實上 IFC 并沒有規定窗必須存在于特定元件中。但沒有相關知識的一般使用 者會自然地認爲 withMinimumWindows 衹適用于墻,所以以下程式碼:
Planning.from(model).onStorey("b1").withMinimumWindows(1) // may returns Wall, Space, Door …
對他們來説會產生未預料結果(Unexpected Results),因為 Planning 可能返回了 牆以外的物件,而使用者並沒有實現對它們進行處理的程式碼。
綜上所述,本研究選擇了后一種設計方法,其它的過濾條件也都考慮了類似 的前提。截至成稿,Planning 中設計完成的過濾條件如下:
1)select:選擇單個或多個類別;
2)from:從模型中選擇;
3)withId:選擇具有特定 ID 的實例;
4)withGuid:選擇具有特定 GUID 的實例;
5)withGeometry:選擇具有幾何外觀的實例;
29
11)connectedTo:選擇與特定實例有連接的實例(如墻對墻,墻對門,墻對 空間等);
12)or:并聯其它 Planning 過濾條件。如果兩個 Planning 均 from 同一個 IFC 模型,第二個可以省略 from:
Planning p1 = Planning.from(model).select(…);
Planning p2 = Planning.onStorey(…); // also from model Planning p3 = p1.or(p2);
若兩個 Planning 各自 from 不同的模型,則 or 會執行兩個 Planning 的疊代器得到兩 個結果,并合并成一個列表:
Planning p1 = Planning.from(model_1).select(…);
Planning p2 = Planning.from(model_2).select(…);
Planning p3 = p1.or(p2);
// equivalent to:
List<Element> p3 = p1.toList().addAll(p2.toList());
此時 p3 已經失去疊代器的意義,退化成一個列表。
30