2014-01-17 4 views
1

Я работаю по следующей структуре:Несколько уровней подклассов и метод построения цепочки

  • Buffer
  • XBuffer расширяет Buffer
  • XYBuffer расширяет XBuffer

Все объекты должны быть инстанциируемый, так нет abstract, чтобы поддерживать совместимость вперед.

я придумал следующее, ловушками/вопросов, которые я опишу ниже:

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

Сейчас происходит следующее:

  • StaticDrawArrayBuffer работает, как и ожидалось.
  • ArrayBuffer не работает должным образом, так как я не могу создать экземпляр без использования дженериков. (Имейте в виду: дженерики, используемые здесь, только там, чтобы помочь мне, а не на самом деле предоставлять общие функции)
  • Buffer не работает должным образом с той же проблемой, что и ArrayBuffer.

Что мне нужно?

  • Все, что есть сейчас, за исключением того, что я могу создать экземпляр Buffer и ArrayBuffer без использования дженериков.

Как бы это сделать?

Для уточнения Более того, эти заявления должны все компиляции:

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

Если бы я не использовать какие-либо генерики вообще, то new ArayBuffer().create() будет возвращать Buffer, в результате чего его нельзя назначить ArrayBuffer. Он оставляет цепочку методов Buffer неповрежденными, но, кроме того, она также нарушится, если цепочка будет содержать методы, доступные только для ArrayBuffer.

Если это помогает, у меня есть возможность использовать Java 8, если это почти без ошибок (должно быть, я думаю?), Учитывая, что этот проект долго не будет видеть реальный дневной свет.

+1

Вы спрашиваете, как вы можете использовать тип вы сделали общий без дженериков? Не делайте это * generic. Учитывая стирание типа Java, что вы подразумеваете под «* общей функциональностью»? –

+0

@ElliottFrisch Я не могу сделать это не общим, тогда функциональность ломается, как описано в моей новой редакции. Лучшее «легкое» решение, которое я могу сделать, это иметь не общий тип «RawArrayBuffer» и «RawBuffer». С * универсальной функциональностью * я имею в виду что-то вроде «List », где параметр типа «String» также фактически используется в результирующем объекте. – skiwi

+0

Прочтите [это] (http://docs.oracle.com/javase/tutorial/java/generics/erasure.html) и объясните, что вы просите. Кроме того, Java 8 планируется выпустить в марте ... Я бы не стал заниматься производством, пока не было нескольких суб-релизов, но кто-то должен быть морским свинком. –

ответ

1

Как насчет возвратных типов Covariant вместо дженериков?

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 это должно составить в настоящее время:

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

Это приводит к дублированию кода печально. Это правда, что ваш ответ действительно, но я не очень доволен тем, что представляю его всем классам. – skiwi

+0

Слишком плохо. Java не имеет спецификатора 'covariant_return' для методов, который указывает, что вызовы метода в подклассах будут иметь тип подкласса (с ограничением, которое методы, объявленные как' covariant_return', должны были бы вернуть объект тип класса, возможно, даже просто «это»). За кулисами компилятор может, возможно, реплицировать метод во все подклассы с правильным типом, чтобы избежать несовместимости байт-кодов, или если ссылки на частные члены родительского класса используются аналогично тому, что у вас есть, и вызвать родительский метод и выполнить возвращаемое значение. – JAB

+0

Í'll просто пойти с этим тогда, это по существу все еще копировать пасту + искать и заменять, чтобы сделать все подклассы, поэтому, кроме одного времени, это не больше работы во всей реальности. – skiwi

0

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

Что я имею в виду, что это сделать следующее:

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

Где BaseBuffer и BaseArrayBuffer являются классы, которые вы звоните Buffer и ArrayBuffer.

(Хотя это кажется, что вы, возможно, уже делают это, основываясь на одном из ваших комментариев?)

Проблема этого метода состоит в том, что вы не могли пройти StaticDrawArrayBuffer как ArrayBuffer или ArrayBuffer как Buffer (или хранить их в переменных этих типов), поскольку они больше не являются подклассами этих конкретных типов. Вы должны использовать что-то вроде BaseArrayBuffer<StaticDrawArrayBuffer> и BaseBuffer<ArrayBuffer> для этого.

+0

Это также приводит к множеству дополнительных сложностей ... Возможно, что «RawBuffer расширяет буфер », и подобное для «ArrayBuffer» было бы лучше? Или он снова разбивает подклассы. – skiwi

+0

@skiwi Я думаю, что это сработало бы почти так же, только вам нужно было бы бросить больше '@SuppressWarnings (« unchecked »)' annotations. Java просто не является хорошим языком для той элегантности, которую вы желаете. – JAB

0

Я настоятельно рекомендую вам пересмотреть вашу архитектуру. Инициирование как детей, так и родительских классов - плохая, плохая идея. Предположим, однажды вы захотите изменить родительское поведение (потому что вы его используете и требования изменились), оставив ребенка неповрежденным. Что тогда?

Используйте стратегии вместо этого. Укажите интерфейс буфера (зачем он нужен? Что он должен делать?) И дайте каждому экземпляру Стратегию, чтобы выполнить свою задачу.

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

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

Родительское и дочернее поведение чередуются навсегда, это не может измениться. Это построено непосредственно поверх OpenGL/LWJGL, чтобы избавиться от всех статических вызовов и вместо этого работать в объектно-ориентированном виде. Я согласен, что интерфейсы являются лучшими, но в этом случае он отличается. – skiwi

+0

Ну, в этом случае я лично не знаю, почему бы вам просто _cast_ не подойти к соответствующему родовому _after_ экземпляру необработанного типа. Дженерики в Java все равно поддельные, поэтому, если вы хотите собрать синтаксический сахар - просто сделайте это. Во всяком случае, я стою наедине: чтобы создать экземпляр бабушки и дедушки, родители и сын - большая ошибка. Должно быть другое решение вашей проблемы, если этот исходный код находится под вашим контролем. – Undespairable

+0

@Undespairable Что именно вы имеете в виду «для создания бабушки и дедушки, родители и сын - большая ошибка»? 'ArrayBuffer', похоже, не требует создания ранее существовавшего« Буфера », а также не создается« StaticDrawArrayBuffer »для создания ранее существовавшего« ArrayBuffer ». – JAB

Смежные вопросы