【Java设计模式 设计模式与范式】创建型模式 四:抽象工厂模式

简介: 【Java设计模式 设计模式与范式】创建型模式 四:抽象工厂模式

本篇Blog继续学习创建型模式,创建型模式的主要关注点是怎样创建对象,它的主要特点是将对象的创建与使用分离,这样可以降低系统的耦合度,使用者不需要关注对象的创建细节。本篇学习的是抽象工厂模式。由于学习的都是设计模式,所有系列文章都遵循如下的目录:

  • 模式档案:包含模式的定义、模式的特点、解决什么问题、优缺点、使用场景等
  • 模式结构:包含模式的结构,包含的角色定义及调用关系
  • 模式实现:包含模式的实现方式代码举例或者生活中简单问题映射代码举例
  • 模式实践:如果工作中或开源项目用到了该模式,就将使用过程贴到这里,并且客观讨论使用的是否恰当
  • 模式对比:如果模式相似或模式有额外的替换方法,有必要体现其相似点及不同点,区分使用,说明哪些场景下使用哪种模式比较好
  • 模式扩展:如果模式有与标准结构定义不同的变体形式,一并体现出其变体结构;对模式的思考需要进行发散等。

接下来所有设计模式的介绍都暂且遵循此基本行文逻辑吗,如果某一条目没有则无需体现,但条目顺序遵循此结构,本文的模式实践案例大多来自极客时间

模式档案

在日常开发中,凡是需要生成复杂对象的地方,都可以尝试考虑使用工厂模式来代替。抽象工厂模式考虑多等级产品的生产,将同一个具体工厂所生产的位于不同等级的一组产品称为一个产品族

模式定义:抽象工厂模式(Abstract Factory Pattern)是围绕一个超级工厂创建其他工厂。该超级工厂又称为其他工厂的工厂。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。在抽象工厂模式中,接口是负责创建一个相关对象的工厂,不需要显式指定它们的类。每个生成的工厂都能按照工厂模式提供对象

模式特点:抽象工厂模式的目的是提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。在抽象工厂模式中,抽象产品 (AbstractProduct) 可能是一个或多个,从而构成一个或多个产品族(Product Family)。 在只有一个产品族的情况下,抽象工厂模式实际上退化到工厂方法模式,可以把抽象工厂比作一个工厂,而工厂方法就是这个抽象工厂中的一条条产品线,负责生产不同维度的产品。

解决什么问题:当一个产品族中的多个对象被设计成一起工作时,它能保证客户端始终只使用同一个产品族中的对象。例如对于宝马车来说,宝马5系和宝马3系都需要:座椅、轮胎。但是宝马5系的配置是:真皮座椅、米其林轮胎;而宝马3系的配置是:革皮座椅、普通轮胎。那么创建5系的工厂和创建3系的工厂就要注意了,它们需要生产成套且有约束的产品族,不能把真皮座椅和普通轮胎组合起来,5系就要5系的产品族配置,3系就要3系的产品族配置。

优点: 除了工厂方法具备的解耦性优点外,它还额外具备产品族的约束作用:当一个产品族中的多个对象被设计成一起工作时,它能保证客户端始终只使用同一个产品族中的对象。

缺点:产品族难扩展,如果产品族中增加了一个创建产品的工厂方法(产品族扩展一个新产品),那么所有具体的工厂类都需要实现这个方法

使用场景: 当要创建的内容为一组相互依赖相互约束的产品族的产品时,使用抽象工厂模式可以方便的实现这个需求,拿上一篇工厂方法模式的形状例子扩展,如果我们想要在抽象工厂中制造红方旗【方形布料+红色染料】就需要布料产品线产出方形布料+染料产品线产出红色染料

模式结构

我们接着按照工厂方法模式中的角色来聊聊抽象工厂中该有哪些工厂以及创建过程,相对于工厂方法模式,它的特点就是:有多个抽象产品类并且每个产品抽象类有多个具体的产品类,有一个抽象工厂类和多个具体的工厂类,每个具体工厂类能创建多个具体的产品类实例

  • 抽象工厂角色: 它是具体工厂角色必须实现的接口或者必须继承的父类,在Java中它由抽象类或接口来实现。它包含创建多个抽象产品的抽象方法。
  • 具体工厂角色:它含有和具体业务逻辑有关的代码。由应用程序调用以创建对应的一组具体产品对象
  • 抽象产品角色:它是具体产品继承的父类或者是实现的接口。在Java中一般由抽象类或者接口来实现。
  • 具体产品角色:具体工厂角色所创建的对象就是此角色的实例。在Java中由具体的类来实现。

在以下的例子中我们的抽象工厂是用来生产旗帜的,拥有多条产品线,包括生产布料产品线和生产染料产品线。

模式实现

以下是各个参与其中的角色

抽象产品角色

布料形状抽象产品角色

package com.example.designpattern.factory;
/**
 * The interface Shape.
 */
public interface Shape {
    /**
     * Draw.
     */
    void draw();
}

染料颜色抽象产品角色

public interface Color {
   void fill();
}

具体产品角色

布料形状具体产品角色

public class Rectangle implements Shape {
   @Override
   public void draw() {
      System.out.println("Inside Rectangle::draw() method.");
   }
}
public class Square implements Shape {
   @Override
   public void draw() {
      System.out.println("Inside Square::draw() method.");
   }
}
public class Circle implements Shape {
   @Override
   public void draw() {
      System.out.println("Inside Circle::draw() method.");
   }
}

布料形状具体产品角色

public class Red implements Color {
   @Override
   public void fill() {
      System.out.println("Inside Red::fill() method.");
   }
}
public class Yellow implements Color {
   @Override
   public void fill() {
      System.out.println("Inside Yellow ::fill() method.");
   }
}
public class Blue implements Color {
   @Override
   public void fill() {
      System.out.println("Inside Blue::fill() method.");
   }
}

抽象工厂角色

抽象工厂角色

public interface AbstractFactory {    
        //制造布料 
        public Shape getShape();  
        //制造染料   
        public Color getColor();   
    }

具体工厂角色

具体工厂角色

public class RedSquareFactory implements AbstractFactory {
    @Override
    public Shape getShape() {
        return new Square ();
    }
    @Override
    public Color getColor() {
        return new Red();
    }
}
public class CircleBlueFactory implements AbstractFactory {
    @Override
    public Shape getShape() {
        return new Circle();
    }
    @Override
    public Color getColor() {
        return new Blue();
    }
}
public class YellowRectangleFactory implements AbstractFactory {
    @Override
    public Shape getShape() {
        return new Rectangle();
    }
    @Override
    public Color getColor() {
        return new Yellow();
    }
}

客户端调用代码知道它所需要的是哪种旗子,通过具体工厂获取产品并打印结果如下:

public class FactoryPatternDemo {
   public static void main(String[] args) {
      //获取 RedSquareFactory工厂并创造产品族实例
      AbstractFactory redSquareFactory = new RedSquareFactory();
      redSquareFactory .getShape();
      redSquareFactory .getColor();
      //获取 CircleBlueFactory工厂并创造产品族实例
      AbstractFactory circleBlueFactory = new CircleBlueFactory();
      circleBlueFactory .getShape();
      circleBlueFactory .getColor();
      //获取 YellowRectangleFactory工厂并创造产品族实例
      AbstractFactory yellowRectangleFactory = new YellowRectangleFactory();
      yellowRectangleFactory .getShape();
      yellowRectangleFactory .getColor();
   }
}

模式实践

接着上篇Blog的文本文件解析,我们再给例子增加一个维度。在简单工厂和工厂方法中,类只有一种分类方式。比如,在规则配置解析那个例子中,解析器类只会根据配置文件格式(Json、Xml、Yaml……)来分类。

