如何设计可以选择作为Singleton运行的Java类?

时间:2016-07-22 14:09:46

标签: java inheritance interface singleton abstract

以下是该方案:

public class A {    
    public A {}
    void doSomething() {
        // do something here...
    }
}

现在,该类已设置,您可以在其中创建多个实例。但我也看到需要将类限制为只有一个实例,即Singleton类。

问题是我不确定如何完成两个目标的设计:多个实例和一个实例。在一堂课中听起来不太可能。我想我需要使用派生类,抽象类,接口,其他东西或某种组合。

我应该将A类创建为基类并创建一个作为单例类的派生类吗?

3 个答案:

答案 0 :(得分:3)

当然,首先应该始终质疑使用单身人士的必要性。但有时,它们只是解决某些问题的务实方式。

如果是这样,首先要理解的是:没有解决方案可以强制执行"您的要求并防止误用,但这是一个"模式"通过转变"意图"这有很大帮助。进入"有意义的"代码:

首先,我有一个表示功能的界面:

interface WhateverService { void foo() }

然后,我有一些想法:

class WhateverServiceImpl implements WhateverService {
  @Override
  void foo() { .... }

现在,如果我需要以单身形式存在,我会

enum WhateverServiceProvider implements WhateverService {
  INSTANCE;
  private final WhateverService impl = new WhateverServiceImpl();
  @Override
  void foo() { impl.foo() }

最后,一些客户端代码可以执行:

WhateverService service = WhateverServiceProvider.INSTANCE;
service.foo()

(但当然,你可能不想直接分配服务对象,但你可以在这里使用依赖注入)

这样的架构可以为您提供:

  1. 核心功能实施单例概念
  2. 之间的明确分离
  3. 保证单身语义(如果有一件事,那就是Java枚举真的很有用......那就是:提供防呆的单身人士!)
  4. 完全"可测试性" (你看 - 当你只使用枚举时,没有把它作为接口使用......那么你很难在客户端代码中模拟那个对象 - 因为你不能直接模拟枚举。)
  5. 更新 - 关于线程安全:

    我不确定你的意思是什么"单身概念"。

    但是我们可以这样说:当你使用这样的枚举时,保证有一个完全实例化一个INSTANCE对象,Java语言保证这一点。但是:如果有几个线程转向枚举,并且并行调用foo()......你仍然在处理围绕这些场景的所有潜在问题。所以,是的,enum"创造"是线程安全的;但是你的代码正在做什么......取决于你。那么锁定或其他任何有意义的东西。

答案 1 :(得分:2)

我想你应该看看这个问题: Can a constructor in Java be private?

这里描述的Builder模式可能有一些有趣的解决方案:

// This is the class that will be produced by the builder
   public class NameOfClassBeingCreated {
      // ...

      // This is the builder object
      public static class Builder {
        // ...

        // Each builder has at least one "setter" function for choosing the
        // various different configuration options. These setters are used
        // to choose each of the various pieces of configuration independently.
        // It is pretty typical for these setter functions to return the builder
        // object, itself, so that the invocations can be chained together as in:
        //
        //     return NameOfClassBeingCreated
        //         .newBuilder()
        //         .setOption1(option1)
        //         .setOption3(option3)
        //         .build();
        //
        // Note that any subset (or none) of these setters may actually be invoked
        // when code uses the builer to construct the object in question.
        public Builder setOption1(Option1Type option1) {
           // ...
           return this;
        }

        public Builder setOption2(Option2Type option2) {
           // ...
           return this;
        }

        // ...

        public Builder setOptionN(OptionNType optionN) {
           // ...
           return this;
        }

        // ...

        // Every builder must have a method that builds the object.
        public NameOfClassBeingCreated build() {
           // ...
        }

        // The Builder is typically not constructible directly
        // in order to force construction through "newBuilder".
        // See the documentation of "newBuilder" for an explanation.
        private Builder() {}
      }

      // Constructs an instance of the builder object. This could
      // take parameters if a subset of the parameters are required.
      // This method is used instead of using "new Builder()" to make
      // the interface for using this less awkward in the presence
      // of method chaining. E.g., doing "(new Foo.Builder()).build()"
      // is a little more awkward than "Foo.newBuilder().build()".
      public static Builder newBuilder() {
         return new Builder();
      }

      // ...

      // There is typically just one constructor for the class being
      // constructed that is private so that it may only be invoked
      // by the Builder's "build()" function. The use of the builder
      // allows for the class's actual constructor to be simplified.
      private NameOfClassBeingCreated(
          Option1Type option1,
          Option2Type option2,
          // ...
          OptionNType optionN) {
        // ...
      }
   }

链接供参考: https://www.michaelsafyan.com/tech/design/patterns/builder

答案 2 :(得分:1)

我不确定这是你在找什么,但你可以使用工厂模式。创建2个工厂,一个将始终返回相同的单例,而另一个将每次创建一个新的A对象。

Factory singletonFactory = new SingetonFactory();
Factory prototypeFactory = new PrototypeFactory();

A a = singletonFactory.createA();
A b = singletonFactory.createA();
System.out.println(a == b); // true

A c = prototypeFactory.createA();
A d = prototypeFactory.createA();
System.out.println(c == d); // false


class A {
    private A() {}
    void doSomething() { /* do something here... */}
}

interface Factory {
    A createA();
}

class SingetonFactory implements Factory {
    private final A singleton = new A();

    public A createA() {
        return singleton;
    }
}

class PrototypeFactory implements Factory {
    public A createA() {
        return new A();
    }
}