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

Java 策略模式與簡單工廠模式組合實戰:圖片保存格式動態選擇

     編輯:Java 程式設計入門

前言

策略模式定義一系列算法,封裝每一個,使它們可互換。配合簡單工廠模式,可根據運行時條件動態選擇具體策略。本文以「圖片保存格式選擇」為例,演示策略接口定義、多種實現、工廠選擇、客戶端調用的完整流程。

核心角色

角色類名職責
Strategy 接口ImageSaver定義統一的 save() 契約
ConcreteStrategyGifSaver, JpegSaver, PngSaver實現具體保存邏輯
FactorySaverFactory根據類型字符串創建策略實例
Context/ClientImageProcessor持有策略引用,委託執行

完整代碼實現

package com.example.pattern;

// ========== 1. 策略接口 ==========
public interface ImageSaver {
    /**
     * 保存圖片
     * @param imageData 圖片二進制數據
     * @param destPath  目標文件路徑
     */
    void save(byte[] imageData, String destPath) throws IOException;
}

// ========== 2. 具體策略實現 ==========
/** GIF 格式保存 */
public class GifSaver implements ImageSaver {
    @Override
    public void save(byte[] imageData, String destPath) throws IOException {
        // 實際項目中使用 ImageIO 或第三方庫
        Files.write(Paths.get(destPath), imageData);
        System.out.println("[GIF] 圖片已保存至:" + destPath);
    }
}

/** JPEG 格式保存(支持質量參數) */
public class JpegSaver implements ImageSaver {
    private final float quality; // 0.0f - 1.0f
    
    public JpegSaver(float quality) {
        this.quality = Math.max(0.1f, Math.min(1.0f, quality));
    }
    
    @Override
    public void save(byte[] imageData, String destPath) throws IOException {
        // 這裡演示邏輯,實際需用 ImageWriter 設置壓縮質量
        Files.write(Paths.get(destPath), imageData);
        System.out.printf("[JPEG] 質量 %.0f%% 保存至:%s%n", quality * 100, destPath);
    }
}

/** PNG 格式保存(無損壓縮) */
public class PngSaver implements ImageSaver {
    @Override
    public void save(byte[] imageData, String destPath) throws IOException {
        Files.write(Paths.get(destPath), imageData);
        System.out.println("[PNG] 無損保存至:" + destPath);
    }
}

// ========== 3. 策略工廠(簡單工廠 + 註冊表模式) ==========
public class SaverFactory {
    // 策略註冊表:類型 -> 實例供應商(支持延遲創建、參數化)
    private static final Map> REGISTRY = new ConcurrentHashMap<>();
    
    static {
        // 默認註冊
        register("gif", GifSaver::new);
        register("jpeg", () -> new JpegSaver(0.9f)); // 默認 90% 質量
        register("jpg", () -> new JpegSaver(0.9f));
        register("png", PngSaver::new);
        register("webp", () -> new WebpSaver(0.8f)); // 擴展示例
    }
    
    /** 註冊自定義策略(開放擴展) */
    public static void register(String type, Supplier supplier) {
        REGISTRY.put(type.toLowerCase(), supplier);
    }
    
    /** 獲取策略實例 */
    public static ImageSaver getSaver(String type) {
        Supplier supplier = REGISTRY.get(type.toLowerCase());
        if (supplier == null) {
            throw new IllegalArgumentException("不支持的圖片格式:" + type + 
                ",支持列表:" + REGISTRY.keySet());
        }
        return supplier.get(); // 每次獲取新實例(無狀態可共享單例)
    }
    
    /** 獲取所有支持格式 */
    public static Set getSupportedFormats() {
        return Collections.unmodifiableSet(REGISTRY.keySet());
    }
}

// ========== 4. 新增格式示例:WebP ==========
class WebpSaver implements ImageSaver {
    private final float quality;
    WebpSaver(float q) { this.quality = q; }
    @Override public void save(byte[] data, String path) throws IOException {
        Files.write(Paths.get(path), data);
        System.out.printf("[WebP] 質量 %.0f%% 保存至:%s%n", quality * 100, path);
    }
}

// ========== 5. 上下文/客戶端使用 ==========
public class ImageProcessor {
    private ImageSaver currentSaver;
    