设计一个多维可扩展文件解析工厂

如果解析维度更多,比如,我们既可以按照配置文件格式来分类,也可以按照解析的对象(Rule 规则配置还是 System 系统配置)来分类,那就会对应下面这 8 个 parser 类

针对规则配置的解析器:基于接口IRuleConfigParser
JsonRuleConfigParser
XmlRuleConfigParser
YamlRuleConfigParser
PropertiesRuleConfigParser
针对系统配置的解析器:基于接口ISystemConfigParser
JsonSystemConfigParser
XmlSystemConfigParser
YamlSystemConfigParser
PropertiesSystemConfigParser

针对这种特殊的场景,如果还是继续用工厂方法来实现的话,我们要针对每个 parser 都编写一个工厂类,也就是要编写 8 个工厂类。如果我们未来还需要增加针对业务配置的解析器(比如 IBizConfigParser),那就要再对应地增加 4 个工厂类。而我们知道,过多的类也会让系统难维护。

抽象工厂就是针对这种非常特殊的场景而诞生的。我们可以让一个工厂负责创建多个不同类型的对象(IRuleConfigParser、ISystemConfigParser 等),而不是只创建一种 parser 对象。这样就可以有效地减少工厂类的个数。具体的代码实现如下所示

抽象产品角色

package com.example.designpattern.factory;
public interface IRuleConfigParser{
   void parse();
}
public interface ISystemConfigParser{
   void parse();
}

具体产品角色

IRuleConfigParser的具体产品

public class JsonRuleConfigParser implements IRuleConfigParser {
   @Override
   public void parse() {
      System.out.println("IRuleConfigParser  JsonRuleConfigParser parse ");
   }
}
public class XmlRuleConfigParser implements IRuleConfigParser {
   @Override
   public void parse() {
      System.out.println("IRuleConfigParser  XmlRuleConfigParser parse");
   }
}
public class YamlRuleConfigParser implements IRuleConfigParser {
   @Override
   public void parse() {
      System.out.println("IRuleConfigParser  YamlRuleConfigParser parse");
   }
}
public class PropertiesRuleConfigParser implements IRuleConfigParser {
   @Override
   public void parse() {
      System.out.println("IRuleConfigParser  PropertiesRuleConfigParser parse");
   }
}

ISystemConfigParser 的具体产品

public class JsonSystemConfigParser implements ISystemConfigParser {
   @Override
   public void parse() {
      System.out.println("ISystemConfigParser JsonRuleConfigParser parse ");
   }
}
public class XmlSystemConfigParser implements ISystemConfigParser {
   @Override
   public void parse() {
      System.out.println("ISystemConfigParser XmlRuleConfigParser parse");
   }
}
public class YamlSystemConfigParser implements ISystemConfigParser {
   @Override
   public void parse() {
      System.out.println("ISystemConfigParser YamlRuleConfigParser parse");
   }
}
public class PropertiesSystemConfigParser implements ISystemConfigParser {
   @Override
   public void parse() {
      System.out.println("ISystemConfigParser PropertiesRuleConfigParser parse");
   }
}

抽象工厂角色

public interface IConfigParserFactory {
  IRuleConfigParser createRuleParser();
  ISystemConfigParser createSystemParser();
  //此处可以扩展新的parser类型,比如IBizConfigParser
}

具体工厂角色

