【软考设计模式】简单工厂模式:多形态实现与代码填空精讲

【软考设计模式】简单工厂模式:多形态实现与代码填空精讲

系列定位:软考软件设计师 / 系统架构设计师 — 创建型模式专题第 1 讲
考察分值:上午题 1-2 分,下午题常结合代码填空出现
难度等级:⭐⭐☆☆☆(基础必拿分)


一、考纲定位与模式定义

1.1 考纲要求

简单工厂模式在软考中属于创建型模式的入门内容。考察形式包括:

  • 上午选择题:判断描述所属模式;识别简单工厂与工厂方法的区别

  • 下午设计题:补全工厂类的创建逻辑;根据类图写出工厂方法返回值

1.2 模式定义

简单工厂模式:定义一个工厂类,它可以根据参数的不同返回不同类的实例,被创建的实例通常都具有共同的父类。

核心意图:将对象的创建逻辑从客户端抽离,实现"创建与使用分离"


二、UML 类图与角色划分

┌─────────────────────────────────────────────┐
│                    Client                   │
│  + main()                                   │
└─────────────────────┬───────────────────────┘
                      │ uses
                      ▼
┌─────────────────────────────────────────────┐
│              SimpleFactory                  │
│  + createProduct(type): Product             │
└─────────────────────┬───────────────────────┘
                      │ creates
          ┌───────────┴───────────┐
          ▼                       ▼
┌─────────────────┐     ┌─────────────────┐
│  <<interface>>  │     │  <<interface>>  │
│    Product      │◄────│    Product      │
│  + operation()  │     │  + operation()  │
└─────────────────┘     └─────────────────┘
          △                          △
          │                          │
   ┌──────┴─────────┐         ┌──────┴─────────┐
   │                │         │                │
┌──┴─────┐     ┌────┴───┐  ┌──┴─────┐     ┌────┴───┐
│Concrete│     │Concrete│  │Concrete│     │Concrete│
│ProductA│     │ProductB│  │ProductC│     │ProductD│
└────────┘     └────────┘  └────────┘     └────────┘
角色职责软考填空关键词
Product抽象产品,定义接口interface / abstract class
ConcreteProduct具体产品,实现接口实现类名
SimpleFactory工厂类,负责创建实例create() / getInstance()
Client通过工厂获取产品面向接口调用方法

三、场景一:文件解析器(接口 + 分支判断)

业务背景:系统需要解析 PDF、Excel、Word 三种文件格式,解析逻辑由工厂统一创建。

3.1 代码实现

// 抽象产品:接口
interface FileParser {
    void parse(String filePath);
}

// 具体产品
class PdfParser implements FileParser {
    public void parse(String filePath) {
        System.out.println("解析 PDF 文件: " + filePath);
    }
}

class ExcelParser implements FileParser {
    public void parse(String filePath) {
        System.out.println("解析 Excel 文件: " + filePath);
    }
}

class WordParser implements FileParser {
    public void parse(String filePath) {
        System.out.println("解析 Word 文件: " + filePath);
    }
}

// 工厂类:使用 if-else 分支判断
class ParserFactory {
    public static FileParser createParser(String ext) {
        if ("pdf".equalsIgnoreCase(ext)) {
            return new PdfParser();
        } else if ("excel".equalsIgnoreCase(ext) || "xlsx".equalsIgnoreCase(ext)) {
            return new ExcelParser();
        } else if ("word".equalsIgnoreCase(ext) || "docx".equalsIgnoreCase(ext)) {
            return new WordParser();
        }
        throw new IllegalArgumentException("不支持的文件格式: " + ext);
    }
}

// 客户端
public class ParserClient {
    public static void main(String[] args) {
        String extension = "pdf"; // 来自文件上传组件
        FileParser parser = ParserFactory.createParser(extension);
        parser.parse("/data/report.pdf");
    }
}

四、场景二:缓存策略(抽象类 + Map 注册)

业务背景:系统支持内存缓存、Redis 缓存、磁盘缓存三种策略,通过 Map 预先注册,避免分支判断。

4.1 代码实现

import java.util.HashMap;
import java.util.Map;
import java.util.function.Supplier;

// 抽象产品:抽象类(可含公共字段/方法)
abstract class Cache {
    protected String name;
    
    public Cache(String name) {
        this.name = name;
    }
    
    public abstract void put(String key, Object value);
    public abstract Object get(String key);
}

