java开发案例(JDK源代码中)

 2025-08-12 13:03:02  阅读 597  评论 0

摘要:这个设计模式,以其复杂著称,很长一段时间,我一直认为它高高在上,只存在于理论之中,从未真正落地。直到我在JDK中,遇到这个从未使用过的模块, 才发现这个设计模式,其实早已被前辈们落地了。设计模式还是要落地的大家好,欢迎关注极客架构师,极客架构师,专注架构师成长

这个设计模式,以其复杂著称,很长一段时间,我一直认为它高高在上,只存在于理论之中,从未真正落地。直到我在JDK中,遇到这个从未使用过的模块, 才发现这个设计模式,其实早已被前辈们落地了。


设计模式还是要落地的

大家好,欢迎关注极客架构师,极客架构师,专注架构师成长,我是码农老吴。

本期仍然是《源码说》的第6期。

在上半部分,我向大家分享了工厂系列模式里面的前面两个设计模式,静态工厂方法模式和工厂方法模式。静态工厂方法模式的受欢迎程度,明显与它的江湖地位不匹配,以后,在我——码农老吴的设计模式知识体系中,将它与正式的设计模式一视同仁,不再使用它原来的名字——简单工厂模式(有轻蔑之意)。另外,支撑Spring框架,实现IOC容器的设计模式,竟然不过是工厂方法模式而已。

下面,我继续给大家分享工厂系列模式里面的第三个设计模式,也就是最复杂,最抽象的抽象工厂模式。

基本思路

工厂方法个数的困惑

抽象工厂模式的查询技巧

JDK

BorderFactory(浪费大家的感情)

Toolkit模块(经典的抽象工厂模式)

StackOverFlow 上的抽象工厂模式

DocumentBuilderFactory

TransformerFactory

XPathFactory

后续规划

工厂方法模式 VS 抽象工厂模式

工厂系列模式,有三个设计模式,从简到繁,依次是静态工厂方法模式(简单工厂模式),工厂方法模式和抽象工厂模式。这三个设计模式,最容易识别的,非静态工厂方法模式莫属。而工厂方法模式和抽象工厂模式,如果仅从概念上看,这两个设计模式应该比较好识别,但是,如果仅仅给你源代码,让你识别出这两个设计模式,就没那么容易了。

如果从概率上让你猜,你闭着眼睛选工厂方法模式就可以了。大部分程序员,这辈子遇到抽象工厂模式的概率很低,而使用抽象工厂模式设计自己的软件模块,就更微乎其微了。

那么到底如何仅仅通过源代码,区分出这两个设计模式呢。

要搞清楚这个问题,我们需要系统地梳理一下,看看这两个家伙,到底有哪些区别。

产品族与产品分类

要区分工厂方法模式和抽象工厂模式,有两个概念非常重要,那就是产品族与产品分类。我在前面分享抽象工厂的文章中,专门深入探讨了这两个概念,这里再复习一下。

何为产品族,以我们电商平台的文件导出系统为例。

市场情况有变,电商平台不仅要面向国内市场,还要走出国门,开拓海外市场。我们的导出系统现在也要支持国际化,不仅要支持国内商家,还需要支持其他国家或者地区的商家。在国际化时,每个国家导出的文件格式,基本上是相同的,否则就是不平等,是要出国际争端的,导致世界大战,人类就灭亡了,到时候这个责任又算到了我们码农头上,我们码农不背锅。

而正是这个时候,产品族就出现了。

比如,我们的系统现在要支持中国和俄罗斯两个国家的文件导出。

中国:Excel文件(Excel2003,Excel2007),SQL文件(Mysql,Oracle)

俄罗斯:Excel文件(Excel2003,Excel2007),SQL文件(Mysql,Oracle)

(补充一点,不同国家的数据导出,不仅仅是个简单的翻译问题,还涉及到日期格式,货币格式,文字排版,汇率转化等方方面面,所以要建立不同的导出类。)

这里面,大家要体会到产品族和产品分类的区别。

以上面的需求为例,Excel文件,SQL文件,或者Word文件,PDF文件,这些都属于产品分类。

一个抽象工厂模式,支持生产多个产品分类。而且产品分类下面,还可以再分类,也就是二级分类,三三级分类,比如Excel文件,我们就还可以分为Excel2003,Excel2007等等。

而导出的文件,如果要区分中国,俄罗斯,以及其他国家或者地区,这属于产品族的概念。

族的本质,也是分类,只是给分类换了一个名字而已,但是在抽象工厂模式里面,产品族有特殊的约定。

一个产品族,往往需要包含所有的产品分类(不是必须的)

比如俄罗斯地区的导出文件,也需要支持Excel,SQL文件,Word,PDF等各种文件。

一个系统同一时刻,往往只能选择一个产品族,但是可以使用多个产品分类。

比如,中国地区的商家,往往只选择中国地区的文件导出。不会两个都需要。总之,不同产品族的产品,一般不会同时使用。对于一个商家,可以选择多个产品分类,但是往往同一时刻,只需要选择一个产品族。这个应该不难理解吧。

同一个产品族,里面的产品,往往有依赖关系

同一个产品族,里面的不同产品之间,可以有依赖性。而不同的产品族,里面的产品,则一般不会有依赖关系。也就是,中国地区的PDF文件格式,可以依赖中国地区的其他分类的文件格式,而不会依赖其他国家的文件格式。

有关产品分类和产品族,本文的后面,有一个非常经典的案例,就是JDK的Toolkit模块里面的抽象工厂,可以进一步帮助大家理解产品族和产品分类的概念。

概念比较

工厂方法模式:

定义一个用于创建对象的接口,让子类决定实例化哪一个类。Factory Method 使一个类的实例化延迟到其子类。

——Gof《设计模式:可复用面向对象软件的基础》

抽象工厂模式:

提供一个创建一系列相关或者相互依赖对象的接口,而无需指定他们的具体类

——Gof《设计模式:可复用面向对象软件的基础》

从概念上看,工厂方法模式强调的重点是,“子类决定实例化哪一个类的对象”。也就是抽象工厂中定义的工厂方法,返回的是抽象产品,而实际上要返回抽象产品对应的哪个具体产品(每个抽象产品,一般都对应多个具体产品),则由抽象工厂的子类,也就是具体工厂来决定。

抽象工厂模式,则强调的是,抽象工厂提供的工厂方法(往往是多个工厂方法),可以创建“一系列相关或者相互依赖的对象”(具有产品族的多个分类的产品体系)。

那么当架构师根据上面的概念,设计出软件模块,应用了上面的设计模式之后,我们仅仅从源代码,还能不能逆向识别出,是哪个设计模式呢,我们可以从以下角度分析,思考。

1,子类的角度:这两个设计模式,其实都有接口和抽象类,都有具体类,仅仅从子类的角度,不太好识别是哪个设计模式。

2,产品的角度:工厂方法模式,在实践中,创建的产品,或者叫对象,比较简单,通常只有一个分类。当需要创建多个分类时,就需要多建立几个相互独立的工厂方法模式即可。但是,当产品不光有多个分类,而且产品族的概念出现时,抽象工厂模式就会应运而生。所以,如果从产品角度判断,如果只有一个类型的产品,大概率是工厂方法模式,反之,如果有多个类型的产品,而且这些不同分类的产品,还有一个相同的维度,也就是产品族,那么就是抽象工厂模式了。

基于REIS分析模式比较

根据REIS分析模型,对工厂方法模式进行分析,工厂方法模式包含五种角色,抽象工厂角色,具体工厂角色,抽象产品角色,具体产品角色,客户方角色。它的宗旨是,在抽象工厂的接口和抽象类里面,定义工厂方法,创建抽象产品。而将创建具体产品的操作,延迟到具体工厂的工厂方法中。它的目的是解除框架在创建对象时,对具体类依赖,实现了两者的解耦和。

根据REIS分析模型,对抽象工厂模式进行分析,抽象工厂模式和工厂方法模式结构有些相似,也包含五种角色,分别是抽象工厂角色,具体工厂角色,抽象产品角色,具体产品角色,客户方角色。它通过抽象工厂定义一系列的工厂方法,每个工厂方法生产一类抽象产品。它的宗旨是,通过每个具体工厂,负责生产一系列相关或者相互依赖的产品。

