【问题标题】:Implementation question regarding base classes and derived classes关于基类和派生类的实现问题
【发布时间】:2010-03-09 17:16:35
【问题描述】:

我有一个关于实现这一点的最佳方法的问题。我将描述我当前的实现以及我是如何将自己画到一个角落的:

我有一个名为 Package 的抽象类:

public abstract class Package {
    protected String description;
    protected String packagingCode;
    protected Dimension dimensions;
    protected Weight weight;

    protected Package() {
        this.description = null;
        this.packagingCode = null;
        this.dimensions = null;
        this.weight = null;
    }

    protected Package(String description, String packagingCode, Dimension dimensions, Weight weight) throws ShippingException {
        this.description = description;
        this.packagingCode = packagingCode;
        this.dimensions = dimensions;
        this.weight = weight;

        String exceptionMessage = "";

        if(!meetsWeightRequirements()) {
            exceptionMessage = "This package's weight exceeds limits. ";
        }

        if(!meetsDimensionalRequirements()) {
            exceptionMessage += "This package's dimensions exceed limits.";
        }

        if(!StringUtils.isEmpty(exceptionMessage)) {
            throw new ShippingException(exceptionMessage);
        }
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public String getPackagingCode() {
        return packagingCode;
    }

    public void setPackagingCode(String packagingCode) {
        this.packagingCode = packagingCode;
    }

    public Dimension getPackageDimensions() {
        return dimensions;
    }

    public void setPackageDimensions(Dimension dimensions) throws ShippingException {
        this.dimensions = dimensions;

        if(!meetsDimensionalRequirements()) {
            this.dimensions = null;
            throw new ShippingException("This package's dimensions exceed limits.");
        }
    }

    public Weight getPackageWeight() {
        return weight;
    }

    public void setPackageWeight(Weight weight) throws ShippingException {
        this.weight = weight;

        if(!meetsWeightRequirements()) {
            this.weight = null;
            throw new ShippingException("This package's weight exceeds limits.");
        }
    }


    public abstract boolean meetsWeightRequirements();

    public abstract boolean meetsDimensionalRequirements();
}

然后我有像这样扩展这个抽象类的类:

public class WeightBasedPackage extends Package {

    public boolean meetsWeightRequirements() {
        Weight weight = this.getPackageWeight();
        boolean meetsRequirements = false;

        if(weight != null) {
            meetsRequirements = (weight.getWeight() > 0);
        }

        return meetsRequirements;
    }

    public boolean meetsDimensionalRequirements() {
        return true;
    }
}

我有另一个对象 (ShipRequest) 维护一个包列表 (List<Package>)。我还有一个服务(例如WeightBasedPackageShipService)使用这个对象并且可以访问这个包列表。此实现运行良好,因为服务并不真正关心它是什么类型的包。包之间的唯一区别是它们实现抽象方法的方式。

这就是问题所在。我创建了一个新类:

public class OrderQuantityPackage extends Package {

    int quantity;

    public OrderQuantityPackage() {
        super();
    }

    public void setQuantity(int quantity) {
        this.quantity = quantity;
    }

    public int getQuantity() {
        return this.quantity;
    }

    public boolean meetsWeightRequirements() {
        return true;
    }

    public boolean meetsDimensionalRequirements() {
        return true;
    }
}

其中有一个quantity 字段。我需要访问服务中的这个字段 (OrderQuantityPackageShipService)。但是,由于它是 Package 类型,我必须强制转换它(看起来有点笨拙)。

我的问题是,我如何以更好的方式实现这一点(所以我不必强制转换)并确保类型安全(所以如果您使用 OrderQuantityPackageShipService,则包必须是 @ 类型987654331@)。我考虑过使用泛型,但对于我正在尝试做的事情似乎有点笨拙(ShipRequest 有许多其他属性,根据包的类型对其进行泛化似乎很奇怪)。

谢谢。

【问题讨论】:

  • 我没有正确理解您的模型还是应该 OrderQuantityPackage 扩展 Package?
  • 我的错,这是一个错字!
  • 我不明白为什么你不能将你的 Package 转换为 OrderQuantityPackage,如果它将 Package 扩展为你的 OrderQuantityPackageShipService,因为它应该期望一个与其名称 OrderQuantityPackageShipService 相同的数量。我在这里错过了什么吗?
  • 我已经更新了这个问题 - 我想可以投射,但我想知道是否有更好的方法来解决这个问题,这样它就不需要投射了。
  • 就我而言,最好在有用的时候上最普通的课程。但是,在您的情况下,您似乎需要一个特定的类来完成这项工作,具体取决于您的运输服务。然后就是说,我会选择转换我的基类并确保它是 OrderQuantityPackage 类,并且可能在不是时抛出。否则,我会要求 OrderQuantityPackage 类的实例作为我的服务的输入参数。

标签: java design-patterns oop inheritance polymorphism


【解决方案1】:
public abstract class Package {
    protected String description;  // These shouldn't be private fields instead of protected?
    protected String packagingCode; // Nah, I don't think so, otherwise how could I store a value into the Quantity field? =P 
    protected Dimension dimensions;  
    protected Weight weight;  
    protected int quantity;

    // Constructors, getters and setters...

    public virtual int getQuantity {
        throw new NotImplementedException();
    }

    public virtual int setQuantity(int quantity) {
        throw new NotImplementedException();
    }
}

public final class OrderQuantityPackage extends Package {
    public override int getQuantity {
        return super.quantity;
    }