// 具体产品
class MemoryCache extends Cache {
    public MemoryCache() { super("Memory"); }
    public void put(String key, Object value) {
        System.out.println("[" + name + "] 写入: " + key);
    }
    public Object get(String key) {
        System.out.println("[" + name + "] 读取: " + key);
        return null;
    }
}

class RedisCache extends Cache {
    public RedisCache() { super("Redis"); }
    public void put(String key, Object value) {
        System.out.println("[" + name + "] 写入: " + key);
    }
    public Object get(String key) {
        System.out.println("[" + name + "] 读取: " + key);
        return null;
    }
}

class DiskCache extends Cache {
    public DiskCache() { super("Disk"); }
    public void put(String key, Object value) {
        System.out.println("[" + name + "] 写入: " + key);
    }
    public Object get(String key) {
        System.out.println("[" + name + "] 读取: " + key);
        return null;
    }
}

// 工厂类:使用 Map + Supplier 注册(Java 8+)
class CacheFactory {
    private static final Map<String, Supplier<Cache>> registry = new HashMap<>();
    
    // 静态注册
    static {
        registry.put("memory", MemoryCache::new);
        registry.put("redis", RedisCache::new);
        registry.put("disk", DiskCache::new);
    }
    
    public static Cache createCache(String type) {
        Supplier<Cache> supplier = registry.get(type);
        if (supplier == null) {
            throw new IllegalArgumentException("未知缓存类型: " + type);
        }
        return supplier.get();
    }
}

// 客户端
public class CacheClient {
    public static void main(String[] args) {
        Cache cache = CacheFactory.createCache("redis");
        cache.put("user:1001", "张三");
    }
}

五、场景三:数据加密(接口 + 配置文件反射)

业务背景:系统支持 AES、RSA、SM4 等加密算法,算法类名写在配置文件中,工厂通过反射动态创建,新增算法无需修改工厂代码

5.1 代码实现

import java.io.InputStream;
import java.util.Properties;

// 抽象产品:接口
interface Encryptor {
    String encrypt(String plainText);
    String decrypt(String cipherText);
}

// 具体产品
class AesEncryptor implements Encryptor {
    public String encrypt(String plainText) {
        return "AES(" + plainText + ")";
    }
    public String decrypt(String cipherText) {
        return "AES-decrypt(" + cipherText + ")";
    }
}

class RsaEncryptor implements Encryptor {
    public String encrypt(String plainText) {
        return "RSA(" + plainText + ")";
    }
    public String decrypt(String cipherText) {
        return "RSA-decrypt(" + cipherText + ")";
    }
}

class Sm4Encryptor implements Encryptor {
    public String encrypt(String plainText) {
        return "SM4(" + plainText + ")";
    }
    public String decrypt(String cipherText) {
        return "SM4-decrypt(" + cipherText + ")";
    }
}

// 工厂类:通过配置文件 + 反射创建
class EncryptorFactory {
    private static final Properties config = new Properties();
    
    static {
        try (InputStream is = EncryptorFactory.class
                .getClassLoader()
                .getResourceAsStream("encrypt.properties")) {
            config.load(is);
        } catch (Exception e) {
            throw new RuntimeException("加载配置失败", e);
        }
    }
    
    public static Encryptor createEncryptor(String name) {
        String className = config.getProperty(name);
        if (className == null) {
            throw new IllegalArgumentException("未配置: " + name);
        }
        try {
            Class<?> clazz = Class.forName(className);
            return (Encryptor) clazz.getDeclaredConstructor().newInstance();
        } catch (Exception e) {
            throw new RuntimeException("创建失败: " + className, e);
        }
    }
}

// 配置文件 encrypt.properties(放在 classpath 下)
// aes=com.example.AesEncryptor
// rsa=com.example.RsaEncryptor
// sm4=com.example.Sm4Encryptor

// 客户端
public class EncryptClient {
    public static void main(String[] args) {
        Encryptor encryptor = EncryptorFactory.createEncryptor("aes");
        String result = encryptor.encrypt("敏感数据");
        System.out.println(result);
    }
}

六、三种实现方式对比

维度场景一:分支判断场景二:Map 注册场景三:反射配置
抽象产品interfaceabstract classinterface
创建方式if-else / switchMap<String, Supplier>Class.forName() + 配置文件
扩展性差(新增需改工厂)中(新增需改 Map 注册)优(新增只需改配置文件)
软考适用基础代码填空考察 Java 8 特性考察反射与配置化
复杂度