我们从下面两个角度,尝试思考两者的区别。

1,角色角度:这两个设计模式,都包含5个相同的角色,这导致他们的代码结构,往往很相似,很难识别。但是还是有一些细微的不同之处,就是工厂里面定义的工厂方法的个数。工厂方法模式,它里面往往只有一个工厂方法。而抽象工厂方法,则往往包含多个工厂方法。那么是否能仅仅根据工厂方法的个数来识别这两个设计模式呢,这个问题,我们后面专门讨论一下。

2,具体工厂角度:工厂方法模式中的具体工厂,它里面的工厂方法,决定了应该创建哪个具体产品。抽象工厂里面的具体工厂,每个具体工厂,往往包含多个工厂方法,而且所有的工厂方法,创建的具体产品,应该属于同一个产品族。

工厂方法个数的困惑

我们是否可以仅仅根据工厂里面的工厂方法的个数,来精确区分工厂方法模式和抽象工厂模式。

结论先行,结论是不严谨,但可以使用。

工厂方法模式,它里面的抽象工厂里面往往只有一个工厂方法,只创建一个分类的产品。如果工厂方法模式的工厂里面,虽然定义了多个工厂方法,但是这些工厂方法创建的产品,之间没有必然关系,也就是没有产品族的概念出现时,也属于工厂方法模式,这种情况一般很少,可以建立多个独立的工厂方法模式来解决。

抽象工厂模式,它里面的抽象工厂里面,往往会定义多个工厂方法,每个工厂方法负责创建一个类型的产品。每个具体工厂,负责创建多个不同的具体产品,这些不同分类的产品之间,有比较强烈的依赖关系,也就是有产品族的概念出现。理论上也存在这种情况,抽象工厂里面只有一个工厂方法的情景,但是业务上,产品族的概念又比较强烈,实践中几乎没有。

有关工厂方法模式和抽象工厂模式的比较,我们就分析到这里。下面我们看看开源软件里面的抽象工厂模式。

抽象工厂模式的查询技巧

抽象工厂模式,和我们前面分享的工厂方法一样,类的命名比较规范,一般也是以factory结尾。但是,当我们根据这个关键词,查询出结果后,大部分是工厂方法模式,也可能是静态工厂方法模式,而抽象工厂模式,由于其复杂性,所以非常稀少,在一般的项目中,很难被应用,所以要找它的案例,只能是可遇而不可求。需要从一堆查询结果中,去逐个的筛选。

通过这种笨功夫,加上相关资料的辅助,幸不辱命,我还真找到了几个,比较标准的应用抽象工厂模式的开源软件。我们从JDK开始。

JDK

BorderFactory(这里要浪费大家的感情了)

repo:^github\.com/openjdk/jdk$ file:^src/java\.desktop/share/classes/javax/swing/BorderFactory\.java

在一开始,在JDK中,我把找到抽象工厂模式的希望,放在了javax.swing模块,这个Java创始人James Gosling 曾经参与过的模块。原因是swing里面的组件,可能会受到操作系统的影响,需要区分不同的操作系统。一开始,遇到的就是BorderFactory类。

产品很复杂

BorderFactory类,用于创建组件的边框,支持创建的边框种类也很多,这与抽象工厂可以创建复杂的产品体系不谋而合。如下,

UML类图:

BorderFactory里面有一堆create...()方法,用于创建各种类型的Border对象

代码片段

从我截取的BorderFactory类的部分代码,我们可以看到,它里面创建Border对象的create...()方法,竟然是静态的方法。也就意味着,这个类,其实用的就是最简单的工厂模式——静态工厂方法模式。

public class BorderFactory
{

    /** Don't let anyone instantiate this class */
    private BorderFactory() {
    }


//// LineBorder ///////////////////////////////////////////////////////////////
    /**
     * Creates a line border with the specified color.
     *
     * @param color  a Color to use for the line
     * @return the Border object
     */
    public static Border createLineBorder(Color color) {
        return new LineBorder(color, 1);
    }

//// BevelBorder /////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
    static final Border sharedRaisedBevel = new BevelBorder(BevelBorder.RAISED);
    static final Border sharedLoweredBevel = new BevelBorder(BevelBorder.LOWERED);

