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

冒號和他的學生們(連載 25)——軟件應變:抽象與封裝如何應對變化

     編輯:Java 相關知識

第 25 課以「將所有 private 改為 public 程序還能工作嗎?」為切入點,揭示信息隱藏的本質非安全而是應對變化。通過復數類「直角坐標 vs 極坐標」兩種實現的對比,展示抽象接口屏蔽結構性變化;引入橋梁模式徹底解耦接口與實現;並對比 Java 無頭文件優勢與 C++ 頭文件帶來的重編譯負擔。

核心論點

  • 軟件之「軟」:變化與固化之別。維護時間超過編寫時間,難點在邏輯複雜與需求變化。
  • 抽象與封裝:抽象提煉不變本質(接口),封裝隱藏易變實現(結構)。陰陽兩面,變化發生在陰面,對外屏蔽。
  • 信息隱藏 ≠ 安全:是為了抗變能力,非防止惡意訪問。
  • 兩類變化:結構性重構(內在需求)與功能性擴展(外在需求)。理想抽象完全隔離前者,最大程度降低後者副作用。

經典案例:復數類的兩種實現

直角坐標實現

public class Complex {
    private double x, y; // 實部、虛部
    public Complex(double x, double y) { this.x = x; this.y = y; }
    public double real() { return x; }
    public double imaginary() { return y; }
    public double modulus() { return Math.hypot(x, y); }
    public double argument() { return Math.atan2(y, x); }
    public Complex add(Complex o) { return new Complex(x + o.x, y + o.y); }
    public Complex multiply(Complex o) {
        return new Complex(x * o.x - y * o.y, x * o.y + y * o.x);
    }
}

極坐標實現

public class Complex {
    private double r, theta; // 模、輻角
    public Complex(double x, double y) {
        r = Math.hypot(x, y);
        theta = Math.atan2(y, x);
    }
    public double real() { return r * Math.cos(theta); }
    public double imaginary() { return r * Math.sin(theta); }
    public double modulus() { return r; }
    public double argument() { return theta; }
    public Complex add(Complex o) {
        return new Complex(r * Math.cos(theta) + o.r * Math.cos(o.theta),
                           r * Math.sin(theta) + o.r * Math.sin(o.theta));
    }
    public Complex multiply(Complex o) {
        Complex p = new Complex(0, 0);
        p.r = r * o.r;
        p.theta = theta + o.theta;
        return p;
    }
}

結論:接口相同,實現截然不同。客戶代碼零修改即可切換實現。直角坐標利於加減,極坐標利於乘除冪根。

語言層面的差異:Java vs C++

  • C++ 需頭文件,private 成員也須聲明 → 改動私有結構觸發所有包含頭文件的單元重編譯鏈接,大型項目難以忍受。
  • Java 編譯時自動提取類元信息(.class),無頭文件,修改私有成員不觸發客戶代碼重編譯。
  • C++ 緩解方案:前置聲明、匿名命名空間移動私有靜態成員、橋梁/柄體模式。

橋梁模式徹底解耦

// 接口
public interface ComplexImpl {
    double real(); double imaginary(); double modulus(); double argument();
    Complex add(Complex o); Complex multiply(Complex o);
}

// 直角坐標實現
public class ComplexCartesianImpl implements ComplexImpl { ... }

// 極坐標實現
public class ComplexPolarImpl implements ComplexImpl { ... }

// 橋梁類
public class Complex {
    private ComplexImpl impl;
    public Complex(double x, double y) {
        impl = new ComplexCartesianImpl(x, y); // 或 PolarImpl
    }
    // 委託所有操作
    public double real() { return impl.real(); }
    ...
}

優勢:接口與實現完全分離,可編譯期或運行期切換實現,增強適應力、擴展性。代價:增加複雜度與間接調用開銷,酌情使用避免過度設計。

功能性變化的應對

新增運算(如冪、開方)只需在接口添加方法,不修改現有方法簽名,不傷及舊客戶代碼——體現開放封閉原則。

本章啟示

  1. 設計接口時思考「什麼不會變」,將其固化為公共契約。
  2. 2. 將「會變」的實現細節藏在 private/內部類/獨立實現類中。 3. 跨語言開發時,理解語言機制(頭文件、編譯模型)對封裝效力的影響。 4. 橋梁模式是「接口與實現分離」的極致體現,適合多實現切換、插件化架構場景。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved