【问题标题】:Does Class.newInstance() follow the "Abstract factory" design pattern?Class.newInstance() 是否遵循“抽象工厂”设计模式?
【发布时间】:2012-09-04 19:28:23
【问题描述】:

我已经开始阅读 Joshua Bloch 的“Effective Java”(第二版)。在阅读第 2 条时(在面对许多构造函数参数时考虑构建器),有一个特殊的语句,作者使用 Class.newInstance() 方法。具体来说,作者说

Java 中的传统抽象工厂实现是“Class”对象,“newInstance”方法扮演“build”方法的角色。

这部分让我有点困惑——我对抽象工厂设计模式的理解是它用于表示工厂的工厂。 Class.newInstance() 方法,在我看来,更接近于“静态工厂方法”编码哲学(顺便说一下,这是同一本书中的第 1 项)

想法,有人吗?我一直在努力准备应对一些艰难的面试,如果我在参加此类面试之前基础扎实,我将不胜感激。

谢谢。

【问题讨论】:

    标签: java reflection builder java.lang.class


    【解决方案1】:

    这是我的看法。

    首先,抽象工厂模式并非旨在成为工厂的工厂。这种模式的关键方面是有一个可访问的接口和一个底层的(可能不可访问的)工厂实现,通过它您可以获得(可能不可访问的)对象实现的可访问接口。我知道,这是一个关于我如何理解 Gamma 书中这种模式的一些适用条件的冗长讨厌的文字游戏:

    • 系统应独立于其产品的创建、组合和表示方式
    • 您想提供一个产品类库,并且只想显示它们的接口,而不是它们的实现。

    最后你得到的是对象,而不是工厂。

    其次,我不会在模式概念和语言关键字之间建立一对一的关系。 “抽象工厂”不一定总是转换为 Java abstract classinterface 构造。只要您以某种方式保证客户端代码独立于底层工厂和对象实现,您仍然可以拥有一个表示“抽象工厂”的常规、可扩展、可实例化的类。 java.lang.Class 就是这种情况,它既不是抽象的也不是接口,而是通过newInstance() 方法隐藏它所表示的类型的无参数构造函数实现。如果您像这样使用它可能会更清楚:

    Object o = Class.forName(type).newInstance();
    

    Class 播放“抽象工厂”,Object 播放“抽象产品”到类型实现。

    最后,newInstance() 不是静态工厂方法,我认为是因为这种模式旨在返回实现它的类的实例。 newInstance() 不返回 Class 或 sub-Classes 的实例。它返回它所代表的类型的实例。它也不是 Bloch 在他的书中所说的“工厂方法”。

    【讨论】:

    • 宾果游戏!这就是我一直在寻找的。感谢您的详细解释。
    • [Class] 的工作是隐藏它所代表的类型的无参数构造函数实现。 我完全不同意,newInstance() 什么也没隐藏。要直接调用构造函数,您需要知道存在一个无参数、可见的构造函数。要使用newInstance() 进行实例化,您需要知道存在一个无参数、可见的构造函数。 newInstance() 的行为是明确指定的,所以它真的不能封装任何后果。在这两种情况下,您都不需要了解构造函数本身的实际实现
    • 马克,我同意你的说法,Class.newInstance() 没有隐藏任何东西,即此方法的调用者必须确保指定的类实例存在无参数构造函数。将相同的思路应用于工厂模式,如果客户端获得对工厂实现的引用(例如,通过注入)并在其上调用方法,客户端不应该确定注入的工厂实例不是简单地实现所有工厂接口方法都抛出UnsupportedException?在我看来,很少有什么是真正透明的。
    【解决方案2】:

    我不认为抽象工厂是“工厂的工厂”。 AbstractFactory<T> 不会创建创建 Ts 的工厂,而是直接创建 Ts。

    它是抽象的想法是允许创建T 的逻辑被注入。例如,您可以:

    public interface ConnectionFactory {
        Connection newConnection();
    }
    
    //passed to your object normally:
    public class RealConnectionFactory implements ConnectionFactory {
        //...
    }
    
    //passed to your object when unit testing:
    public class FakeConnectionFactory implements ConnectionFactory {
        //...
    }
    
    //...
    
    public class MyDao {
       public MyDao(ConnectionFactory connectionFactory) {
           this.conn = connectionFactory.newConnection();
       }
    }
    

    在这种情况下,ConnectionFactory 创建了Connections,但它是抽象的,因为它是一个接口。

    我倾向于同意你的观点,Class<?>.newInstance() 不是抽象工厂的典型例子,因为Class 不是抽象的,事实上它无法扩展。您不能要求 Class<Integer> 并让一个实现将新值初始化为 1,而另一个实现将新值初始化为 7

    但是,您可以通过说 Class<? extends InputStream>InputStreams 的抽象工厂,具体实现 Class<SocketInputStream>Class<FileInputStream> 来扩展事情。这不是“抽象”的传统含义(仍然只有一个类:Class)。

    但即便如此,它作为抽象工厂也没有用,因为实现“工厂”的新具体版本的方式是创建一个扩展InputStream 的新类。这几乎不是抽象工厂的用途。

    【讨论】:

    • 嗯,从技术上讲,您可以致电Integer.class.getConstructor(int.class).newInstance(1);,但这并不方便。
    • @Duncan:可以,但这与抽象工厂无关。抽象工厂的要点是您调用的方法由接口指定。这是相反的:你有一个具体的工厂,其中有多个工厂方法以不同的方式调用。
    • + 1 用于解释和注入示例。
    【解决方案3】:

    在我看来,他指的是如下代码:

    Integer.class.newInstance();
    

    Class<T> 是抽象工厂。当您传递类型参数时,它变得具体,例如Integer。然后你调用了“builder”newInstance()

    【讨论】:

    • 有道理。我会在接受你的回答之前等待一段时间,看看其他人对此有何看法。