повторно использовать один и тот же класс в разных иерархиях классов - PullRequest
0 голосов
/ 29 сентября 2019

Иногда ваш интерфейс обратного вызова имеет более одного метода обратного вызова, каждый для своего рода события.Одним из примеров является org.xml.sax.ContentHandler, который имеет startElement(), characters() и т. Д.

Иногда вы хотите иметь набор многократно используемых обработчиков и объединять их в произвольном порядке.В Java это решается с помощью шаблона проектирования делегата: вы создаете абстрактный класс, который реализует каждый метод интерфейса, см .:

javax.xml.stream.util.StreamReaderDelegate
org.xml.sax.helpers.XMLFilterImpl
com.common.util.xml.DelegatingContentHandler

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

Мне было интересно, позволит ли declare parents AspectJ создавать такие цепочки статически.Я не хочу вручную создавать класс делегатора для интерфейса и не хочу создавать цепочки во время выполнения.

Вот что я сделал до сих пор: есть 3 класса: A, B, C, которые печатают свое имя и затем звонят super.Для каждой возможной пары я хотел бы создать классы: AB, AC, BC, которые имеют функции, соответствующие их паре классов.

Желаемый результат моей программы:

ASupport
BSupport
===============
BSupport
CSupport
===============
ASupport
CSupport
===============

Программаэто не компилируется:

package org.foo;

public class AnAspect {

    public static void main(String[] args) {
        new AB().handle(null);
        new BC().handle(null);
        new AC().handle(null);
    }

}

aspect AnAspect1 {
    declare parents : AB extends ASupport;

    // can only insert a class into hierarchy, but org.foo.BSupport is not a subtype of org.foo.CSupport
    declare parents : ASupport extends BSupport;
}

aspect AnAspect2 {
    declare parents : BC extends BSupport;
    declare parents : BSupport extends CSupport;
}

aspect AnAspect3 {
    declare parents : AC extends ASupport;
    declare parents : ASupport extends CSupport;
}

class BC extends MyCallback {

}

class AC extends MyCallback {

}

class AB extends MyCallback {

}

class ASupport extends MyCallback {

    @Override
    public void handle(Object o) {
        System.out.println("ASupport");
        super.handle(o);
    }

}

class BSupport extends MyCallback {

    @Override
    public void handle(Object o) {
        System.out.println("BSupport");
        super.handle(o);
    }

}

class CSupport extends MyCallback {

    @Override
    public void handle(Object o) {
        System.out.println("CSupport");
        super.handle(o);
    }

}

abstract class MyCallback {

    public void handle(Object o) {
        System.out.println("===============");
    }

}

1 Ответ

0 голосов
/ 29 сентября 2019

Конечно, вы не можете преодолеть ограничения JVM через AspectJ и заставить ASupport расширять как BSupport, так и CSupport.Каждый класс должен иметь один суперкласс (или ни одного, в этом случае он неявно наследуется от Object).Также вариант declare parents : CSupport extends ASupport; не будет работать, потому что он приведет к круговой иерархии, которая, конечно, запрещена, потому что это не имеет логического смысла.

Вы также, похоже, неправильно понимаете, как работает шаблон делегатора:

  • не работает через наследование и super() вызовы, но
  • с помощью внутреннего члена, указывающего на делегата (если есть, первого члена в цепочкеКонечно, у нас нет делегата.

Давайте просто взглянем на два класса, которые вы упомянули в своем вопросе, не так ли?

package javax.xml.stream.util;

// (...)

public class StreamReaderDelegate implements XMLStreamReader {
  private XMLStreamReader reader;

  /**
   * Construct an empty filter with no parent.
   */
  public StreamReaderDelegate(){}

  /**
   * Construct an filter with the specified parent.
   * @param reader the parent
   */
  public StreamReaderDelegate(XMLStreamReader reader) {
    this.reader = reader;
  }

  // (...)

}
package org.xml.sax.helpers;

// (...)

public class XMLFilterImpl
  implements XMLFilter, EntityResolver, DTDHandler, ContentHandler, ErrorHandler
{
  private XMLReader parent = null;

  /**
   * Construct an empty XML filter, with no parent.
   * (...)
   */
  public XMLFilterImpl() {
    super();
  }

  /**
   * Construct an XML filter with the specified parent.
   * (...)
   */
  public XMLFilterImpl(XMLReader parent) {
    super();
    setParent(parent);
  }

  /**
   * Set the parent reader.
   * (...)
   */
  public void setParent(XMLReader parent) {
    this.parent = parent;
  }

   // (...)
}

Видеть?В обоих случаях нет наследования, кроме делегирования.Вызовы super() не связаны с шаблоном делегирования, они просто гарантируют, что конструктор суперкласса вызывается так, как и должно быть.Здесь у нас есть просто гибкий список объектов, каждый из которых указывает на своего делегата, похожий на связанный список.Это гораздо более гибкий подход, чем ваша идея привести все к фиксированному набору классов.

IMO, ваш подход пытается решить проблему, которая на самом деле не существует.Это пример проблемы XY .Не стесняйтесь задавать дополнительные вопросы, если я могу помочь вам в этом.Скажи мне что ты хочешь достичь, а не как ты считаешь, что это должно быть сделано.

...