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

Java Interface 兩大經典用法:動態加載多態與策略模式參數傳遞

     編輯:Java 相關知識

用法一:接口作為動態加載的多態契約

場景:主程序不依賴具體實現類,通過配置/參數指定類名,利用反射動態實例化,實現「不修改主代碼擴展新實現」(開閉原則)。

核心接口

package com.example.vehicle;

public interface Vehicle {
    /**
     * 計算行駛速度
     * @param a 參數A
     * @param b 參數B
     * @param c 參數C
     * @return 速度 km/h
     */
    double calculateSpeed(double a, double b, double c);
}

具體實現

package com.example.vehicle.impl;

public class Car implements Vehicle {
    @Override
    public double calculateSpeed(double a, double b, double c) {
        return a * b / c; // 汽車速度公式
    }
}

public class Plane implements Vehicle {
    @Override
    public double calculateSpeed(double a, double b, double c) {
        return a + b + c; // 飛機速度公式
    }
}

// 新增 Ship 無需修改任何現有代碼
public class Ship implements Vehicle {
    @Override
    public double calculateSpeed(double a, double b, double c) {
        return (a + b) * c / 10; // 船隻速度公式
    }
}

主程序:動態加載與計算

package com.example.main;

import com.example.vehicle.Vehicle;

public class TravelTimeCalculator {
    public static void main(String[] args) {
        if (args.length != 4) {
            System.err.println("用法: java TravelTimeCalculator    ");
            System.err.println("示例: java TravelTimeCalculator com.example.vehicle.impl.Car 100 45 67");
            return;
        }

        String className = args[0];
        double a = Double.parseDouble(args[1]);
        double b = Double.parseDouble(args[2]);
        double c = Double.parseDouble(args[3]);

        try {
            // 關鍵:反射動態實例化
            Class clazz = Class.forName(className);
            Vehicle vehicle = (Vehicle) clazz.getDeclaredConstructor().newInstance();

            double speed = vehicle.calculateSpeed(a, b, c);
            double hours = 1000.0 / speed; // 固定距離 1000km

            System.out.printf("交通工具: %s%n", className);
            System.out.printf("參數: A=%.2f, B=%.2f, C=%.2f%n", a, b, c);
            System.out.printf("平均速度: %.2f km/h%n", speed);
            System.out.printf("行駛 1000km 需時: %.2f 小時%n", hours);
        } catch (ClassNotFoundException e) {
            System.err.println("找不到類: " + className);
        } catch (Exception e) {
            System.err.println("實例化失敗: " + e.getMessage());
            e.printStackTrace();
        }
    }
}

運行示例

$ java TravelTimeCalculator com.example.vehicle.impl.Car 100 45 67
交通工具: com.example.vehicle.impl.Car
參數: A=100.00, B=45.00, C=67.00
平均速度: 67.16 km/h
行駛 1000km 需時: 14.89 小時

$ java TravelTimeCalculator com.example.vehicle.impl.Plane 130 45 67
交通工具: com.example.vehicle.impl.Plane
參數: A=130.00, B=45.00, C=67.00
平均速度: 242.00 km/h
行駛 1000km 需時: 4.13 小時

用法二:接口作為方法參數實現策略模式

場景:同一操作有多種算法/行為,通過傳入不同接口實現動態切換行為,避免大量 if-else/switch。

擴展板卡插槽例子

// 統一接口
public interface ExpansionCard {
    void initialize(); // 初始化卡片
}

// 具體實現
public class GraphicsCard implements ExpansionCard {
    @Override public void initialize() { System.out.println("顯卡初始化: GPU 驅動加載、VRAM 檢測"); }
}

public class NetworkCard implements ExpansionCard {
    @Override public void initialize() { System.out.println("網卡初始化: MAC 地址綁定、協議棧啟動"); }
}

public class SoundCard implements ExpansionCard {
    @Override public void initialize() { System.out.println("聲卡初始化: DSP 固件加載、編解碼器就緒"); }
}

// 主板類:接口作為方法參數
public class Motherboard {
    public void installCard(ExpansionCard card) { // 關鍵:接口類型參數
        System.out.println("主板檢測到擴展卡,開始初始化...");
        card.initialize(); // 多態調用,具體行為由傳入實例決定
        System.out.println("擴展卡安裝完成。");
    }
}

// 客戶端代碼
public class ComputerAssembly {
    public static void main(String[] args) {
        Motherboard mb = new Motherboard();
        
        mb.installCard(new GraphicsCard());  // 裝顯卡
        mb.installCard(new NetworkCard());   // 裝網卡
        mb.installCard(new SoundCard());     // 裝聲卡
        
        // 新增 USB 卡無需修改 Motherboard
        mb.installCard(new ExpansionCard() {
            @Override public void initialize() { System.out.println("USB 控制器初始化"); }
        });
    }
}

對比總結

維度動態加載多態策略模式參數傳遞
核心機制反射 + Class.forName()多態調用 (接口引用指向實現對象)
耦合度極低:主程序完全不依賴實現類低:依賴接口定義,不依賴具體類
擴展方式新增實現類 + 配置類名新增實現類 + 調用處傳入新實例
典型場景插件系統、驅動程序、可配置組件算法策略、回調處理器、事件監聽器
性能首次反射較慢,可緩存 Constructor直接調用,JIT 內聯優化極佳
類型安全運行時才發現 ClassNotFound/Cast 異常編譯期類型檢查,安全

現代化演進:SPI 機制與依賴注入

JDK SPI (ServiceLoader)

// META-INF/services/com.example.vehicle.Vehicle 文件內容:
com.example.vehicle.impl.Car
com.example.vehicle.impl.Plane
com.example.vehicle.impl.Ship

// 代碼加載
ServiceLoader loader = ServiceLoader.load(Vehicle.class);
for (Vehicle v : loader) {
    System.out.println("發現實現: " + v.getClass().getName() + " 速度: " + v.calculateSpeed(1,2,3));
}

Spring 依賴注入

@Component
public class TravelService {
    private final Map vehicles; // 自動注入所有實現 Bean

    public TravelService(Map vehicles) {
        this.vehicles = vehicles;
    }

    public double calcTime(String vehicleName, double a, double b, double c) {
        Vehicle v = vehicles.get(vehicleName);
        if (v == null) throw new IllegalArgumentException("未知交通工具: " + vehicleName);
        return 1000 / v.calculateSpeed(a, b, c);
    }
}

總結

Interface 是 Java 面向抽象編程的基石。動態加載多態 通過反射打破編譯期依賴,支撐插件化架構;策略模式參數傳遞 利用多態消除條件分支,實現行為動態組合。現代框架(Spring、Dubbo、SPI)進一步將這兩種模式標準化、自動化,開發者應優先使用框架提供的機制(@Autowired、ServiceLoader、@SPI),僅在框架無法覆蓋的邊緣場景手寫反射/多態代碼。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved