Java 8 中多重继承的用法

2024-01-07

Am I usingJava 8 的一个功能或misusing it?

请参阅下面的代码和解释以了解为什么选择这样。

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

首先是我如何得到这段代码的步骤:

  1. 我从Drawable接口和每个实现都有自己的drawDepthPass, draw and delete方法。

  2. 重构delete to a default方法简单、琐碎,应该不会出错。

  3. 然而能够重构drawDepthPass and draw我需要访问是否Drawable被细分和/或实例化,所以我添加了公共(非默认) 方法isTessellated(), isInstanced() and getInstancesCount().

  4. 然后我发现这会有点麻烦,因为我们程序员很懒,在每个地方都实现它们Drawable.

  5. 结果我添加了default方法Drawable,给出最基本的行为Drawable.

  6. 然后我发现我仍然很懒,也不想为镶嵌和实例化变体手动实现它。

  7. 所以我创建了TessellatedDrawable and InstancedDrawable提供default isTessellated() and isInstanced()分别。并且在InstancedDrawable我撤销了default实施getInstancesCount().

结果我可以得到以下结果:

  • Normal 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, TessellatedDrawableJava 8 完美地处理了这个问题,因为功能应该来自哪个接口没有任何歧义。

现在开始我自己的 OOP 设计评估:

  • Every Drawable实际上是一个Drawable, so Collection<Drawable>不会破裂。
  • 可以对所有的进行分组TessellatedDrawable and/or InstancedDrawable,与具体如何实施无关。

我的其他想法:

  • 使用更传统的分层方法,但是我忽略了这一点,因为它最终会导致:

  • abstract class AbstractDrawable

  • class Drawable extends AbstractDrawable
  • class TessellatedDrawable extends AbstractDrawable
  • class InstancedDrawable extends AbstractDrawable
  • class InstancedTessellatedDrawable extends AbstractDrawable

我还考虑过构建器模式,但是这是在创建某个对象的许多唯一实例时使用的模式,这不是我们在这里所做的,也不是关于对象的构造函数的。

所以第一个也是最后一个问题是:我是usingJava 8 的一个功能或misusing it?


首先,如果它有效,并且它做了你想做的事情,并且将来不存在出现问题的危险,那么说你滥用它是没有意义的。毕竟,它完成了工作,对吧?像默认方法和静态方法这样的功能被添加到接口中时考虑到了特定的目标,但如果它们帮助您实现其他目标,那么要么是对新功能的创造性使用,要么是粗暴而肮脏的黑客攻击。 :-) 在某种程度上,这是一个品味问题。

考虑到这一点,我在 API 中寻找的内容以及我在设计 API 时尝试做的就是区分clients的 API 来自实施者一个 API 的。 API 的典型客户端或用户从某处获取某种接口类型的引用,并调用其上的方法来使事情发生。实现者提供接口中定义的方法的实现,覆盖方法,并且(如果是子类)调用超类方法。通常,客户端调用的方法与子类调用的方法不同。

在我看来,这些概念正在混合在一起Drawable界面。当然,客户Drawable会做一些事情,比如打电话给draw or drawDepthPass对他们的方法。伟大的。但是看看默认的实现drawDepthPass,它使用获取一些信息isTessellated and isInstanced方法,然后使用这些方法来选择一个程序并以特定方式调用它的方法。将这些逻辑封装在一个方法中是可以的,但是为了在一个方法中完成它default方法中,必须强制 getter 进入公共接口。

当然,我对你的模型可能是错误的,但在我看来,这种逻辑更适合抽象超类和子类关系。抽象超类实现了一些处理所有 Drawable 的逻辑,但它使用以下方法与特定的 Drawable 实现进行协商isTesselated or isInstanced。在抽象超类中,这些将是子类需要实现的受保护方法。通过将此逻辑放入接口的默认方法中,所有这些都必须是公共的,这会使客户端接口变得混乱。其他看起来相似的方法是getDataMode, isShadowReceiver, and isShadowCaster。客户是否期望调用这些,或者它们在逻辑上是实现内部的?