    public override void setQuantity(int quantity) {
        super.quantity = quantity;
    }
}

虽然我对语法并不完全确定,对 NotImplementedException 也不完全确定,但我希望你明白这一点。因此,任何需要或需要数量的 Package 派生类都可以通过覆盖 Quantity 属性的 getter 和 setter 来实现。

在不需要 Quantity 的地方不应抛出异常,不应调用它,因此不应抛出异常。此外,它证明您的模型仅在时机成熟时执行所需的操作。

除此之外,OrderQuantityShipService 不应该要求 OrderQuantityPackage 中的 Weight 属性,正如 Vivin 所写,无论如何都可以访问重量。

否则,应该在您的服务中进行简单的转换。使用铸造不是肮脏的方法。例如,必须将事件处理程序中的发送者对象转换为他希望检查名称、状态或其他属性值的正确控件类型!然后将最普通的课程传递给活动,并且必须投... 而这个,不是我说选择这种方式的,这些是软件工程师!...

编辑 Vivin,如何在 JAVA 中从一种数据类型转换为另一种数据类型,就像在 C/C++/C# 中一样?

CastedType variable = (CastedType)TypeCast; 

【讨论】:

  • 谢谢 Will,让我试试这个方法(我必须将它转换为 Java - Javaland 中没有虚拟)所以是的,这意味着运行时错误与编译时,但这看起来是我最好的可以在这里做。另外,感谢关​​于选角的那一点。我想这在某些情况下是有道理的——只是我已经听过很多次了,如果你正在铸造,那么你需要检查你的模型......这就是我想要做的:) 是的,那些字段应该是私有的。我唯一不确定的是quantity 在基类中。我宁愿它留在派生类中:)
  • 大声笑谢谢 Vivin!我不确定Java中的这一点。我在 .NET 方面有更多经验,但不确定在 JAVA 中的这种特定用途。我对 JAVA 越来越感兴趣,因为我很容易识别出类似于 C# 的代码,嗯,C# 主要类似于 JAVA,我应该准确地说。 =) 尽管如此,这两种语言都从彼此那里得到了一些有趣的特性,所以它们看起来几乎一样。这就是为什么我提到我不确定我的代码中的异常等等。你对铸造的事情是正确的,你是一个自我审问和咨询的天才。
  • 有什么理由让我失去了投票,还是 SO 的 GUI 欺骗了我? =P
  • 嗯...我确实给了你一个赞成票 - 我不知道发生了什么(也许我双击了??)。它不让我再给你一个 - 它说它太旧了:(也许如果你编辑了它,我可以给你一个。哈哈是的,我也回答了 C# 问题,因为它们似乎与 Java 很相似!我实际上要采用铸造方式,我已经决定了。由于OrderQuantityShipService 知道它正在获得OrderQuantityPackage,所以我认为铸造它是有意义的。另外,感谢您的赞美 - 只是尝试以确保我正在编写好的代码:)
  • 感谢您对我的投票的关注。毕竟,它只是一颗糖果! =P 我利用编辑我的答案向您询问有关在 JAVA 中进行类型转换的问题。 =)
【解决方案2】:

简答:依赖倒置

您有一个 OrderQuantityPackageShipService 类需要它处理的对象的某些特征。所以 OrderQuantityPackageShipService 应该是指定这些要求的那个。通常这是通过接口完成的。如果它非常特定于服务,请创建嵌套接口。即:

class OrderQuantityPackageShipService {
    //...
    interface QuantityPackage {
        int getQuantity();
        // ...
    }
}

如果其他服务可以以一致的方式使用它,请在 OrderQuantityPackageShipService 类之外定义它。

然后让某些包实现该接口...

【讨论】:

  • 与从 Package 派生 OrderQuantityPackage 并指定附加属性成员不同吗?这样做只会提供具有 Quantity 属性的对象。为了完成这项工作,我认为您需要直接从 Package 实现这个接口。这样做与使 Quantity 属性成员成为 Package 基类的成员并在未覆盖时使其抛出相同,在我的拙见中,除了良好的对象模型设计之外,这宁愿是 Vivin 方法的解决方案。
  • @Will:我不确定。在某些方面,这就是 DI 的重点——我不需要也不关心知道大多数细节。在我的模型中,有些包会实现 Quantity,有些则不会。如果它们都需要(因为包的某些固有特性),那么是的,它应该在包中(也许默认是返回 1)。如果它不是固有的,我会把它分开。 YMMV。很难从一般描述中说出来,但我比大多数人更倾向于推动与界面的分离,而且它通常会让我在路上得到回报(当然,有时会在很远的地方值得投资......)
【解决方案3】:

也许您应该创建一个抽象服务并将其扩展为要处理的不同类型的包。您可以让处理方法是抽象的,并且让每种服务都知道如何处理相应的包。如果您不想混合使用不同类型的包,那么这可能会起作用。

【讨论】:

  • 我想这在某种程度上已经在这里完成了,因为您可能会遇到不同类型的运输服务,对吧?
【解决方案4】:

我能想到的一件事是,为什么需要访问 OrderQuantityPackageShipService 类中的 quantity 属性?正如我所看到的,您对Package 类的每个属性都有一个getter 和setter。这些 getter 和 setter 真的需要吗?为所有这些属性设置 getter/setter 并不适合封装。

您能否考虑在Package 类中提供在更高级别运行且不暴露内部属性的公共方法?这不是有帮助吗?

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-01-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多