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

本文涉及的产品
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 1个月
简介: 【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厂商这个父类中,增加生产耳麦的接口。最后在戴尔工厂,惠普工厂这两个类中,分别实现生产戴尔耳麦,惠普耳麦的功能。

总结一下

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

相关文章
|
2月前
|
设计模式
「全网最细 + 实战源码案例」设计模式——抽象工厂模式
抽象工厂模式是一种创建型设计模式,提供接口用于创建一系列相关或依赖的对象,无需指定具体类。它解决了产品族问题,管理和创建一组相关产品。结构上包括抽象工厂、具体工厂、抽象产品和具体产品。适用于创建相关对象、产品族固定但种类变化的场景。优点是分离接口与实现、管理产品族方便且扩展性好;缺点是产品族扩展困难且代码复杂度增加。通过配置文件和反射机制可进一步改进,使系统更灵活易扩展。
75 17
|
3月前
|
存储 Java 数据挖掘
Java 8 新特性之 Stream API:函数式编程风格的数据处理范式
Java 8 引入的 Stream API 提供了一种新的数据处理方式,支持函数式编程风格,能够高效、简洁地处理集合数据,实现过滤、映射、聚合等操作。
120 6
|
4月前
|
设计模式 消息中间件 搜索推荐
Java 设计模式——观察者模式:从优衣库不使用新疆棉事件看系统的动态响应
【11月更文挑战第17天】观察者模式是一种行为设计模式,定义了一对多的依赖关系,使多个观察者对象能直接监听并响应某一主题对象的状态变化。本文介绍了观察者模式的基本概念、商业系统中的应用实例,如优衣库事件中各相关方的动态响应,以及模式的优势和实际系统设计中的应用建议,包括事件驱动架构和消息队列的使用。
|
4月前
|
设计模式 Java 数据库连接
Java编程中的设计模式:单例模式的深度剖析
【10月更文挑战第41天】本文深入探讨了Java中广泛使用的单例设计模式,旨在通过简明扼要的语言和实际示例,帮助读者理解其核心原理和应用。文章将介绍单例模式的重要性、实现方式以及在实际应用中如何优雅地处理多线程问题。
66 4
|
5月前
|
设计模式 Java 程序员
[Java]23种设计模式
本文介绍了设计模式的概念及其七大原则,强调了设计模式在提高代码重用性、可读性、可扩展性和可靠性方面的作用。文章还简要概述了23种设计模式,并提供了进一步学习的资源链接。
104 0
[Java]23种设计模式
|
4月前
|
设计模式 JavaScript Java
Java设计模式:建造者模式详解
建造者模式是一种创建型设计模式,通过将复杂对象的构建过程与表示分离,使得相同的构建过程可以创建不同的表示。本文详细介绍了建造者模式的原理、背景、应用场景及实际Demo,帮助读者更好地理解和应用这一模式。
142 0
|
5月前
|
设计模式 监控 算法
Java设计模式梳理:行为型模式(策略,观察者等)
本文详细介绍了Java设计模式中的行为型模式,包括策略模式、观察者模式、责任链模式、模板方法模式和状态模式。通过具体示例代码,深入浅出地讲解了每种模式的应用场景与实现方式。例如,策略模式通过定义一系列算法让客户端在运行时选择所需算法;观察者模式则让多个观察者对象同时监听某一个主题对象,实现松耦合的消息传递机制。此外,还探讨了这些模式与实际开发中的联系,帮助读者更好地理解和应用设计模式,提升代码质量。
Java设计模式梳理:行为型模式(策略,观察者等)
|
5月前
|
设计模式 Java
Java设计模式
Java设计模式
60 0
|
3月前
|
设计模式 前端开发 搜索推荐
前端必须掌握的设计模式——模板模式
模板模式(Template Pattern)是一种行为型设计模式,父类定义固定流程和步骤顺序,子类通过继承并重写特定方法实现具体步骤。适用于具有固定结构或流程的场景,如组装汽车、包装礼物等。举例来说,公司年会节目征集时,蜘蛛侠定义了歌曲的四个步骤:前奏、主歌、副歌、结尾。金刚狼和绿巨人根据此模板设计各自的表演内容。通过抽象类定义通用逻辑,子类实现个性化行为,从而减少重复代码。模板模式还支持钩子方法,允许跳过某些步骤,增加灵活性。
174 11
|
9天前
|
设计模式 Java 关系型数据库
设计模式:工厂方法模式(Factory Method)
工厂方法模式是一种创建型设计模式,通过将对象的创建延迟到子类实现解耦。其核心是抽象工厂声明工厂方法返回抽象产品,具体工厂重写该方法返回具体产品实例。适用于动态扩展产品类型、复杂创建逻辑和框架设计等场景,如日志记录器、数据库连接池等。优点包括符合开闭原则、解耦客户端与具体产品;缺点是可能增加类数量和复杂度。典型应用如Java集合框架、Spring BeanFactory等。