防禦式拷貝
阿新 • • 發佈:2018-12-09
假設類的客戶端會盡其所能來破壞這個類的約束條件,因此你必須保護性的設計程式。
demo:
- import java.util.Date;
- public final class Period {
- private final Date start;
- private final Date end;
- public Period(Date start,Date end) {
- if(start.compareTo(end) > 0){
- throw
- }
- this.start = start;
- this
- }
- public Date start(){
- return start;
- }
- public Date end(){
- return end;
- }
- //remainder omitted
- }
[java] view plain copy
- Date start = new Date();
- Date end = new Date();
- Period period = new Period(start, end);
- end.setYear(78);
- System.out.println(period.end());
[java] view plain copy
- public Period(Date start,Date end) {
- this.start = new Date(start.getTime());
- this.end = new Date(end.getTime());
- if(this.start.compareTo(this.end) > 0){
- throw new IllegalArgumentException(this.start + " after " + this.end);
- }
- }
保護性拷貝是在檢查引數的有效性之前進行的,並且有效性檢查是針對拷貝之後的物件,而不是原始物件。
對於引數型別可以被不可信任方子類化的引數,請不要使用clone方法進行保護性拷貝。
通過改變Period:
[java] view plain copy
- Date start = new Date();
- Date end = new Date();
- Period period = new Period(start, end);
- period.end().setYear(98);
- System.out.println(period.end());
[java] view plain copy
- public Date end(){
- return new Date(end.getTime());
- }
但是這樣讓人寫起來很浮躁,所以還是要有一個必要性的把握。
引數的保護性拷貝不僅僅針對不可變類。每當編寫編寫方法和構造器時,如果他要允許客戶提供的物件進入到內部資料結構中,則有必要考慮一下,客戶提供的物件是否有可能是可變的,我是否能夠容忍這種可變性。特別是你用到list、map之類連線元素時。
在內部元件返回給客戶端的時候,也要考慮是否可以返回一個指向內部引用的資料。或者,不使用拷貝,你也可以返回一個不可變物件。如:Colletions.unmodifiableList(List<? extends T> list)
如果類具有從客戶端得到或者返回到客戶端的可變元件,類就必須保護性的拷貝這些元件。如果拷貝的成本受到限制,並且類信任他的客戶端不會進行修改,或者恰當的修改,那麼就需要在文件中指明客戶端呼叫者的責任(不的修改或者如何有效修改)。
特別是當你的可變元件的生命週期很長,或者會多層傳遞時,隱藏的問題往往暴漏出來就很可怕。