Java: суперкласс для построения подкласса на определенных условиях, возможных?

У меня есть это условие

public class A {
     public action() {
         System.out.println("Action done in A");
     }
}


public class B extends A {
     public action() {
         System.out.println("Action done in B");
     }
}

когда я создам экземпляр B, действие сделает просто действия в B, поскольку это переопределяет действие суперкласса.

проблема состоит в том, что в моем проекте, суперкласс A уже используется слишком много раз, и я ищу путь, который при определенных условиях, когда я создаю экземпляр этого, осуществляет проверку и если это верно, замените себя B.

public class A {
     public A() {
         if ([condition]) {
            this = new B();
         }
     }

     public action() {
         System.out.println("Action done in A");
     }
}

A a = new A();
a.action();
// expect to see "Action done in B"...

действительно ли это возможно в некотором роде?

7
задан ramihope 5 May 2010 в 12:44
поделиться

5 ответов

Я бы сказал, что выполнение этого:

this = new B();

в конструкторе для A нарушит принципы объектно-ориентированного проектирования, даже если это возможно делать (а это не так).

При этом, если бы я столкнулся с таким сценарием:

проблема в том, что в моем проекте суперкласс A уже используется слишком много раз

, я бы решил ее одним из двух следующих способов :
Я предположил, что ваше условие состоит в том, что вам не нужно слишком много объектов типа A, в противном случае вы можете заменить их любым другим условием.

Вариант 1. Используйте заводской шаблон проектирования.

public class AFactory
{
    private static count = 0;
    private static final MAX_COUNT = 100;

    public A newObject() {
        if (count < MAX_COUNT) {
            count++;
            return new A();
        } else {
            return new B();
        }
    }
}

А еще вы генерируете объекты следующим образом:

A obj1 = factory.newObject();
A obj2 = factory.newObject();

Вариант 2: Статический счетчик + try & catch

Используйте статический счетчик в вашем классе A, который отслеживает количество раз, когда A был создан, путем увеличения статическая переменная на единицу в конструкторе. Если он достигает предела для максимального количества объектов типа A, бросить InstantiationError в конструктор A.

Это будет означать, что всякий раз, когда вы создаете экземпляр A, вам необходимо использовать блок try..catch , чтобы перехватить InstantionError , а затем создать новый объект типа B вместо этого.

public class A {

    private static count = 0;
    private static final MAX_COUNT = 100;

    public A() {
        if (count > 100) {
            throw new InstationError();
        }
    }

}

И при создании ваших объектов:

A obj1, obj2;
try {
    obj1 = new A();
} catch (InstantiationError ie) {
    obj1 = new B();
}
try {
    obj2 = new A();
} catch (InstantiationError ie) {
    obj2 = new B();
}

Вариант 2 наиболее близок к тому, что вы задаете прямо в вопросе. Однако я лично предпочел бы использовать шаблон проектирования фабрики, потому что это гораздо более элегантное решение, и оно позволяет вам в любом случае достичь того, чего вы хотите.

6
ответ дан 6 December 2019 в 10:48
поделиться

Это было бы возможно, если бы вы использовали фабричный метод . Когда вы используете конструктор: нет, совершенно невозможно.

2
ответ дан 6 December 2019 в 10:48
поделиться

Можно ли выбирать, использовать или нет конструктор суперкласса?

Невозможно условно контролировать, использовать или нет конструктор суперкласса, поскольку один из конструкторов суперкласса должен быть вызван перед созданием собственного объекта.

Из вышесказанного следует, что в Java существует требование, что первая строка конструктора должна вызывать один из конструкторов суперкласса - фактически, даже если нет явного вызова конструктора суперкласса, будет неявный вызов super():

public class X {
   public X() {
      // ...
   }

   public X(int i) {
      // ...
   }
}

public class Y extends X {
   public Y() {
     // Even if not written, there is actually a call to super() here.
     // ...
   }
}

Следует подчеркнуть, что невозможно вызвать конструктор суперкласса после выполнения чего-то другого:

public class Y extends X {
   public Y() {
     doSomething();  // Not allowed! A compiler error will occur.
     super();        // This *must* be the first line in this constructor.
   }
}

Альтернатива

Тем не менее, способом достижения желаемого здесь может быть использование паттерна фабричного метода, который может выбирать вид реализации в зависимости от некоторого условия:

public A getInstance() {
  if (condition) {
     return new B();
  } else {
     return new C();
  }
}

В приведенном выше коде, в зависимости от условия, метод может вернуть либо экземпляр B, либо C (предполагая, что оба являются подклассом класса A).

Пример

Ниже приведен конкретный пример, использующий интерфейс, а не класс.

Пусть существуют следующие интерфейсы и классы:

interface ActionPerformable {
  public void action();
}

class ActionPerformerA implements ActionPerformable {
  public void action() {
    // do something...
  }
}

class ActionPerformerB implements ActionPerformable {
  public void action() {
    // do something else...
  }
}

Тогда существует класс, который будет возвращать один из вышеуказанных классов в зависимости от условия, передаваемого через метод:

class ActionPeformerFactory {

  // Returns a class which implements the ActionPerformable interface.
  public ActionPeformable getInstance(boolean condition) {

    if (condition) {
      return new ActionPerformerA();
    } else {
      return new ActionPerformerB();
    }
  }
}

Затем класс, использующий вышеуказанный фабричный метод, который возвращает соответствующую реализацию в зависимости от условия:

class Main {
  public static void main(String[] args) {

    // Factory implementation will return ActionPerformerA
    ActionPerformable ap = ActionPerformerFactory.getInstance(true);

    // Invokes the action() method of ActionPerformable obtained from Factory.
    ap.action();    
  }
}
4
ответ дан 6 December 2019 в 10:48
поделиться

Предполагая, что вы не хотите переносить его в интерфейс или фабрику, это выглядит некрасиво, но вы могли бы сохранить делегированную копию B в A и переписать ваши методы ot вызова делегата:

public class A{
  B delegate;

  public A(){
    if([condition]){
      delegate = new B()
      return;
    }
    ...//normal init
  }

  public void foo(){
    if(delegate != null){
      delegate.foo();
      return;
    }
    ...//normal A.foo()
  }

  public boolean bar(Object wuzzle){
    if(delegate != null){
      return delegate.bar(wuzzle);
    }
    ...//normal A.bar()
  }
  ...//other methods in A
}
1
ответ дан 6 December 2019 в 10:48
поделиться

Не напрямую, нет. Вызов new A() всегда создаст экземпляр A. Однако можно сделать конструктор A защищенным, а затем иметь статический метод:

public static A newInstance() {
  // Either create A or B here.
}

Затем преобразовать все текущие вызовы конструктора в вызовы фабричного метода.

5
ответ дан 6 December 2019 в 10:48
поделиться
Другие вопросы по тегам:

Похожие вопросы: