【问题标题】:Is there any design pattern in this? Is there any possible flaw in this java code?这有什么设计模式吗?这个java代码有什么可能的缺陷吗?
【发布时间】:2013-10-03 06:58:24
【问题描述】:

为了在 java 中设计 API,我想出了以下模式来满足此处列出的某些要求

  • 实际的公共 API 类应作为最终类实现,以防止继承和可能的误用
  • 实际的公共 API 类不应公开任何超出所需方法的内容。
  • 将 API 类和内部实现分离到不同的包中
  • 公共类和内部类的可扩展性或演变范围

示例代码如下:

package external;

import internal.AbstractProduct;

public final class Product extends AbstractProduct{

    protected Product(int a, int b){
        super(a,b);
    }

    @Override
    public int result(){
        return super.result();
    }
}

public class ProductFactory {
    public static Product createProudct(int a, int b){
        return new Product(a, b);
    }
}

和内部类如下:

package internal;

public abstract class AbstractProduct {

    private final AbstractProduct impl;

    protected AbstractProduct(int a, int b){
        impl = new ProductImpl(a, b);
    }

    protected AbstractProduct(){
        impl = null;
    }

    protected int result(){
        return impl.result();
    }
}

class ProductImpl extends AbstractProduct{

    private int a;
    private int b;

    ProductImpl(int a, int b){
        this.a = a;
        this.b = b;
    }

    @Override
    protected int result(){
        return a*b;
    }
}

虽然它工作正常并且也有适当的访问级别,但我只有设计模式或 API 设计的初学者水平技能,所以我似乎很难发现可能的故障。那么这有什么问题吗?或者它是一些已经实践过的模式?

【问题讨论】:

  • 谢谢@sᴜʀᴇsʜᴀᴛᴛᴀ,不知道“代码审查”。那么我应该删除并在CR中重新发布还是什么?
  • IMO 这个问题不应该被删除。除了基本的非灵活工厂方法模式之外,您的设计非常奇怪。为什么要将AbstractProduct impl 标记为final?看起来你想实现decorator
  • 是的,这段代码有很多缺陷。如果您在此处发布您要解决的问题或您要通过此练习完成什么以获得更好的指导,那就更好了。
  • @LuiggiMendoza final 与impl 并不是真正必要的。请查看我在此答案上发布的评论stackoverflow.com/a/19043032/1443529,其中我解释了我在此设计背后的思考过程。

标签: java oop design-patterns api-design


【解决方案1】:

您尝试实现的唯一设计模式是Factory MethodProductFactory 类中。这是唯一想要的设计模式。

由于您当前的代码非常不灵活,因此整体甚至可以视为anti-pattern,更具体地说:

  • Poltergeist,因为 Product 只是为了执行 ProductImpl#result 而存在。
  • Call super,因为Product 只使用super 调用。
  • Accidental complexity,即使过程不仅仅是简单的int 乘法。
  • Cargo cult,因为您仍然没有意识到为什么以及何时使用设计模式。

(可能还有更多……)

说明:您的工厂方法模式非常不灵活。请注意,Product 类是 public,但有一个 protected 构造函数(甚至标记为 final 类,这是奇怪:为什么要在一个类上拥有 protected 方法可以从不被继承?),这意味着ProductFactory 至少应该与Product 在同一个包中。


正如我在其他 cmets 中直接针对您的问题所指出的那样,如果您能解释功能要求以获得更好、更准确的设计帮助,那就太好了。


IMO 为了学习设计模式,最好去看看现实世界的例子,而不是在网上阅读更多关于它们的内容,然后开始练习。我强烈推荐 BalusC(Java 和 Java EE 专家)的这个问答:Examples of GoF Design Patterns in Java's core libraries

【讨论】:

  • 好吧,我想我需要一些时间来解释一下,我真的不擅长解释事情。还将尝试详细说明功能需求。
  • 感谢您指出反模式!顺便说一句,ProductFactory 保证在同一个包中,尽管到目前为止,工厂对“产品”的创建没有太多控制权,但将来可能会有,所以我保留了这个简单的工厂。
  • @cpz 答案已编辑以包含 Java SE 和 Java EE 中设计模式的真实示例。
  • 哇,这个答案(@BalusC's)真的很重要!实际上我并不是盲目追求设计模式,而是考虑到一些 API 设计建议和约束,我想提出最佳设计,然后我想看看这个设计是否匹配某种模式,或者它是否有任何错误。我应该尽快准备好对实际需求的解释,以便进一步讨论。
【解决方案2】:

为什么要这样做?

protected AbstractProduct(){
    impl = null;
}

这会在调用result()时引发NullPointerException。

我也看不出AbstractProductProductImpl 的意义。只需将代码放入Product即可。

如果只有一个实现,我也会质疑为什么您需要ProductFactory,但如果您计划在未来进行实现,那么就可以了。

【讨论】:

  • 是的,Product 的未来实现是有可能的。所以工厂没问题。而Product 是我必须为其提供 javadoc 的 API 的一部分,因此为了保持它的简洁和最小化,我只想在 Product 中有实际需要的方法。而AbstractProduct 充当ProductProductImpl 之间的内部通用合约。而且这样我可以灵活地在ProductImpl中添加/删除内部方法来进化或扩展。
  • 您可以在Product 中拥有不属于公共 API 的内部方法,只需将它们设为私有即可。然后在制作 Javadoc 时将其设置为仅为公共方法创建 html。我只是担心你把事情复杂化了。如果你还没有读过,我推荐 Jaroslav Tulach 的 Practical API Design,它很好地涵盖了这个主题。
  • 我同意这有点过于复杂,但如果我在Product 中拥有所有内容,那么会以某种方式影响可扩展性或进化吗?就我的设计而言,整体 API 设计的可扩展性或演变具有优势。我最近拿到了那本书,还没有完全通读,但似乎很难一次考虑所有的事情并适应这个项目:-/
  • @dkatzel - 很好的建议,但有时(很少)不实用。为了完成工作,班级有时需要合作者的帮助。因此,您可以使用抽象工厂来使用包可见性,或者避免暴露内部结构。 . .我一点也不反对,只是指出一个可能的例外。
  • @JasperBlues 在这种情况下,我认为我们不需要合作者将两个数字相乘@cpz 因为Product 已经是最终的,我不认为添加私有方法会改变它的可扩展性完全没有
【解决方案3】:

如果是我,我会这样分解:

package external;

import internal.InternalProduct;
import internal.ProductImpl;

public final class Product {

    private final InternalProduct internalProduct;

    Product(final InternalProduct internalProduct) {
        this.internalProduct = internalProduct;
        assert this.internalProduct != null;
    }

    public int result() {
        return this.internalProduct.result();
    }
}

public class ProductFactory {
    public static Product createProduct(final int a, final int b) {
        return new Product(new ProductImpl(a, b));
    }
}

面向内部的包内容为:

package internal;

public interface InternalProduct {
    int result();
}

public final class ProductImpl implements InternalProduct {

    private final int a;
    private final int b;

    public ProductImpl(final int a, final int b) {
        this.a = a;
        this.b = b;
    }

    @Override
    protected int result() {
        return a * b;
    }
}

您不需要抽象类,并且您绝对不希望Product 扩展它。您应该在这里使用封装。这可以保护您的 Product 类免受内部接口的更改 - 可以添加方法而不需要 Product 获取它们,除非您选择它。

如果你有更多的实现并且你想要一个抽象的父类,你可以选择每个实现是否扩展它,因为你的external 包依赖于接口。

【讨论】:

    猜你喜欢
    • 2011-06-28
    • 2018-05-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-05-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多