在层次结构中为每个 class 创建一个对象实例
Create one object instance per class in hierarchy
我在面试中被问到这个问题。
有 3 class 个 A
、B extends A
和 C extends B
。我们必须设计这些 classes 符合这些约束
- 客户端只能使用带有
new
关键字的默认构造函数来实例化 A
的一个实例、B
的一个实例和 C
的一个实例。
- 尝试创建其中任何一个的另一个实例 class 将导致异常。
- class 的设计者必须强制执行以上 2 条规则,以便客户端将隐含地体验以上规则(即,客户端不应该有责任遵守以上规则)。
我建议使用 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?
由于 A
由 B
和 C
扩展,您可以在 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: ...
}
}
我在面试中被问到这个问题。
有 3 class 个 A
、B extends A
和 C extends B
。我们必须设计这些 classes 符合这些约束
- 客户端只能使用带有
new
关键字的默认构造函数来实例化A
的一个实例、B
的一个实例和C
的一个实例。 - 尝试创建其中任何一个的另一个实例 class 将导致异常。
- class 的设计者必须强制执行以上 2 条规则,以便客户端将隐含地体验以上规则(即,客户端不应该有责任遵守以上规则)。
我建议使用 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?
由于 A
由 B
和 C
扩展,您可以在 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: ...
}
}