    /**
     * Creates a border with a raised beveled edge, using
     * brighter shades of the component's current background color
     * for highlighting, and darker shading for shadows.
     * (In a raised border, highlights are on top and shadows
     *  are underneath.)
     *
     * @return the Border object
     */
    public static Border createRaisedBevelBorder() {
        return createSharedBevel(BevelBorder.RAISED);
    }


//// SoftBevelBorder ///////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////

    private static Border sharedSoftRaisedBevel;
    private static Border sharedSoftLoweredBevel;

    /**
     * Creates a beveled border with a raised edge and softened corners,
     * using brighter shades of the component's current background color
     * for highlighting, and darker shading for shadows.
     * In a raised border, highlights are on top and shadows are underneath.
     *
     * @return the {@code Border} object
     *
     * @since 1.7
     */
    public static Border createRaisedSoftBevelBorder() {
        if (sharedSoftRaisedBevel == null) {
            sharedSoftRaisedBevel = new SoftBevelBorder(BevelBorder.RAISED);
        }
        return sharedSoftRaisedBevel;
    }

 

//// EtchedBorder ///////////////////////////////////////////////////////////

    static final Border sharedEtchedBorder = new EtchedBorder();
    private static Border sharedRaisedEtchedBorder;

    /**
     * Creates a border with an "etched" look using
     * the component's current background color for
     * highlighting and shading.
     *
     * @return the Border object
     */
    public static Border createEtchedBorder()    {
        return sharedEtchedBorder;
    }

 
}

总结:

BorderFactory类,可以创建多种类型的Border对象,竟然用的是静态工厂方法模式。

什么情况?码农老吴,你是不是在浪费大家的感情呢,我们这节课的主题是抽象工厂模式啊。

非也,非也,之所以要加入这个小插曲,就是要告诉大家一个道理,最简单的工厂模式——抽象工厂方法模式,用好了,也是可以创建出复杂的产品体系的,大家不要因为它简单,就轻视它。

小插曲结束了,我们开始吃大餐。

在启动调查之前,由于抽象工厂模式比较复杂,我一直怀疑是否有开源软件中使用过它。经过一番调查,还真在JDK中,找到一个模块,使用了抽象工厂模式,而且非常标准,堪称抽象工厂模式的经典案例。它就是JDK中的Toolkit模块,我有点迫不及待了。

Toolkit模块(经典的抽象工厂模式)

抽象工厂模式,英文名称大家都知道,就是Abstract Factory Pattern,而大多数人不知道的是,它还有另外一个英文名称,就是Kit Pattern,所以抽象工厂模式,也可以叫Kit模式,或者叫工具箱模式。而我们要研究的这个模块,名字是ToolKit,不知道是巧合,还是谁抄的谁。

我们先看一下Toolkit模块的使用案例,熟悉一下它的功能,再分析它里面的抽象工厂模式。

类图:

从ToolKit的类图中,我们可以看出来,可以用它来获取和系统硬件有关的信息,比如屏幕分辨率,屏幕大小,操作系统的剪贴板等等,以及查询图片,创建图片等操作。

Toolkit代码片段

从Toolkit的代码片段,我们可以看出,这个类是一个抽象类,抽象类是不能直接获取对象的,而获取这个类的对象,不是通过常用的new关键字,而是通过它里面的静态方法getDefaultToolkit()。

我们看后面的使用案例。

public abstract class Toolkit {

    /**
     * Constructs a {@code Toolkit}.
     */
    protected Toolkit() {}
   
    
    public abstract Dimension getScreenSize()
        throws HeadlessException;

    
    public abstract int getScreenResolution()
        throws HeadlessException;

    
    public static synchronized Toolkit getDefaultToolkit() {
        if (toolkit == null) {
            toolkit = PlatformGraphicsInfo.createToolkit();
            if (GraphicsEnvironment.isHeadless() &&
                !(toolkit instanceof HeadlessToolkit)) {
                toolkit = new HeadlessToolkit(toolkit);
            }
            if (!GraphicsEnvironment.isHeadless()) {
                loadAssistiveTechnologies();
            }
        }
        return toolkit;
    }

    
    public abstract Image getImage(String filename);

   
    public abstract Image createImage(String filename);
    
}

