我有一个JAVA类A,它有一个方法foo
abstract class A {
abstract void foo();
}
我还有一个派生类A - MutableA。 MutableA是一个单例对象,表示不需要更新,这对重用代码流很有用。 永远不应该在MutableA上调用foo()。 实现这一目标的最佳方法是什么:
有人可以推荐一下这种情况下的最佳做法吗?
答案 0 :(得分:2)
因为你说"不需要更新" ,所以似乎是一个空的实现方式。如果我这样做:
A someA = createA();
someA.foo(); // make sure it's "foo"ed
我不知道我是否回来了MutableA
,所以我打电话给foo
只是为了确定。
如果禁止在foo
上拨打MutableA
,我会选择UnsupportedOperationException
或重新考虑设计。也许你可以将A
包装在AHandler
中,该foo
知道何时以及如何在它所包含的实例上调用{{1}}。
答案 1 :(得分:2)
我会考虑重新考虑设计。
在超类中使用抽象方法意味着子类应该实现该方法。除非您计划使用更大的层次结构,否则您应该考虑将foo方法降低到更接近实现的层次结构中。
如果您打算保留foo方法的位置,我也会将MutableA抽象化。
答案 2 :(得分:2)
你应该考虑看一下Liskov替代原理,这是良好设计的基本SOLID原则之一。
该原则规定派生对象的行为不应与其父母不同。违反Liskov原则的一个常见例子是从椭圆中推导出一个圆。这在数学上是完全合理的,声音被认为是糟糕的设计,因为从椭圆得到的圆(例如)仍会暴露方法来设置宽度和高度,但在某些情况下它会表现得很奇怪。请考虑以下
Something FooBar (Ellipse e)
{
// do something with e
}
如果我们不知道调用者通过Circle而不是Ellipse并且我们设置了宽度和高度,我们的结果可能与我们预期的不同,因为Circle忽略了SetHeight或者它设置了SetWidth上的宽度bot和SetHeight。无论哪种方式,如果我们期望在Ellipse上操作,这种行为是出乎意料的。因此Liskov替代原则。
答案 3 :(得分:1)
这取决于该方法的用途。 您列出的所有替代品几乎都是好的。
顺便说一句,如果你抛出一个UnsupportedOperation
异常,你就知道不应该使用这个方法。使用例外始终是"消息"偏离"正常",所以你要定义与A.foo()
的基本实现相关的事情,但是你也要用它来分解这个实现。子类。
如果你使用一个空的实现,你可以在一个hypotetical流程中使子类更加可用,而不会与过去(超类A
)分开,所以你可能不知道使用子类在你想要的每一种环境中。
在最后的分析中,你说MutableA
是单身,所以我认为你会在特定的环境中使用它,特别注意它:所以我会采用"异常&# 34;溶液
答案 4 :(得分:1)
我使用这样的东西:
public abstract class ReadableA {
}
public abstract class A extends ReadableA{
abstract void foo();
}
public class MutableA extends ReadableA {
}
代码中您希望收到A
的所有位置,请改为使用ReadableA
,除非您特别想要致电foo()
。然后将A
放入方法签名中。如果您想收到MutableA
,请写一个MutableA
签名。
示例:
public class UsageOfA {
public void bar(ReadableA a) {
// Can use both A and MutableA but a.foo() cannot be invoked.
}
public void bar(A a) {
a.foo();
}
public void bar(MutableA a) {
// a.foo() cannot be invoked.
}
}
答案 5 :(得分:1)
我认为抛出UnsupportedOperationException
是正确的选择。假设您有3个课程延长A
。
class X extends A{
//foo allowed here
}
class Y extends A{
//foo allowed here
}
class Z extends A{
//foo allowed here
}
class MutableA extends A{
//foo NOT allowed here
}
现在从设计角度来看,X,Y,Z and MutableA
应该以{em>一致的方式表现foo()
。我不鼓励引入另一个类层次结构,只是为了使MutableA foofree 。一个简单的灵魂就是抛出UnsupportedOperationException
并让调用者知道调用对象不能支持该方法。另一方面,空实现仍然有效,坦率地说,从设计角度来看并不合适,因为它是 >仍然是来自调用对象的有效调用。
PS:我也会考虑重新思考设计。任何可用但不应该使用的方法都不是好设计的一部分。