这强调的是,尽管添加了默认方法和静态方法,接口仍然面向客户端,而不是面向支持子类。原因如下:

  • 接口只有公共成员。
  • 抽象类可以有受保护的方法供子类重写或调用。
  • 抽象类可以具有私有方法以实现实现共享。
  • 抽象类可以具有可以受保护以与子类共享状态的字段(状态),或者通常是私有的。
  • 抽象类可以具有最终方法来对子类强制执行某些行为策略。

我注意到的另一个问题Drawable接口系列的特点是它使用默认方法相互覆盖的能力,以允许对实现类进行一些简单的混合,例如Box。你可以直接说,这有点简洁implements TessellatedDrawable并避免讨厌的覆盖isTesselated方法!问题是这现在成为实现类类型的一部分。客户知道以下内容有用吗?Box也是一个TessellatedDrawable?或者这只是一个让内部实现更干净的方案?如果是后者,这些 mixin 接口可能会更好,例如TessellatedDrawable and InstancedDrawable不是公共接口(即包私有)。

另请注意,这种方法会使类型层次结构变得混乱,这会使代码导航起来更加混乱。通常,新类型是一个新概念,但拥有仅定义返回布尔常量的默认方法的接口似乎很重要。

这方面还有一点。再说一次,我不知道你的模型,但这里混合的特征非常简单:它们只是布尔常量。如果曾经有一个Drawable比如说,一开始没有被实例化,后来可以被实例化的实现,它不能使用这些 mixin 接口。默认实现的功能确实受到很大限制。它们无法调用私有方法或检查实现类的字段,因此它们的使用非常有限。以这种方式使用接口几乎就像使用它们作为标记接口一样,只需添加一点即可调用方法来获取特征,而不是使用instanceof。除此之外似乎没有太多用处。

中的静态方法Drawable界面看起来大多合理。它们是看起来面向客户端的实用程序,并且它们提供了公共实例方法所提供的逻辑的合理聚合。

最后,该模型有几点看起来很奇怪,尽管它们与默认方法和静态方法的使用没有直接关系。

这似乎是一个Drawable has-a Program,因为有实例方法compileProgram, getProgram, and delete。然而drawDepthPass类似的方法需要客户端传入两个程序,根据布尔 getter 的结果选择其中之一。我不清楚呼叫者应该在哪里选择正确的程序。

类似的事情正在发生drawAll方法和offset价值。看起来在 Drawable 列表中,必须根据每个 Drawable 的数据大小使用特定的偏移量来绘制它们。然而显然最基本的方法是draw,要求调用者传入一个偏移量。这似乎是一个很大的责任,要推给调用者。所以也许偏移量的东西确实也属于实现范围内。

有几种方法可以获取可绘制列表并调用stream()进而forEach() or forEachOrdered()。这是没有必要的,因为List has a forEach其上的方法,继承自Iterable.

我认为探索如何使用这些新东西是很棒的。它太新了,尚未出现普遍接受的风格。像这样的实验和讨论有助于发展这种风格。另一方面,我们还需要小心,不要仅仅因为这些闪亮的新功能是新的、闪亮的而使用它们。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Java 8 中多重继承的用法 的相关文章

随机推荐

  • 如何从 OpenSSL 加密数据中获取初始化向量 (iv)

    免责声明 密码学新手 我有一个使用 OpenSSL 加密数据的外部进程 该进程现在使用盐 iPhone 应用程序从服务器获取该数据 将其下载到应用程序的文档目录 然后需要对其进行解密 iPhone OS 不包含 OpenSSL 库 您可以自
  • 如何使用 PHP 将一个 YouTube 视频上传到另一个 YouTube 频道

    我正在尝试实现用户可以将 Youtube 视频上传到他的 YouTube 频道的功能 从here http www phpgang com how to authenticate upload videos to youtube chann
  • NGRX/存储有效负载类型混淆

    我有以下行动 export const ActionTypes CREATE OH type ORDERHEAD Create Orderhead MODIFY SELECTED OH type ORDERHEAD Select Order
  • 使用干预将图像的 DPI 从 72 更改为 300

    我正在与laravel并使用in用于图像处理 我想增加图像的 DPI 我没有看到任何有关 DPI 的文档here http image intervention io 有什么解决办法吗php或任何其他方法php or laravel Int
  • 静态方法中的继承

    为什么下面的代码打印 Main public class Main public static void method System out println Main public static void main String args
  • 蛮力魔方

    基本上我有一个 3 x 3 网格 其中填充了两位数字 00 99 其中一些数字作为输入给出 其余数字未知 关于如何用 C 语言暴力解决此类问题 有哪些建议 EDIT 抱歉我忘记了部分问题 每行 每列和对角线的总和必须相同 我不需要任何代码
  • 在nodejs中运行mocha测试用例时出现内存不足异常

    对于单元测试我正在使用mocha最近我正在观察out of memory exception运行测试用例时 Last few GCs gt 548213 ms Scavenge 1365 3 1457 7 gt 1365 3 1457 7
  • React-google-chart 不占用选项

    我在用react google chart为了以图形形式 条形图 显示我的数据 根据要求我必须制作一个dual y axis chart 我已经制作了该图表 但问题是该图表没有占据options 它是一个Bar当我将图表作为ColumnCh
  • 如何在CRM插件中获取当前用户记录?

    我正在开发一个插件 每当调用插件时 我需要获取当前用户信息 有什么办法可以找回它吗 该信息可在 PluginExecutionContext 中找到 下面的代码来自您的插件必须实现的 Execute 方法 public void Execu
  • 如何在 Android 版 Chrome 中强制硬重新加载

    在桌面版 Chrome 中 我可以在开发工具中选择在打开开发工具时完全禁用缓存 并且可以选择在长按重新加载按钮时手动执行硬重新加载 在开发工具打开的情况下 Android 版 Chrome 有这样的技术吗 我没有找到任何设置 当我在开发时想
  • 为 Android 构建内核模块

    我需要将 FTDI USB 模块添加到 Android 内核 Android 2 3 1 Linux 2 6 32 因此我获得了 2 6 32 内核并尝试构建该模块 make modules ARCH arm CROSS COMPILE a
  • 将 PowerShell 变量传递到脚本块

    我正在尝试获取 PowerShell 变量并将它们应用到脚本块 param string username throw Blackberry Admin User Name is required string password throw
  • PostgreSQL 使用 tf-idf 吗?

    我想知道 PostgreSQL 9 3 中使用 GIN GiST 索引的全文搜索是否使用 tf idf 术语频率 逆文档频率 特别是 在我的短语列中 我有一些更受欢迎的单词 而有些则非常独特 即名称 我想对这些列建立索引 以便匹配的唯一单词
  • django.db.utils.InternalError:(1050,“表'django_content_type'已经存在”)

    django db utils InternalError 1050 表 django content type 已经存在 我刚刚从我的朋友那里复制了一个项目 当我运行 makemirations 时它运行正常 但对于 python3 ma
  • 将元素添加到对象数组中

    我有一堂课叫receipt其中一个属性是一个数组item items 我有一个方法addItem string name int quantity double price 我的问题是如何将这些参数添加到数组中items 那么如何检查数量是
  • jQuery 从 Google 标签管理器加载到 gtm.js

    我遇到了一个问题 jQuery v1 9 1 被包含在 gtm js 文件的顶部 它会导致一些问题 并且可能会破坏已经加载到 jQuery fn 上的 jQuery 插件 回归测试也是一个问题 我检查了一下 在加载 jQuery 的 Goo
  • SQL*Plus :强制它返回错误代码

    我有一个存储过程 它有一个 OUT 参数 指示错误代码 如果错误代码不为 0 那么我会提出错误 DECLARE BEGIN foo err code IF err code lt gt 0 THEN raise application er
  • LINQ-to-Entities(不是 Linq-to-SQL)中是否有 DataContext?

    我最近问了一个关于跟踪 Linq to Entity 的问题 https stackoverflow com questions 137712 sql tracing linq to entities 我觉得答案之一 https stack
  • 当表中不存在列时如何将 paginate() 与having() 子句一起使用

    我有一个棘手的案例 以下数据库查询不起作用 DB table posts gt select posts DB raw haversineSQL as distance gt having distance lt distance gt p
  • Java 8 中多重继承的用法

    Am I usingJava 8 的一个功能或misusing it 请参阅下面的代码和解释以了解为什么选择这样 public interface Drawable public void compileProgram public Pro