使用案例:

通过Toolkit获取屏幕的大小。

Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
double width = screenSize.getWidth();
double height = screenSize.getHeight();

Toolkit的三个子类:

Toolkit是用来获取硬件信息的,而硬件信息的获取,离不开操作系统,所以,Toolkit对于不同的操作系统,有相应的子类。

如下图所示:Toolkit的三个子类,都执行了同一个接口ComponentFactory,它就是我们要重点研究的,它就是抽象工厂模式中的抽象工厂。而这三个子类,就是抽象工厂模式中的具体工厂。

XToolkit:Unix或者类Unix系统

LWCToolkit:苹果系统

WToolkit:windows系统

抽象工厂:ComponentFactory

repo:^github\.com/openjdk/jdk$ file:^src/java\.desktop/share/classes/sun/awt/ComponentFactory\.java

它里面提供了一系列的create...()方法,用于创建不同的组件,也就是多种类型的产品对象。

产品体系

ComponentFactory可以创建多种类型的产品。我们挑其中一个看看,就可以了。就以ButtonPeer为例。

抽象产品:ButtonPeer

具体产品:

XButtonPeer:Unix或者类Unix系统

LWButtonPeer:苹果系统

WButtonPeer:windows系统

产品族:

Unix或者类Unix系统、苹果系统、windows系统。

这个案例,是目前能找到的,比较权威的,非常标准的,也容易让大家接受的抽象工厂模式真实案例。

Toolkit模式里面的抽象工厂模式一览

抽象工厂:ComponentFactory,SunToolkit

工厂方法:ComponentFactory它里面定义的一系列的create...()方法。

具体工厂:XToolkit、LWCToolkit、WToolkit

产品:

有很多,如:

抽象产品:ButtonPeer

具体产品:XButtonPeer、LWButtonPeer、WButtonPeer

产品族:Unix或者类Unix系统、苹果系统、windows系统。

StackOverFlow 上公认的抽象工厂模式案例

国外的Stack OverFlow网站,号称全球程序员的知乎,它里面有个帖子,是有关JDK中的设计模式的汇总,有个网友的回帖,获得了3469人的点赞,答案很全面,有一定的参考价值,但是是否准确,还有待我们研究。如下图:

对于抽象工厂模式,这位网友给了三个类,我今天就给大家分析一下前面两个,第三个大家自行分析,答案写在评论区。

javax.xml.parsers.DocumentBuilderFactory#newInstance()

javax.xml.transform.TransformerFactory#newInstance()

javax.xml.xpath.XPathFactory#newInstance()

DocumentBuilderFactory

repo:^github\.com/openjdk/jdk$ file:^src/java\.xml/share/classes/javax/xml/parsers/DocumentBuilderFactory\.java

这个类,是JDK里面提供的,用于创建DOM模式的XML解析器的,用于对XML文件内容进行解析。

这个类里面,是否有抽象工厂模式,网上的呼声很高,好多人说它里面使用了抽象工厂模式,真是这样吗?我们一步一步分析。先看它的使用案例,大家先了解一下这个类。

使用案例

1、DocumentBuilderFactory 这个抽象类通过newInstance()静态方法,获取到自己的对象(这种方式很常见,这个方法,就是静态工厂方法)。

2、调用DocumentBuilderFactory对象的实例方法,获取到DocumentBuilder类的对象。

3、调用DocumentBuilder类的parse()方法,可以将输入的xml文件,转化为Document对象。

类图:

DocumentBuilder

类图:

DocumentBuilder类,从名字上看,符合建造者模式的命名规范,但是这个类里面,并没有出现建造者模式的一些常见特征(静态内部类,链式风格等),它里面最重要的方法,就是parse()方法,返回Document对象。所以这个类,又有点像普通的工厂方法模式。但是如果说它是比较简单的建造者模式,也有一定道理。根据名字上看,原作者应该更倾向于建造者模式。我个人的观点是,目前这个类虽然更像工厂方法模式,但是也可以看作是初期的建造者模式,随着代码的发展,建造者模式的特征会越来越明显。

