当前位置: 首页 > news >正文

23种设计模式-创建型模式-抽象工厂

文章目录

  • 简介
  • 场景
  • 问题
    • 1. 风格一致性失控
    • 2. 对象创建硬编码
    • 3. 产品族管理失效
  • 解决
  • 总结

简介

抽象工厂是一种创建型设计模式,可以生成相关对象系列,而无需指定它们的具体类。

场景

假设你正在写一个家具店模拟器。
你的代码这些类组成:

  1. 相关产品系列,例如:椅子 + 沙发 + 咖啡桌。
  2. 此系列有多种风格。例如,椅子 + 沙发 + 咖啡桌系列有以下风格:现代、维多利亚、装饰艺术。

在这里插入图片描述
你需要一种方法来创建家具对象,确保它们与同一风格的其他对象相匹配。如果客户收到风格不匹配的家具,他们就会非常生气。

在这里插入图片描述
另外,在向程序添加新产品或产品系列时,你也不想更改现有代码。
按照惯例,大家一开始会怎么实现?

// 直接实例化具体家具类导致风格混杂
class Client {
    public void createLivingRoom() {
        // 混合使用不同风格组件(致命错误)
        Chair modernChair = new ModernChair();
        Sofa victorianSofa = new VictorianSofa();
        CoffeeTable artDecoTable = new ArtDecoCoffeeTable();
        
        modernChair.sit();
        victorianSofa.lieDown();
        artDecoTable.placeMagazine();
    }
}

// 具体产品实现
class ModernChair extends Chair {
    public void sit() { System.out.println("Modern chair sitting"); } 
}

class VictorianSofa extends Sofa {
    public void lieDown() { System.out.println("Victorian sofa relaxing"); }
}

class ArtDecoCoffeeTable extends CoffeeTable {
    public void placeMagazine() { System.out.println("ArtDeco table placement"); }
}

问题

1. 风格一致性失控

客户端直接创建不同风格的对象(现代椅子+维多利亚沙发)
╭── 问题场景 ──╮
用户订单要求"维多利亚风格客厅"时:
┌─────────────────┬──────────────────────┐
│ 预期组合 │ 实际可能创建的组合 │
├─────────────────┼──────────────────────┤
│ VictorianChair │ VictorianChair │
│ VictorianSofa │ ModernSofa ←不匹配 │
│ VictorianTable │ ArtDecoTable ←灾难性 │
└─────────────────┴──────────────────────┘
结果:客户收到风格冲突的家具套装

2. 对象创建硬编码

每当新增风格时(如新增ArtDeco),强制修改所有客户端代码


// 新增风格场景产生连锁修改
class Client {
    // 必须添加新分支判断
    public void createSet(String style) {
        if ("ArtDeco".equals(style)) { // 破坏开放封闭原则
            chair = new ArtDecoChair(); // 需要新增多个类引用
            sofa = new ArtDecoSofa();
        }
    }
}

3. 产品族管理失效

缺乏统一约束机制,易出现类型错误

// 错误将现代餐桌与维多利亚椅组合(类型系统无法阻止)
FurnitureSet set = new FurnitureSet(
    new ModernChair(),
    new VictorianDiningTable() // 应该抛出异常但现有代码无法约束
);

解决

抽象工厂模式建议的第一件事就是明确声明产品系列中每个不同产品的接口(例如,椅子、沙发或咖啡桌)。然后,让所有风格的产品都实现这些接口。例如,所有风格的椅子都可以实现 Chair 接口;所有风格的咖啡桌都可以实现 CoffeeTable 接口,等等。

在这里插入图片描述

// 接口约束产品规格
public interface Chair {
    void sit();
}

public interface Sofa {
    void lieDown();
}

public interface CoffeeTable {
    void placeItem();
}
// 确保现代系列组件统一
public class ModernChair implements Chair {
    @Override
    public void sit() { 
        System.out.println("Modern chair designed seating"); 
    }
}

public class ModernSofa implements Sofa {
    @Override
    public void lieDown() { 
        System.out.println("Modern sofa clean lines design"); 
    }
}

public class ModernCoffeeTable implements CoffeeTable {
    @Override
    public void placeItem() { 
        System.out.println("Modern geometric table surfaces"); 
    }
}

// 保证维多利亚风格一致性
public class VictorianChair implements Chair {
    @Override
    public void sit() { 
        System.out.println("Classic carved wood chair"); 
    }
}

public class VictorianSofa implements Sofa {
    @Override
    public void lieDown() { 
        System.out.println("Antique fabric sofa"); 
    }
}

public class VictorianCoffeeTable implements CoffeeTable {
    @Override
    public void placeItem() { 
        System.out.println("Ornate marble-top table"); 
    }
}

下一步是声明抽象工厂(接口),其中包含特定产品系列所有产品的创建方法列表(例如,createChair、createSofa 和 createCoffeeTable)。这些方法必须返回我们之前定义的抽象产品类型接口:Chair、Sofa、CoffeeTable 等等。对于产品的每种风格,我们基于 AbstractFactory 接口创建一个单独的工厂类。这个工厂类是返回特定类型产品的类。例如,ModernFurnitureFactory 只能创建 ModernChair、ModernSofa 和 ModernCoffeeTable 对象。
在这里插入图片描述

