如果我需要在构造中使用方法重载,我应该如何实现单例类?

时间:2015-05-11 20:55:31

标签: java oop

我正在努力实现这个效果:

  1. 基类A的方法为getFileName()
  2. 两个派生类B和C已覆盖该方法的实现,返回特定于B和C的文件名。
  3. A类需要使用 singleton 类S的服务
    • 我希望它成为单身的原因是因为(a)我想要保证它只会被构造一次而且(b)我想要急切初始化那个类,这在app启动时发生,而不是一开始使用。
  4. 类S需要根据文件名(例如,读取该文件的内容)进行工作 - 这取决于使用哪个A子类。
  5. 这似乎是一个不可避免的难题,因为:

    如何实施此设计? (如果有更好的模式,我愿意改变设计)

3 个答案:

答案 0 :(得分:2)

...需要使用单身人士的服务......这取决于使用哪个A的子类

这意味着Singleton不是你真正的问题,它是根据类型询问获得正确的类!

您的设计与您尝试的方式紧密耦合。您需要将Service与服务的Consumers完全分离,Singleton在此练习中并不重要。

您需要的是某种形式的依赖注入。

这正是Guice创建的问题类型,它能够根据绑定中的其他类类型提供注入的类。那说......

大多数人都没有意识到Java始终通过DI支持ConstructorGuice使得硬编码更少,但它仍然是注入实例的依赖项。

Guice通过基于类类型注入正确的服务来实现这一点。但是可以在没有任何DI框架/库的情况下完成。如果您的案件使用Guice被视为重手,那么仍然可以轻松完成。

以下是没有框架/库的一种方法:

public class Solution
{
    static class Singleton
    {
        public static final Singleton INSTANCE;
        static { INSTANCE = new Singleton(); }

        private Singleton() { /* this is important */ }
        public void doWhatever(@Nonnull final B b) { /* whatever */ }
        public void doWhatever(@Nonnull final C c) { /* whatever */ }
    }

    static abstract class A
    {
        private final Singleton s;

        public A(final Singleton s) { this.s = s; }

        public abstract String getFilename();
    }

    static class B extends A
    {
        public B(final Singleton s) { super(s); }

        @Override
        public String getFilename() { /* code goes here */ }
    }

    static class C extends A
    {
        public C(final Singleton s) { super(s); }

        @Override
        public String getFilename() { /* code goes here */ }
    }
}

你提到的单身反模式只是:

Singleton模式应隐藏在Factory模式后面。你的消费者需要拥有1和只有1的人不应该关心是否有1和只有1.他们应该只关心那个对象符合某个界面的某些契约。

我的实现是一个在静态块中创建的天真Factory。大多数是在第一次使用时创建的,这不是更好。

使用Enum创建Singleton个对象是对Enum语义的误用和反模式,无法正确进行单元测试。

所有静态实用程序类方法相同,无法对其他实现进行单元测试或替换。 这两者的结合是完全可憎的,这是不可能进行单元测试和维持完整的噩梦!

如何确定Singleton工作的A的哪个子类很简单:

这就是上面的代码所示的重载。

其他任何事情做得不对instanceof失败,reflection更大失败。

可以使用重载方法,泛型或适当的设计模式来选择基于Type的逻辑。

Strategy模式可以轻松解决这个问题,并使N个子类在运行时可管理和可扩展。

答案 1 :(得分:0)

我认为你需要决定S是否使用A或A是否使用S.

如果S使用A,那么A可以是基类或接口,S将有一个接受A的实例的方法,这些方法被getfileName()的正确实现覆盖。

如果A使用S,则A应该是getFileName()的抽象,强制构造一个实现,它应该在内部调用它尚未定义的getFileName()将其作为一个传递S的论据。

单身人士是面向对象解决方案和非面向对象解决方案之间的粘合剂,因此您可以避免这个难题

  1. 将对象传递给非面向对象的单例“实用程序例程”

  2. 将已解析的参数传递给非面向对象的单例“实用程序例程”

  3. 第一项技术的示例代码

    // this could be abstract class too, as long as getName() is abstract
    public interface Nameable 
       public String getName();
    
    }
    
    public enum Utility {
    
       INSTANCE;
    
       public static deleteByName(Nameable nameable) {
          createBackup(nameable.getName());
          updateIntentLog(nameable.getName());
          removeFile(nameable.getName());
          updateAuditLog(nameable.getName());
       }
    
    }
    

    public abstract class Nameable {
    
      public abstract String getName();
    
      public void delete() {
         Utility.INSTANCE.deleteFile(getName());
      }
    
    }
    
    public enum Utility {
    
      INSTANCE;
    
      public void deleteFile(String name) {
        ...
      }
    }
    

答案 2 :(得分:-1)

您可以创建手动初始化的单例类,即具有静态实例变量,但也可以使用静态initialize()方法。如果您尝试初始化两次,则会抛出initialize。这允许您在运行时选择要使用的子类,并且还可以清除初始化顺序。