简体   繁体   English

多个级别的子类和方法链接

[英]Multiple levels of subclasses and method chaining

I am working on the following structure: 我正在研究以下结构:

  • Buffer
  • XBuffer extends Buffer XBuffer扩展Buffer
  • XYBuffer extends XBuffer XYBuffer扩展XBuffer

All objects should be instantiable, so no abstract , in order to support forward compatability. 为了支持前向兼容性,所有对象都应该是可实例化的,因此不能abstract

I have came up with the following, with pitfalls/issues I will describe below: 我提出了以下内容,并提出了以下陷阱/问题:

public class Buffer<S extends Buffer<S>> {
    protected final int bufferType;
    protected final int bufferDataType;

    protected int bufferId;
    protected boolean created;

    public Buffer(final int bufferType, final int bufferDataType) {
        this.bufferType = bufferType;
        this.bufferDataType = bufferDataType;
    }

    @SuppressWarnings("unchecked")
    public S create() {
        assertNotCreated();
        bufferId = GL15.glGenBuffers();

        created = true;
        return (S)this;
    }

    @SuppressWarnings("unchecked")
    public S bind() {
        assertCreated();
        GL15.glBindBuffer(bufferType, bufferId);

        return (S)this;
    }

    @SuppressWarnings("unchecked")
    public S fillData(final float[] data) {
        assertCreated();

        FloatBuffer dataBuffer = BufferUtils.createFloatBuffer(data.length).put(data);
        dataBuffer.flip();
        GL15.glBufferData(bufferType, dataBuffer, bufferDataType);

        return (S)this;
    }

    public void delete() {
        assertCreated();

        GL15.glDeleteBuffers(bufferId);
    }

    public int getBufferId() {
        return bufferId;
    }

    public boolean hasBeenCreated() {
        return created;
    }

    private void assertCreated() {
        if (!hasBeenCreated()) {
            throw new RuntimeException("Buffer has not been created.");
        }
    }

    private void assertNotCreated() {
        if (hasBeenCreated()) {
            throw new RuntimeException("Buffer has been created already.");
        }
    }

    @Override
    public String toString() {
        return "Buffer(" + bufferType + ", " + bufferDataType + ", " + bufferId + ", " + created + ")";
    }
}

public class ArrayBuffer<S extends ArrayBuffer<S>> extends Buffer<S> {
    public ArrayBuffer(final int bufferDataType) {
        super(GL15.GL_ARRAY_BUFFER, bufferDataType);
    }
}

public class StaticDrawArrayBuffer extends ArrayBuffer<StaticDrawArrayBuffer> {
    public StaticDrawArrayBuffer() {
        super(GL15.GL_STATIC_DRAW);
    }
}

Now the following happens: 现在发生以下情况:

  • Instantiating any of the lowest level subclasses works fine, StaticDrawArrayBuffer works as expected. 实例化任何最低级别的子类都可以正常工作, StaticDrawArrayBuffer可以按预期工作。
  • ArrayBuffer does not work as expected, as I cannot instantiate it anymore without using generics. ArrayBuffer无法正常工作,因为如果不使用泛型,我将无法实例化它。 ( Keep in mind: The generics used here are only there to help me, not to actually provide generic functionality ) 请记住:这里使用的泛型只是为了帮助我,而不是实际提供泛型功能
  • Buffer does not work as expected either with the same issue as ArrayBuffer . BufferArrayBuffer相同,也无法按预期工作。

What do I want? 我想要什么?

  • Everything I have now, except that I can instantiate Buffer and ArrayBuffer without using generics. 我现在拥有的一切,除了可以不使用泛型实例化BufferArrayBuffer

How would I do this? 我该怎么做?

To clarify more, these statements should all compile: 为了澄清更多,这些语句应全部编译为:

  • Buffer buffer = new Buffer().create().bind();
  • ArrayBuffer arrayBuffer = new ArrayBuffer().create.bind();
  • StaticDrawArrayBuffer staticDrawArrayBuffer = new StaticDrawArrayBuffer().create.bind()

If I would not use any generics at all, then new ArayBuffer().create() will return a Buffer , resulting in that it cannot be assigned to ArrayBuffer anymore. 如果我根本不使用任何泛型,那么new ArayBuffer().create()将返回Buffer ,导致无法再将其分配给ArrayBuffer It does leave chaining of Buffer methods intact, but it would furthermore also break if the chain would contain methods only available to ArrayBuffer . 它确实使Buffer方法的链保持完整,但是如果该链包含仅可用于ArrayBuffer方法,它也会中断。