public interface FurnitureFactory {
    Chair createChair();
    Sofa createSofa();
    CoffeeTable createCoffeeTable();
}

// 现代风格产品线工厂
public class ModernFactory implements FurnitureFactory {
    @Override
    public Chair createChair() { 
        return new ModernChair(); 
    }
    @Override
    public Sofa createSofa() { 
        return new ModernSofa(); 
    }
    @Override
    public CoffeeTable createCoffeeTable() { 
        return new ModernCoffeeTable(); 
    }
}

// 维多利亚风格产品线工厂
public class VictorianFactory implements FurnitureFactory {
    @Override
    public Chair createChair() { 
        return new VictorianChair(); 
    }
    @Override
    public Sofa createSofa() { 
        return new VictorianSofa(); 
    }
    @Override
    public CoffeeTable createCoffeeTable() { 
        return new VictorianCoffeeTable(); 
    }
}

客户端代码必须通过抽象接口来和工厂、产品协作。这样就可以更改传给客户端代码的工厂的类型以及客户端代码接收的产品风格,而不破坏实际的客户端代码。
假设客户希望工厂生产一把椅子。客户不必知道工厂的类别,也不必关心它得到的椅子是什么类型。无论是现代风格还是维多利亚风格的椅子,客户都必须使用抽象Chair接口以相同的方式处理所有椅子。唯一知道的是sitOn方法。此外,无论返回哪种椅子,它总是与同一工厂对象生产的沙发或咖啡桌风格相匹配。

public class FurnitureStore {
    private FurnitureFactory factory;
    
    // 动态绑定具体工厂
    public FurnitureStore(FurnitureFactory factory) {
        this.factory = factory;
    }
    
    public void showcaseSet() {
        Chair chair = factory.createChair();
        Sofa sofa = factory.createSofa();
        CoffeeTable table = factory.createCoffeeTable();
        
        System.out.println("展示完整风格套件:");
        chair.sit();
        sofa.lieDown();
        table.placeItem();
    }
}

// 使用示例
public class Main {
    public static void main(String[] args) {
        // 创建现代风格商店
        FurnitureStore modernStore = new FurnitureStore(new ModernFactory());
        modernStore.showcaseSet();
        
        // 创建维多利亚风格商店
        FurnitureStore victorianStore = new FurnitureStore(new VictorianFactory());
        victorianStore.showcaseSet();
    }
}

还有一件事需要明确:如果客户端只接触抽象接口,那是什么创建了实际的工厂对象(即new ModernFactory())?通常,应用程序在初始化阶段创建一个具体的工厂对象。在这之前,应用程序必须根据配置或环境设置选择工厂类型。

总结

在这里插入图片描述

  1. 抽象产品(Abstract Prod­uct):构成所有产品的一组接口。
  2. 具体产品(Con­crete Prod­uct):抽象产品的多种不同类型实现。所有风格产品(维多利亚/现代)都必须实现相应的抽象产品(椅子/沙发)。
  3. 抽象工厂(Abstract Fac­to­ry)接口:声明了一组创建各种抽象产品的方法。
  4. 具体工厂(Con­crete Fac­to­ry):实现抽象工厂的产品创建方法。每个具体工厂都对应特定风格的产品,且只能创建这一种风格的产品。
  5. 尽管具体工厂会对具体产品进行初始化,它的创建方法必须返回相应的抽象产品。只有这样,使用工厂类的客户端代码就不会与工厂创建的特定风格产品耦合。客户端(Client)只需通过抽象接口调用工厂和产品对象,就可以跟任何具体工厂/产品进行交互。

相关文章:

  • python中两个星号什么意思
  • C#入门学习记录(五)轻松掌握条件分支与循环语句
  • AI+金融 应用 使用DeepSeek、Qwen等大模型输入自然语言,得到通达信等行情软件公式代码,导入后使用
  • 日语学习-日语知识点小记-构建基础-JLPT-N4N5阶段(25):解释说明:という
  • draw.io费的思维导图软件、支持ProcessOn无水印导出。
  • M-LAG 技术全面解析
  • 基于springboot的墙绘产品展示交易平台(037)
  • 进程状态与PV操作
  • Android 13组合键截屏功能的彻底移除实战
  • 数据库MVCC详解
  • Netty——NIO 空轮询 bug
  • 直观理解ECC椭圆曲线加密算法
  • 特征工程自动化(FeatureTools实战)
  • Windows 上彻底卸载 Node.js
  • Linux信号处理:从内核机制到工程艺术
  • nano 是一个简单易用的命令行文本编辑器,适合在终端中快速编辑文件。它比 vi 或 vim 更容易上手,特别适合初学者
  • ES集群的部署
  • 游戏立项时期随笔记录(2)
  • 深度学习仓库代码结构认识
  • 石家庄 10 年 PHP 开发者转岗分析
  • 2025年上海科技节5月17日启动,56家重点实验室和大科学设施将向公众开放
  • 盖茨说对中国技术封锁起到反作用
  • 商务部就开展打击战略矿产走私出口专项行动应询答记者问
  • 《淮水竹亭》:一手好牌,为何打成这样
  • 历史地理学者成一农重回母校北京大学,担任历史系教授
  • 来伊份:已下架涉事批次蜜枣粽产品,消费者可获额外补偿,取得实物后进一步分析