不过,不管把它认定为建造者模式,还是工厂方法模式,都不会对我们最终的结果,产生影响。我们的目标是判断DocumentBuilderFactory里面是否有抽象工厂模式。

到底有没有抽象工厂?

我的结论是:没有。

原因是,产品体系太过简单,只有一个产品分类,也没有产品族出现。

DocumentBuilderFactory,只能创建DocumentBuilder类的对象。也就是只能创建一个分类的产品,另外,更重要的一点,就是这里面并没有体现产品族的思想。

结论:

DocumentBuilderFactory:本身是个工厂方法模式,创建的产品对象是DocumentBuilder。

DocumentBuilder:可以说是一个简单的建造者模式(虽然还不太标准),创建的产品对象是Document。当然,说它是工厂方法模式,也有一定道理。

这里面没有抽象工厂模式的影子,当然,也可能咱们不是XML解析器的专家,分析的可能不精确。你有什么看法,欢迎评论区留言。

TransformerFactory

repo:^github\.com/openjdk/jdk$ file:^src/java\.xml/share/classes/javax/xml/transform/TransformerFactory\.java

TransformerFactory,这个类的作用,和DocumentBuilderFactory恰恰相反,是把Document对象,转化为xml文件。

使用案例如下:

UML类图

是否有抽象工厂模式?

有,总结如下。

TransformerFactory里面的抽象工厂模式一览

抽象工厂:TransformerFactory,SAXTransformerFactory

工厂方法:newTransformer(), newTemplates()

具体工厂:SmartTransformerFactoryImpl, TransformerFactoryImpl

抽象产品及具体产品:Transformer及其实现类,Templates及其实现类

产品族:

结论:

从类图上可以看出,TransformerFactory比较符合抽象工厂模式,里面有两个产品分类,而且具体工厂的建立,里面也体现了产品族的思想。其实要想理解的更透一些,还是要对它里面的业务有一定的了解。

XPathFactory

repo:^github\.com/openjdk/jdk$ file:^src/java\.xml/share/classes/javax/xml/xpath/XPathFactory\.java

这个类里面是否有抽象工厂模式,如果没有,是否有其他设计模式,大家自己分析一下,欢迎评论区留言。

后续规划

至此,《源码说》有关6种创建型设计模式(单例模式,原型模式,建造者模式,静态工厂方法模式,工厂方法模式,抽象工厂模式)在开源软件中的落地情况,全部分享完毕。不少网友,已经急不可待,私信催促我赶紧分享后面的设计模式,其实我自己也一样,迫不及待了。

下周开始,我们开始进行行为型设计模式的分享,关于行为型模式,我已经分享了三个,分别是

模板模式,状态模式和策略模式,下周我们分享大名鼎鼎的命令模式。

极客架构师,专注架构师成长,我们下周见。

版权声明:我们致力于保护作者版权,注重分享,被刊用文章【java开发案例(JDK源代码中)】因无法核实真实出处,未能及时与作者取得联系,或有版权异议的,请联系管理员,我们会立即处理! 部分文章是来自自研大数据AI进行生成,内容摘自(百度百科,百度知道,头条百科,中国民法典,刑法,牛津词典,新华词典,汉语词典,国家院校,科普平台)等数据,内容仅供学习参考,不准确地方联系删除处理!;

原文链接:https://www.yxiso.com/zhishi/2078319.html

标签:java开发案例

发表评论:

关于我们
院校搜的目标不仅是为用户提供数据和信息,更是成为每一位学子梦想实现的桥梁。我们相信,通过准确的信息与专业的指导,每一位学子都能找到属于自己的教育之路,迈向成功的未来。助力每一个梦想,实现更美好的未来!
联系方式
电话:
地址:广东省中山市
Email:beimuxi@protonmail.com

Copyright © 2022 院校搜 Inc. 保留所有权利。 Powered by BEIMUCMS 3.0.3

页面耗时0.1748秒, 内存占用1.98 MB, 访问数据库24次

陕ICP备14005772号-15