public class JsonConfigParserFactory implements IConfigParserFactory {
  @Override
  public IRuleConfigParser createRuleParser() {
    return new JsonRuleConfigParser();
  }
  @Override
  public ISystemConfigParser createSystemParser() {
    return new JsonSystemConfigParser();
  }
}
public class XmlConfigParserFactory implements IConfigParserFactory {
  @Override
  public IRuleConfigParser createRuleParser() {
    return new XmlRuleConfigParser();
  }
  @Override
  public ISystemConfigParser createSystemParser() {
    return new XmlSystemConfigParser();
  }
}
public class YamlConfigParserFactory implements IConfigParserFactory {
  @Override
  public IRuleConfigParser createRuleParser() {
    return new YamlRuleConfigParser ();
  }
  @Override
  public ISystemConfigParser createSystemParser() {
    return new YamlSystemConfigParser ();
  }
}
public class PropertiesConfigParserFactory implements IConfigParserFactory {
  @Override
  public IRuleConfigParser createRuleParser() {
    return new PropertiesRuleConfigParser ();
  }
  @Override
  public ISystemConfigParser createSystemParser() {
    return new PropertiesSystemConfigParser ();
  }
}

客户端调用同工厂方法

模式对比

这里简单对比下工厂方法模式和抽象工厂模式,并整体回顾一下工厂模式的演变。通俗的说这个区别在于产品,如果产品单一,最合适用工厂模式,但是如果有多个业务品种、业务分类时,通过抽象工厂模式产生需要的对象是一种非常好的解决方式

再通俗深化理解下:工厂模式针对的是一个产品等级结构 ,抽象工厂模式针对的是面向多个产品等级结构。抽象工厂在只有一条产品线的时候退化为工厂方法模式。再看一个工厂模式的演进路线:

1 简单工厂模式

简单工厂模式不是 23 种里的一种,简而言之,就是有一个专门生产某个产品的类。比如下图中的鼠标工厂,专业生产鼠标,给参数 0,生产戴尔鼠标,给参数 1,生产惠普鼠标

2 工厂方法模式

工厂方法也就是鼠标工厂是个父类,有生产鼠标这个接口。戴尔鼠标工厂,惠普鼠标工厂继承它,可以分别生产戴尔鼠标,惠普鼠标。生产哪种鼠标不再由参数决定,而是创建鼠标工厂时,由戴尔鼠标工厂创建。后续直接调用鼠标工厂.生产鼠标()即可

3 抽象工厂模式

抽象工厂模式也就是不仅生产鼠标,同时生产键盘。也就是 PC 厂商是个父类,有生产鼠标,生产键盘两个接口。戴尔工厂,惠普工厂继承它,可以分别生产戴尔鼠标+戴尔键盘,和惠普鼠标+惠普键盘。创建工厂时,由戴尔工厂创建。后续工厂.生产鼠标()则生产戴尔鼠标,工厂.生产键盘()则生产戴尔键盘

在抽象工厂模式中,假设我们需要增加一个工厂,假设我们增加华硕工厂并且华硕也有自己的鼠标和键盘,则我们需要增加华硕工厂,和戴尔工厂一样,继承 PC 厂商,之后创建华硕鼠标,继承鼠标类。创建华硕键盘,继承键盘类即可。

在抽象工厂模式中,假设我们需要增加一个产品,假设我们增加耳麦这个产品,则首先我们需要增加耳麦这个父类,再加上戴尔耳麦,惠普耳麦这两个子类。之后在PC厂商这个父类中,增加生产耳麦的接口。最后在戴尔工厂,惠普工厂这两个类中,分别实现生产戴尔耳麦,惠普耳麦的功能。

总结一下

抽象工厂模式其实是工厂方法模式的升级版,其优点其实和工厂方法类似,都是将对象的创建解耦出来,不过抽象工厂模式的工厂能创建的不只是一个产品而是一组产品,这一组产品都有相同的产品类别,同组不同类的产品相互依赖和约束,例如一个工厂要生产红方旗,那么布料类产品必须是方形布料、染料类产品必须为红色染料,方形布料和红色染料在该工厂中就是绑定关系,该工厂也就约束了其生产的具体产品品类。

