【发布时间】:2014-03-29 22:14:51
【问题描述】:
我是使用 Java 8 的一个特性还是滥用它?
请参阅下面的代码和解释,了解为什么选择这样。
public interface Drawable {
public void compileProgram();
public Program getProgram();
default public boolean isTessellated() {
return false;
}
default public boolean isInstanced() {
return false;
}
default public int getInstancesCount() {
return 0;
}
public int getDataSize();
public FloatBuffer putData(final FloatBuffer dataBuffer);
public int getDataMode();
public boolean isShadowReceiver();
public boolean isShadowCaster(); //TODO use for AABB calculations
default public void drawDepthPass(final int offset, final Program depthNormalProgram, final Program depthTessellationProgram) {
Program depthProgram = (isTessellated()) ? depthTessellationProgram : depthNormalProgram;
if (isInstanced()) {
depthProgram.use().drawArraysInstanced(getDataMode(), offset, getDataSize(), getInstancesCount());
}
else {
depthProgram.use().drawArrays(getDataMode(), offset, getDataSize());
}
}
default public void draw(final int offset) {
if (isInstanced()) {
getProgram().use().drawArraysInstanced(getDataMode(), offset, getDataSize(), getInstancesCount());
}
else {
getProgram().use().drawArrays(getDataMode(), offset, getDataSize());
}
}
default public void delete() {
getProgram().delete();
}
public static int countDataSize(final Collection<Drawable> drawables) {
return drawables.stream()
.mapToInt(Drawable::getDataSize)
.sum();
}
public static FloatBuffer putAllData(final List<Drawable> drawables) {
FloatBuffer dataBuffer = BufferUtils.createFloatBuffer(countDataSize(drawables) * 3);
drawables.stream().forEachOrdered(drawable -> drawable.putData(dataBuffer));
return (FloatBuffer)dataBuffer.clear();
}
public static void drawAllDepthPass(final List<Drawable> drawables, final Program depthNormalProgram, final Program depthTessellationProgram) {
int offset = 0;
for (Drawable drawable : drawables) {
if (drawable.isShadowReceiver()) {
drawable.drawDepthPass(offset, depthNormalProgram, depthTessellationProgram);
}
offset += drawable.getDataSize(); //TODO count offset only if not shadow receiver?
}
}
public static void drawAll(final List<Drawable> drawables) {
int offset = 0;
for (Drawable drawable : drawables) {
drawable.draw(offset);
offset += drawable.getDataSize();
}
}
public static void deleteAll(final List<Drawable> drawables) {
drawables.stream().forEach(Drawable::delete);
}
}
public interface TessellatedDrawable extends Drawable {
@Override
default public boolean isTessellated() {
return true;
}
}
public interface InstancedDrawable extends Drawable {
@Override
default public boolean isInstanced() {
return true;
}
@Override
public int getInstancesCount();
}
public class Box implements TessellatedDrawable, InstancedDrawable {
//<editor-fold defaultstate="collapsed" desc="keep-imports">
static {
int KEEP_LWJGL_IMPORTS = GL_2_BYTES | GL_ALIASED_LINE_WIDTH_RANGE | GL_ACTIVE_TEXTURE | GL_BLEND_COLOR | GL_ARRAY_BUFFER | GL_ACTIVE_ATTRIBUTE_MAX_LENGTH | GL_COMPRESSED_SLUMINANCE | GL_ALPHA_INTEGER | GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH | GL_ALREADY_SIGNALED | GL_ANY_SAMPLES_PASSED | GL_ACTIVE_SUBROUTINE_UNIFORM_MAX_LENGTH | GL_ACTIVE_PROGRAM | GL_ACTIVE_ATOMIC_COUNTER_BUFFERS | GL_ACTIVE_RESOURCES | GL_BUFFER_IMMUTABLE_STORAGE;
int KEEP_OWN_IMPORTS = UNIFORM_PROJECTION_MATRIX.getLocation() | VS_POSITION.getLocation();
}
//</editor-fold>
private FloatBuffer data;
private Program program;
private final float width, height, depth;
public Box(final float width, final float height, final float depth) {
this.width = width;
this.height = height;
this.depth = depth;
data = generateBox();
data.clear();
}
@Override
public void compileProgram() {
program = new Program(
new VertexShader("data/shaders/box.vs.glsl").compile(),
new FragmentShader("data/shaders/box.fs.glsl").compile()
).compile().usingUniforms(
UNIFORM_MODEL_MATRIX,
UNIFORM_VIEW_MATRIX,
UNIFORM_PROJECTION_MATRIX,
UNIFORM_SHADOW_MATRIX
);
}
@Override
public int getInstancesCount() {
return 100;
}
@Override
public Program getProgram() {
return program;
}
@Override
public int getDataSize() {
return 6 * 6;
}
@Override
public FloatBuffer putData(final FloatBuffer dataBuffer) {
FloatBuffer returnData = dataBuffer.put(data);
data.clear(); //clear to reset data state
return returnData;
}
@Override
public int getDataMode() {
return GL_TRIANGLES;
}
@Override
public boolean isShadowReceiver() {
return true;
}
@Override
public boolean isShadowCaster() {
return true;
}
private FloatBuffer generateBox() {
FloatBuffer boxData = BufferUtils.createFloatBuffer(6 * 6 * 3);
//put data into boxData
return (FloatBuffer)boxData.clear();
}
}
首先是我如何获得此代码的步骤:
我从
Drawable接口开始,每个实现都有自己的drawDepthPass、draw和delete方法。将
delete重构为default方法很简单,很简单,应该不会出错。但是,为了能够重构
drawDepthPass和draw,我需要访问Drawable是否被镶嵌和/或实例化,所以我添加了公共(非默认) 方法isTessellated()、isInstanced()和getInstancesCount()。然后我发现在每个
Drawable中实现它们会有点麻烦,因为我们程序员很懒惰。因此,我将
default方法添加到Drawable,给出最基本的Drawable的行为。然后我发现我仍然很懒惰,不想为镶嵌和实例化的变体手动实现它。
所以我创建了
TessellatedDrawable和InstancedDrawable,它们分别提供defaultisTessellated()和isInstanced()。在InstancedDrawable中,我撤销了getInstancesCount()的default实现。
因此,我可以拥有以下内容:
- 正常
Drawable:public class A implements Drawable - 镶嵌
Drawable:public class A implements TessellatedDrawable - 实例化
Drawable:public class A implements InstancedDrawable - 镶嵌和实例化
Drawable:public class A implements InstancedDrawable, TessellatedDrawable。
为了确保您可以正常编译和运行,implements InstancedDrawable, TessellatedDrawable 由 Java 8 完美处理,因为功能应该来自哪个接口没有任何歧义。
现在开始我自己的小 OOP 设计评估:
- 每个
Drawable实际上都是Drawable,所以Collection<Drawable>不会中断。 - 可以对所有
TessellatedDrawable和/或InstancedDrawable进行分组,与具体实现方式无关。
我的其他想法:
使用更传统的分层方法,但我忽略了这一点,因为它最终会出现:
abstract class AbstractDrawableclass Drawable extends AbstractDrawableclass TessellatedDrawable extends AbstractDrawableclass InstancedDrawable extends AbstractDrawableclass InstancedTessellatedDrawable extends AbstractDrawable
我也考虑过 Builder 模式,但是当您创建某个对象的许多唯一实例时要使用这种模式,这不是我们在这里所做的,这也不是关于对象。
所以第一个也是最后一个问题是:我是使用 Java 8 的一个特性还是滥用它?
【问题讨论】:
-
哇,问题透彻,谢谢
-
这是一个很好的问题,但对于 Code Review 来说可能比 Stack Overflow 更好,因为这是一个风格判断而不是正确性的问题。我个人的直觉仍然是避免多重继承,除非确实有必要,因为看到它在其他一些语言中造成混乱......而且我还不喜欢将自己锁定在 Java 8 中的想法。我在您的设计中没有发现任何明显错误的地方,但我会犹豫是否走这条路。
-
这是一个有趣的问题,因为它正在探索一种新语言特征的维度。但是,这是一个非常冗长的问题(对不起)。是否有可能将其缩减为最基本的必需品?此外,您似乎在这里有两个问题:1)这是对语言功能的使用/滥用吗? 2)你将如何处理这个设计空间?最好将这些问题分解成单独的、更容易回答的问题!
-
@DavidBullock 我认为阅读整个故事至少与问题本身一样重要。
标签: java oop inheritance multiple-inheritance java-8