If it helps, I have the ability to use Java 8 if it is a almost bugfree (it should be now, I think?), considering this project won't see real daylight for a long while anyway. 如果有帮助,我可以使用Java 8(如果它几乎没有错误)(我想现在应该是现在)了,因为考虑到该项目很长一段时间都不会出现实际情况。

What about Covariant return types instead of generics? 那么协变返回类型而不是泛型呢?

public class ArrayBuffer extends Buffer {
    public ArrayBuffer(final int bufferDataType) {
        super(GL15.GL_ARRAY_BUFFER, bufferDataType);
    }

    public ArrayBuffer create() {
        super.create();
        return this;
    }
    public ArrayBuffer bind() {
        super.bind();
        return this;
    }
}

public class StaticDrawArrayBuffer  extends ArrayBuffer {
    public StaticDrawArrayBuffer() {
        super(GL15.GL_STATIC_DRAW);
    }


    public StaticDrawArrayBuffer bind() {
        super.bind();
        return this;
    }
}

AFAIK this should compile now: AFAIK现在应该编译:

Buffer buffer = new Buffer().create().bind();
ArrayBuffer arrayBuffer = new ArrayBuffer().create().bind();
StaticDrawArrayBuffer staticDrawArrayBuffer = new StaticDrawArrayBuffer().create().bind()

One possibility would be to separate the part of your code that does not use generics from the generic inheritance tree (which is kind of clunky to begin with, in my opinion, but that's mainly an issue with the way Java was designed and the design choices made when generics were added to the language). 一种可能是将代码中不使用泛型的部分与泛型继承树分开(我认为这有点笨拙,但这主要是Java设计方式和设计选择方面的问题将泛型添加到语言中时生成)。

What I mean by that is to do the following: 我的意思是执行以下操作:

public class Buffer extends BaseBuffer<Buffer> {
    public Buffer(final int bufferType, final int bufferDataType) {
        super(bufferType, bufferDataType);
    }    
}

public class ArrayBuffer extends BaseArrayBuffer<ArrayBuffer> {
    public ArrayBuffer(final int bufferDataType) {
        super(bufferDataType);
    }
}

Where BaseBuffer and BaseArrayBuffer are the classes you call Buffer and ArrayBuffer . 其中BaseBufferBaseArrayBuffer是您称为BufferArrayBuffer的类。

(Though it seems you may already be doing this, based on one of your comments?) (尽管根据您的评论之一,您似乎已经开始这样做了?)

The issue with this method is that you could not pass a StaticDrawArrayBuffer as an ArrayBuffer or an ArrayBuffer as a Buffer (or store them in variables of those types) as they are no longer subclasses of those specific types. 此方法的问题是,您不能将StaticDrawArrayBuffer作为ArrayBuffer传递或将ArrayBuffer作为Buffer传递(或将它们存储在这些类型的变量中),因为它们不再是那些特定类型的子类。 You'd have to use something like BaseArrayBuffer<StaticDrawArrayBuffer> and BaseBuffer<ArrayBuffer> for that instead. 您必须为此使用诸如BaseArrayBuffer<StaticDrawArrayBuffer>BaseBuffer<ArrayBuffer>类的东西。

I would strongly suggest that you rethink your architecture. 我强烈建议您重新考虑您的体系结构。 Instantiating both children and parent classes is a bad, bad idea. 实例化子类和父类是一个不好的主意。 Suppose, one day you wish to change parent behaviour (because you use it and requirements have changed), while leaving child intact. 假设有一天您希望更改父母的行为(因为您使用了它,并且要求有所变化),而孩子却保持完整。 What then? 然后怎样呢?

Use Strategies instead. 改用策略。 Specify Buffer interface (why do you need it? What it's supposed to do?) and give each instance a Strategy to fulfill their purpose. 指定Buffer接口(为什么需要它?应该做什么?),并为每个实例提供一个策略以实现其目的。

Then you'll have only Buffer that contains Buffers and Strategies to implement your logic: 然后,您将只有Buffer,其中包含实现逻辑的缓冲区和策略:

public interface BufferingStrategy
{
    public Buffer create() ;
    public Buffer bind();
    public Buffer fillData(final float[] data);
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM