Java和Ceylon對象的結構和驗證。本站提示廣大學習愛好者:(Java和Ceylon對象的結構和驗證)文章只能為提供參考,不一定能成為您想要的結果。以下是Java和Ceylon對象的結構和驗證正文
當變換Java代碼為Ceylon代碼時,有時分我會遇到一些Java類結構器混雜了驗證與初始化的情形。讓我們運用一個復雜但是人為的代碼例子來闡明我想論述的意思。
一些壞代碼
思索上面的Java類。(伙計,不要在家裡寫這樣的代碼)
public class Period { private final Date startDate; private final Date endDate; //returns null if the given String //does not represent a valid Date private Date parseDate(String date) { ... } public Period(String start, String end) { startDate = parseDate(start); endDate = parseDate(end); } public boolean isValid() { return startDate!=null && endDate!=null; } public Date getStartDate() { if (startDate==null) throw new IllegalStateException(); return startDate; } public Date getEndDate() { if (endDate==null) throw new IllegalStateException(); return endDate; } }
嘿,我之前曾經正告過,它是人為的。但是,在實踐Java代碼中找個像這樣的東西實踐上並非不罕見。
這裡的問題在於,即便輸出參數(在隱藏的parseDate()辦法中)的驗證失敗了,我們還是會取得一個Period的實例。但是我們獲取的那個Period不是一個“無效的”形態。嚴厲地說,我的意思是什麼呢?
好吧,假設一個對象不能有意義地呼應公用操作時,我會說它處於一個非無效形態。在這個例子裡,getStartDate() 和getEndDate()會拋出一個IllegalStateException異常,這就是我以為不是“有意義的”一種狀況。
從另外一方面來看這個例子,在設計Period時,我們這兒呈現了類型平安的失敗。未反省的異常代表了類型零碎中的一個“破綻”。因而,一個更好的Period的類型平安的設計,會是一個不運用未反省的異常—在這個例子中意味著不拋出IllegalStateException異常。
(實踐上,在真實代碼中,我更有能夠遇到一個getStartDate() 辦法它不反省null ,在這個代碼行之後就會招致一個NullPointerException異常,這就愈加蹩腳了。)
我們可以很容易地轉換下面的Period類成為Ceylon方式的類:
shared class Period(String start, String end) { //returns null if the given String //does not represent a valid Date Date? parseDate(String date) => ... ; value maybeStartDate = parseDate(start); value maybeEndDate = parseDate(end); shared Boolean valid => maybeStartDate exists && maybeEndDate exists; shared Date startDate { assert (exists maybeStartDate); return maybeStartDate; } shared Date endDate { assert (exists maybeEndDate); return maybeEndDate; } }
當然了,這段代碼也會遇到與原始Java代碼異樣的問題。兩個assert符號沖著我們大喊,在代碼的類型平安中有一個問題。
使Java代碼變得更好
Java裡我們怎樣改良這段代碼呢?好吧,這兒就是一個例子關於Java飽受诟病的已反省異常會是一個十分合理的處理辦法!我們可以略微修正下Period來從它的結構器中拋出一個已反省的異常:
public class Period { private final Date startDate; private final Date endDate; //throws if the given String //does not represent a valid Date private Date parseDate(String date) throws DateFormatException { ... } public Period(String start, String end) throws DateFormatException { startDate = parseDate(start); endDate = parseDate(end); } public Date getStartDate() { return startDate; } public Date getEndDate() { return endDate; } }
如今,運用這個處理方案,我們就不會獲取一個處於非無效形態的Period,實例化Period的代碼會由編譯器擔任去處置有效輸出的情形,它會捕捉一個DateFormatException異常。
try { Period p = new Period(start, end); ... } catch (DateFormatException dfe) { ... }
這是一個對已反省異常不錯的、完滿的、正確的運用,不幸的是我簡直很少看到Java代碼像下面這樣運用已反省異常。
使Ceylon代碼變得更好
那麼Ceylon怎樣樣呢?Ceylon沒有已反省異常,因此我們需求尋覓一個不同的處理方式。典型地,在Java調用一個函數會拋出一個已反省異常的情形中,Ceylon會調用函數前往一個結合類型。由於,一個類的初始化不前往除了類自己外的任何類型,我們需求提取一些混合的初始化/驗證的邏輯來使其成為一個工廠函數。
//returns DateFormatError if the given //String does not represent a valid Date Date|DateFormatError parseDate(String date) => ... ; shared Period|DateFormatError parsePeriod (String start, String end) { value startDate = parseDate(start); if (is DateFormatError startDate) { return startDate; } value endDate = parseDate(end); if (is DateFormatError endDate) { return endDate; } return Period(startDate, endDate); } shared class Period(startDate, endDate) { shared Date startDate; shared Date endDate; }
依據類型零碎,調用者有義務去處置DateFormatError:
value p = parsePeriod(start, end); if (is DateFormatError p) { ... } else { ... }
或許,假如我們不關懷給定日期格式的實踐問題(這是有能夠的,假定我們任務的初始化代碼喪失了那個信息),我們可以運用Null而不是DateFormatError:
//returns null if the given String //does not represent a valid Date Date? parseDate(String date) => ... ; shared Period? parsePeriod(String start, String end) => if (exists startDate = parseDate(start), exists endDate = parseDate(end)) then Period(startDate, endDate) else null; shared class Period(startDate, endDate) { shared Date startDate; shared Date endDate; }
至多可以說,運用工廠函數的辦法是優秀的,由於通常來說在驗證邏輯和對象初始化之間它具有更好的隔離。這點在Ceylon中特別有用,在Ceylon中,編譯器在對象初始化邏輯中添加了一些十分嚴峻的限制,以保證對象的一切范疇僅被賦值一次。
以上就是本文的全部內容,希望對大家的學習有所協助,也希望大家多多支持。