【问题标题】:Force Singleton Pattern on a Class implementing an Interface在实现接口的类上强制使用单例模式
【发布时间】:2009-07-30 12:30:13
【问题描述】:

我最好用一个例子来解释这个问题。 我有一个可用于访问数据的接口模型。 Model 可以有不同的实现,可以表示各种格式的数据,例如 XMl、txt 格式等。Model 不关心格式。 让我们说一个这样的实现是 myxmlModel

现在我想强制 myxmlModelModel 的所有其他实现遵循 单例模式。通常的方法是使 myxmlModels 构造函数私有并提供静态工厂方法来返回 myModel 类的实例。但问题是 接口 不能有静态方法定义,结果我无法强制执行特定的工厂方法定义模型的所有实现。因此,一种实现可能以提供 getObject() 结束,而另一种可能具有 getNewModel()..

一种解决方法是允许包访问 myxmlModel 的 构造函数并创建一个工厂类,该类创建 myxmlModel 对象并将其缓存以供进一步使用。

我想知道是否有更好的方法来实现相同的功能。

【问题讨论】:

    标签: java interface singleton design-patterns factory


    【解决方案1】:
    1. 做一个返回的工厂 您的接口 Model 的实例。
    2. 使模型包私有类的所有具体实现 与您的工厂在同一个包装中。
    3. 如果您的模型是单例,并且您使用的是 java 5+,用枚举代替传统的 单例,因为它更安全。
    public enum MyXMLModel{  
    INSTANCE();  
    //rest of class  
    };
    

    编辑: 另一种可能性是创建完成所有工作的委托类,然后使用枚举来提供所有模型选项。

    例如:

    class MyXMLModelDelegate implements Model {
    public void foo() { /*does foo*/}
    ...
    }
    
    class MyJSONModelDelegate implements Model {
    public void foo() { /*does foo*/ }
    ...
    }
    
    public enum Models {
    XML(new MyXMLModelDelgate()),
    JSON(new MyJSONModelDelegate());
    
    private Model delegate;
    public Models(Model delegate) { this.delegate=delegate; }
    
    public void foo() { delegate.foo(); }
    }
    

    【讨论】:

    • Factory 很好。但是我很好奇如果没有 Factory 也能实现相同的功能。
    • 我正在研究 Eclipse。模型接口是扩展点的契约。模型的实现是扩展。所以我需要暴露 myxmlModel。如果我暴露了一些 Factory 类,那么由于它没有实现 Model,Contract 将失败。
    【解决方案2】:

    您可以使用反射。像这样的:

    public interface Model {
      class Singleton {
        public static Model instance(Class<? extends Model> modelClass) {
          try {
            return (Model)modelClass.getField("instance").get(null);
         } catch (blah-blah) {
           blah-blah
         }
      }
    }
    
    public class XmlModel implements Model {
      private static final Model instance = new XmlModel();
    
      private XmlModel() {
      }
    }
    

    用法:

    Model.Singleton.instance(XmlModel.class)
    

    实际上,我不太喜欢这段代码 :)。首先,它使用反射 - 非常慢,其次 - 如果类定义错误,可能会出现运行时错误。

    【讨论】:

    • 我认为反射会起作用。
    【解决方案3】:

    你能把接口重构为抽象类吗?这将允许您将特定的工厂方法强制到所有实现类。

    【讨论】:

    • 多态在静态方法的情况下不起作用。看看你是否在抽象类中有一个静态方法并在子类中覆盖它,那么如果你尝试访问该方法,那么原始方法将被调用而不是子类的方法。
    【解决方案4】:

    我曾经问过自己同样的问题。我提出了相同的答案;-)

    现在我通常放弃“强制”行为,我依赖文档。 我没有发现 Singleton 方面如此引人注目以至于需要通过各种方式强制执行的情况。 这只是该项目的“最佳实践”。

    我通常使用 Spring 来实例化这样的对象, 正是 Spring 配置使其成为单例。 安全,如此简单......加上额外的 Spring 优势(例如代理,一次替换不同的对象以进行一些测试等......)

    【讨论】:

      【解决方案5】:

      这更多是对您对 kts 答案的评论/澄清的答案。是不是这样,真正的问题不是使用单例模式,而是定义一个允许贡献单例的 Eclipse(Equinox)扩展点模式?

      我认为,这是不可能的,因为每次调用 IConfigurationElement.createExecutableExtension 时都会创建一个新实例。这与您的单例要求完全不兼容。因此,您需要公共默认构造函数,以便每个人都可以创建实例。

      除非您可以更改扩展点定义,以便插件贡献 ModelFactory 而不是模型,例如

      public interface ModelFactory {
      
        public Model getModelInstance();
      
      }
      

      所以扩展用户将实例化一个 ModelFactory 并使用它来获取单例。

      如果我猜错了,请发表评论,我会删除答案;)

      【讨论】:

      • 是的,你是对的。但是如果我公开工厂类,那么合同(即实现模型接口)将失败。 IConfigurationElement 中还有许多其他方法,我们可以使用它们来获取有关扩展的更多信息,也许可以获取类名并在其上调用静态方法来获取对象。但是话又说回来了。我们不能在模型接口中有静态声明。
      • 不完全是 - 合同只是移动到 ModelFactory 类。现在您将拥有两个合同:工厂(通过扩展点贡献)和模型(工厂必须返回模型实现)
      猜你喜欢
      • 2022-01-12
      • 2015-10-23
      • 1970-01-01
      • 1970-01-01
      • 2015-05-08
      • 2021-10-18
      • 1970-01-01
      • 1970-01-01
      • 2013-02-19
      相关资源
      最近更新 更多