在层次结构中为每个 class 创建一个对象实例

Create one object instance per class in hierarchy

我在面试中被问到这个问题。

有 3 class 个 AB extends AC extends B。我们必须设计这些 classes 符合这些约束

我建议使用 static Map<Class, Object> 的方法。所以例如当有人呼叫 new B() 时,它会检查是否 map.contains(B.class)。如果是,则抛出异常,如果不是,则将实例保存在地图中,然后创建对象。

但下一个问题是 我如何在每个 class 上实施这种方法?按照我的方法,每个构造函数都必须小心地填充地图,否则它将打破约束。

我该如何解决这个问题?

But the next question was how would I enforce this approach on each class? As per my approach each constructor would have to carefully populate the map otherwise it will break the constraint.

只需将该地图及其周围的代码放入一些不同的实用程序中即可 class。这样您的每个 classes 都可以做类似的事情:

public WhateverClassConstructor() {
  synchroized(someLock) {
     SingeltonChecker.ensureUnique(this);

 public static void ensureUnique(Object objectToAdd) {
   Class<?> classToAdd = o.getClass();
   ...

鉴于 C extends B extends A 这一事实,您可能只需要在 class A 的构造函数中调用该调用。另一方面,假设您对 new C() 的第一次调用导致C-constructor 的异常,但第二次调用不会。当然,这本身就是一个问题,但问题仍然存在:如何确保对象在添加到这样的映射之前已完全正确地初始化?!

因此:在编写此类实用程序代码时需要考虑 的事情。因此,我的回答将集中在给定设计点的不切实际的,几乎 stupid-ness 上,并建议将其丢弃并寻求其他解决方案,例如像这样简单的东西:

public enum SingletonObjectHolder {
  private final Object hold;
  A_INSTANCE(new A()), B_INSTANCE(new B())...

  public Object getSingleton() { return hold; }
  private SingletonObjectHolder(Object o) { hold = o };

不要浪费太多时间试图给人们一个技术性的答案,当真正的点是不是将自己射入脚。别搞错了:让 map-based "singleton" 方法稳健且正确地工作,对于各种情况,认为 真的很难

换句话说:如果我在采访中问你这个问题,我想听到一个挑战那个可怕的设计点的答案。当然,花 10% 的时间为给定场景概述解决方案。但要花 90% 的时间来解释为什么它如此糟糕,以及为什么其他解决方案会好得多。

how would I enforce this approach on each class?

由于 ABC 扩展,您可以在 A 构造函数中添加检查,因此每次 A、B 或 C 是创建它会检查它是否已经存在,否则会抛出异常,例如:

public static class A{
    static List<Class<? extends A>> createdClasses = new ArrayList<>();
    public A() {
        if (createdClasses.contains(getClass())) {
            throw new InvalidParameterException("Class already present!");
        }else{
            createdClasses.add(getClass());
        }
    }
}

通过这种方式,如果您要创建第二个 C 实例,您将得到一个异常

 A testA = new A();
 B testB = new B();
 C testC = new C();
 C testC2 = new C(); //Exception here

这种模式没有多大意义,但您可以在这里使用默认构造函数中的隐式 super() 调用来发挥您的优势。

鉴于:

class A{
        static final Set<Class<?>> classes = new HashSet<>();
        public A() {
            if (!classes.add(getClass())) 
               throw new IllegalStateException(
                  "One instance of " + getClass() + " already created."
               );
        }
    }
class B extends A{}
class C extends B{}

还有某处...

new A();
new B();
new C();
new C();

最后一次调用将抛出异常,因为隐式 C 构造函数调用 super()

下一步是 thread-safe,您可以通过多种方式完成。

您可以创建一组 classes,然后在 class 的构造函数中创建 检查 this.getClass() 是否已经创建。它不然后添加它,否则抛出异常。 下面是简单的例子:

static class A {
    private static Set< Class< ? > > createdClasses = new HashSet<>();
    {
        if( !createdClasses.add( this.getClass() ) ) {
            throw new RuntimeException( "Cannot create 2nd class" );
        }
    }
}
static class B extends A {}
static class C extends B {}

注意:所有扩展 A 的 class 都会有这种行为!

静态状态可能是这样工作的:

import java.util.HashSet;
import java.util.Set;

class Aa {
    private static Set<Class> set = new HashSet();

    static void validateAndAdd(Class c) {
        if (set.contains(c) ) {
            throw new IllegalStateException("...");
        }
        set.add(c);
        System.out.println(set);
    }

    public Aa() {
        validateAndAdd(getClass());
    }
}

class Bb extends Aa {
}

class Cc extends Bb {
}

public class Main {
    public static void main(String[] args) throws Exception {
        new Cc(); // [class Cc]
        new Aa(); // [class Cc, class Aa]
        new Bb(); // [class Bb, class Cc, class Aa]
        new Bb(); // IllegalStateException: ...
    }
}