    public void setFormat(String format) {
        this.currentSaver = SaverFactory.getSaver(format);
    }
    
    public void saveImage(byte[] imageData, String outputPath) throws IOException {
        if (currentSaver == null) {
            throw new IllegalStateException("未設置保存格式,請先調用 setFormat()");
        }
        currentSaver.save(imageData, outputPath);
    }
    
    // ========== 測試主方法 ==========
    public static void main(String[] args) throws IOException {
        ImageProcessor processor = new ImageProcessor();
        byte[] dummyData = "模擬圖片數據".getBytes();
        
        // 用戶選擇 GIF
        processor.setFormat("gif");
        processor.saveImage(dummyData, "output/image.gif");
        
        // 用戶選擇 JPEG(高質量)
        processor.setFormat("jpeg");
        processor.saveImage(dummyData, "output/photo.jpg");
        
        // 用戶選擇 PNG
        processor.setFormat("png");
        processor.saveImage(dummyData, "output/screenshot.png");
        
        // 動態註冊新格式(插件化擴展)
        SaverFactory.register("avif", () -> new AvifSaver());
        processor.setFormat("avif");
        processor.saveImage(dummyData, "output/nextgen.avif");
        
        System.out.println("\n支持格式:" + SaverFactory.getSupportedFormats());
    }
}

// ========== 6. 假設的 AVIF 策略(展示擴展性) ==========
class AvifSaver implements ImageSaver {
    @Override public void save(byte[] data, String path) throws IOException {
        Files.write(Paths.get(path), data);
        System.out.println("[AVIF] 新一代格式保存至:" + path);
    }
}

運行輸出


[GIF] 圖片已保存至:output/image.gif
[JPEG] 質量 90% 保存至:output/photo.jpg
[PNG] 無損保存至:output/screenshot.png
[AVIF] 新一代格式保存至:output/nextgen.avif

支持格式:[gif, jpeg, jpg, png, webp, avif]

設計優勢分析

1. 開閉原則 (OCP)

  • 新增格式只需實現 ImageSaver 並註冊,無需修改現有代碼
  • 工廠使用 Supplier 延遲創建,支持參數化構造(如 JPEG 質量)

2. 單一職責原則 (SRP)

  • 每個 Saver 只關心特定格式邏輯
  • 工廠只負責實例化決策
  • Processor 只負責業務流程編排

3. 運行時動態切換

// 根據用戶選擇、文件擴展名、配置文件動態決定
String format = detectFormatFromUserSelection(); // 或 fileExt, 或 config
processor.setFormat(format);
processor.saveImage(data, path);

4. 測試友好

  • 可輕松 Mock ImageSaver 測試 Processor 邏輯
  • 每個 Saver 可獨立單元測試

進階擴展方向

1. 策略參數化配置

// 支持運行時參數
public class ParametricJpegSaver implements ImageSaver {
    private final float quality;
    private final boolean progressive;
    
    public ParametricJpegSaver(float quality, boolean progressive) { ... }
    
    // 工廠註冊時綁定參數
    SaverFactory.register("jpeg-high", () -> new ParametricJpegSaver(0.95f, true));
    SaverFactory.register("jpeg-web", () -> new ParametricJpegSaver(0.75f, false));

2. Spring 整合:策略作為 Bean

@Component
public class SpringSaverFactory {
    private final Map savers = new HashMap<>();
    
    @Autowired
    public SpringSaverFactory(List savers) {
        savers.forEach(s -> this.savers.put(s.getFormat(), s));
    }
    // ...
}

@Component("gifSaver") @Qualifier("gif") class GifSaverImpl implements ImageSaver { ... }

3. 責任鏈模式組合:格式回退

// 嘗試 WebP → 不支持則回退 JPEG → 最後 PNG
public class FallbackSaver implements ImageSaver {
    private final List chain = List.of(new WebpSaver(), new JpegSaver(0.8f), new PngSaver());
    // save() 依次嘗試...
}

總結

策略模式 + 簡單工廠(註冊表變體)實現了「算法族內部封裝、外部動態選擇、無限擴展不修改核心」。關鍵點:接口定義契約、實現隔離變化、工廠解耦創建、註冊表支持開放擴展。這是框架設計、插件系統、多格式處理的經典最佳實踐。

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