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

时间:2019-05-27 14:34:25

标签: java oop class-design

在一次采访中有人问我这个问题。

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

  • 客户端只能使用其默认构造函数和A实例化B的一个实例,C的一个实例和new的一个实例。
  • 尝试创建任何此类的另一个实例将导致异常。
  • 该类的设计者必须强制执行上述2条规则,这样客户才能隐式地体验上述规则(即,不应该由客户负责遵守上述规则)。

我建议使用static Map<Class, Object>的方法。所以例如当有人打电话给new B()时,它将检查map.contains(B.class)是否。如果是,则引发异常;如果不是,则将实例保存在map中,并创建对象。

但是下一个问题是我将如何在每个班级强制采用这种方法?按照我的方法,每个构造函数都必须仔细填充地图,否则它将打破约束。

我该如何解决这个问题?

5 个答案:

答案 0 :(得分:7)

  

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

只需将地图及其周围的代码放入一些不同的Utility类中。这样您的每个类都可以执行以下操作:

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

使用

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

鉴于C扩展了B扩展了A的事实,您可能只需要在类A的构造函数中进行该调用。另一方面,想象一下您对new C()的首次调用会导致C-发生异常构造函数,但第二个调用不会。当然,这本身就是一个问题,但问题仍然存在:在将对象添加到此类地图之前,如何确保对其进行了完全正确的初始化?!

因此:在编写此类实用程序代码时,需要考虑很多事情。因此,我的回答将集中于给定设计点的不切实际,几乎愚蠢的问题,并建议将其丢弃并寻求其他解决方案,例如简单的事情:

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 };

真实重点是 not 射杀自己的脚时,请不要浪费太多时间尝试给人们提供技术答案。毫无疑问:让基于地图的“单人”方法健壮且正确地工作,对于各种上下文,请考虑真的很难

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

答案 1 :(得分:2)

这种模式没有多大意义,但是您可以在默认构造函数中使用隐式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()

使线程安全是下一步,您可以通过多种方式进行操作。

答案 2 :(得分:1)

  

我将如何在每个课程上实施这种方法?

由于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

答案 3 :(得分:1)

您可以创建然后在类A的构造函数中创建的一组类,检查是否已创建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的类都将具有这种行为!

答案 4 :(得分:1)

静态状态可能如下所示:

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: ...
    }
}