七、软考高频考点与易混淆辨析

7.1 高频考点

考点内容
模式分类创建型模式(非 GoF 23 正式成员,是工厂方法的简化)
核心优点① 客户端与具体产品解耦 ② 创建逻辑集中管理
核心缺点违背开闭原则(OCP):新增产品需修改工厂类(场景一、二);场景三通过反射可缓解
返回值类型必须是抽象产品Product 接口或抽象类),不能是具体类
客户端编程面向抽象产品编程,如 Product p = Factory.create("xxx")

7.2 易混淆辨析:简单工厂 vs 工厂方法

对比项简单工厂工厂方法
工厂数量一个工厂类一个产品对应一个具体工厂
产品扩展修改工厂类(违背 OCP)新增具体工厂类(符合 OCP)
结构复杂度简单较复杂
软考出现常作为代码填空基础题常作为下午设计大题

八、真题风格模拟与代码填空

模拟题 1(上午选择题)

以下关于简单工厂模式的叙述中,正确的是()。

A. 简单工厂是 GoF 23 种设计模式之一
B. 简单工厂新增产品时,无需修改任何现有代码即可扩展
C. 简单工厂中,客户端应直接实例化具体产品类以提高效率
D. 简单工厂将对象的创建逻辑集中到工厂类,降低了客户端与具体产品的耦合

答案:D

解析

  • A 错误:简单工厂不是 GoF 23 种正式模式,工厂方法才是。

  • B 错误:简单工厂新增产品通常需要修改工厂类的分支判断,违背开闭原则。

  • C 错误:客户端应面向抽象产品编程,通过工厂获取实例。

  • D 正确:这是简单工厂的核心优点。


模拟题 2(下午代码填空)

某系统需要支持多种数据导出格式(PDF、Excel),使用简单工厂模式实现。请补全(1)~(3)。

interface ExportFile {
    void export(String data);
}

class PdfExport implements ExportFile {
    public void export(String data) {
        System.out.println("PDF: " + data);
    }
}

class ExcelExport implements ExportFile {
    public void export(String data) {
        System.out.println("Excel: " + data);
    }
}

class ExportFactory {
    public static (1)______ createExport(String type) {
        if ("pdf".equals(type)) {
            return (2)______;
        } else if ("excel".equals(type)) {
            return (3)______;
        }
        return null;
    }
}

public class Client {
    public static void main(String[] args) {
        ExportFile file = ExportFactory.createExport("pdf");
        file.export("报表");
    }
}

答案

  • (1) ExportFile

  • (2) new PdfExport()

  • (3) new ExcelExport()


九、常见陷阱与注意事项

陷阱 1:误认为简单工厂是 GoF 正式模式

简单工厂不在 GoF 23 种设计模式中。如果上午题问"以下属于 GoF 创建型模式的是",选项中出现"简单工厂"不能选

陷阱 2:工厂返回值写成具体类

软考代码填空时,工厂方法的返回值类型必须是抽象产品(接口或抽象类),如 FileParser createParser(...) 而非 PdfParser createParser(...)

陷阱 3:忽视空指针判断

若工厂返回 null,客户端直接调用方法会抛 NullPointerException。规范写法应在工厂内抛异常,或客户端做非空判断。

陷阱 4:switch 与 if-else 的等价性

无论工厂内用 switch 还是 if-else,软考阅卷均认可。但注意:Java 12+ 的 switch 表达式返回值更简洁,但软考通常按 Java 8 语法出题。

陷阱 5:与单例模式混淆

简单工厂可以返回单例对象(工厂内缓存实例),但简单工厂本身≠单例模式。不要看到"工厂只创建一个对象"就误判为单例。


十、总结

要点内容
定义一个工厂类根据参数创建不同实例,实例具有共同父类
分类创建型模式(非 GoF 正式成员,工厂方法的简化版)
实现形态接口/抽象类 + 分支判断 / Map 注册 / 反射配置
优点解耦、集中管理创建逻辑
缺点违背开闭原则(反射配置可缓解)
软考重点与工厂方法/抽象工厂的辨析;代码填空补全工厂类;识别类图角色
答题技巧看到"根据参数返回不同对象"且只有一个工厂类 → 选简单工厂
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值