相关文章
|
5月前
|
设计模式 Java Spring
Java 设计模式之责任链模式:优雅处理请求的艺术
责任链模式通过构建处理者链,使请求沿链传递直至被处理,实现发送者与接收者的解耦。适用于审批流程、日志处理等多级处理场景,提升系统灵活性与可扩展性。
580 2
|
5月前
|
设计模式 网络协议 数据可视化
Java 设计模式之状态模式:让对象的行为随状态优雅变化
状态模式通过封装对象的状态,使行为随状态变化而改变。以订单为例,将待支付、已支付等状态独立成类,消除冗长条件判断,提升代码可维护性与扩展性,适用于状态多、转换复杂的场景。
499 0
|
7月前
|
设计模式 缓存 Java
Java设计模式(二):观察者模式与装饰器模式
本文深入讲解观察者模式与装饰器模式的核心概念及实现方式,涵盖从基础理论到实战应用的全面内容。观察者模式实现对象间松耦合通信,适用于事件通知机制;装饰器模式通过组合方式动态扩展对象功能,避免子类爆炸。文章通过Java示例展示两者在GUI、IO流、Web中间件等场景的应用,并提供常见陷阱与面试高频问题解析,助你写出灵活、可维护的代码。
|
5月前
|
设计模式 算法 搜索推荐
Java 设计模式之策略模式:灵活切换算法的艺术
策略模式通过封装不同算法并实现灵活切换,将算法与使用解耦。以支付为例,微信、支付宝等支付方式作为独立策略,购物车根据选择调用对应支付逻辑,提升代码可维护性与扩展性,避免冗长条件判断,符合开闭原则。
935 35
|
5月前
|
设计模式 消息中间件 传感器
Java 设计模式之观察者模式:构建松耦合的事件响应系统
观察者模式是Java中常用的行为型设计模式,用于构建松耦合的事件响应系统。当一个对象状态改变时,所有依赖它的观察者将自动收到通知并更新。该模式通过抽象耦合实现发布-订阅机制,广泛应用于GUI事件处理、消息通知、数据监控等场景,具有良好的可扩展性和维护性。
455 8
|
6月前
|
人工智能 Java API
Java与大模型集成实战:构建智能Java应用的新范式
随着大型语言模型(LLM)的API化,将其强大的自然语言处理能力集成到现有Java应用中已成为提升应用智能水平的关键路径。本文旨在为Java开发者提供一份实用的集成指南。我们将深入探讨如何使用Spring Boot 3框架,通过HTTP客户端与OpenAI GPT(或兼容API)进行高效、安全的交互。内容涵盖项目依赖配置、异步非阻塞的API调用、请求与响应的结构化处理、异常管理以及一些面向生产环境的最佳实践,并附带完整的代码示例,助您快速将AI能力融入Java生态。
925 12
|
7月前
|
设计模式 安全 Java
Java设计模式(一):单例模式与工厂模式
本文详解单例模式与工厂模式的核心实现及应用,涵盖饿汉式、懒汉式、双重检查锁、工厂方法、抽象工厂等设计模式,并结合数据库连接池与支付系统实战案例,助你掌握设计模式精髓,提升代码专业性与可维护性。
|
7月前
|
设计模式 XML 安全
Java枚举(Enum)与设计模式应用
Java枚举不仅是类型安全的常量,还具备面向对象能力,可添加属性与方法,实现接口。通过枚举能优雅实现单例、策略、状态等设计模式,具备线程安全、序列化安全等特性,是编写高效、安全代码的利器。
|
设计模式 缓存 安全
Java设计模式的单例模式应用场景
Java设计模式的单例模式应用场景
361 4
|
设计模式 Java 数据库连接
Java编程中的设计模式:单例模式的深度剖析
【10月更文挑战第41天】本文深入探讨了Java中广泛使用的单例设计模式,旨在通过简明扼要的语言和实际示例,帮助读者理解其核心原理和应用。文章将介绍单例模式的重要性、实现方式以及在实际应用中如何优雅地处理多线程问题。
226 4