上周末我读了一些关于接口,抽象类和设计原则的东西。最后我有点困惑,我试图建立一个我学到的(或者我以为我学到的)的例子。
这是我的例子: 案例是模拟一个包含树木信息的类。
首先,我会创建一个界面:
public interface Tree{
public void grow();
}
该接口包含应由具体树实现的所有方法。到目前为止这么好,但是这样的树需要一些在所有树家族上共享的属性(变量)。为此,我将使用一个抽象类:
public abstract class AbstractTree implements Tree {
private String barColor;
private int maxHeight;
private boolean isEvergreen;
}
这是正确的方法,还是我无法就其他类中的属性(变量)签订合同?
属性部分完成后,我希望有3种类型的树。
所以这些树" tpyes"可以有个别变量。
public class OakTreeImpl extends AbstractTree{
private String barColor;
private int maxHeight;
private boolean isEvergreen;
private String foo;
@Override
public void grow() {
}
}
这种方法在面向对象的设计原则中听起来是否正确,还是我完全错了呢?
答案 0 :(得分:7)
这确实有效,但它没有多大意义,因为在这种情况下界面完全过时了。
您应该将grow方法添加到AbstractTree
,如下所示:
public abstract class AbstractTree{
protected String barColor;
protected int maxHeight;
protected boolean isEvergreen;
public abstract void grow();
}
使用界面是有意义的,如果你想拥有不同种类的植物,例如都应该能够生长。
interface Plant{
void grow();
}
abstract class Tree implements Plant{
void grow(){ /* do sth */ }
}
abstract class Flower implements Plant{
void grow(){ /* do sth totally different */
}
接口的目的是在具有不同实现的多个类中提供相同的方法,而抽象类提供在其所有子类中共享的方法和属性。
如果抽象类中的方法也是抽象的,那么每个子类都必须自己实现它。
答案 1 :(得分:6)
我宁愿将实例变量标记为protected
。
因为子类可以访问超类的所有受保护成员。 当且仅当父类和子类位于同一个包中
public abstract class AbstractTree implements Tree {
protected String barColor;
protected int maxHeight;
protected boolean isEvergreen;
}
public class OakTreeImpl extends AbstractTree{
// I can access barColor, maxHeight, isEvergreen in this class
@Override
public void grow() {
}
}
答案 2 :(得分:5)
虽然这可能部分是主观的,但我必须同意到目前为止给出的其他答案。
Tree
界面 NOT 已过时。如果要为Tree
建模,那么应该有一个Tree
界面,明确说明每个Tree
都有的方法。
特别是,我建议不要将其替换为AbstractTree
类。有些人说你几乎不应该使用抽象类 (例如Jaroslav Tulach in "Practical API Design")。我至少会说你应该保守地使用非常。最重要的是:您应该尽量避免在其他类的公共接口中出现抽象类。例如,如果您有另一个类/接口,使用类似
void makeGrow(Tree tree) {
System.out.println("Growing "+tree);
tree.grow();
}
然后将此Tree
的外观替换为AbstractTree
会降低灵活性。您永远无法使用 not 继承自AbstractTree
的类 - 并且考虑到您只能从一个类继承,这可能是一个严重的限制。 (您始终可以实现多个接口 - 因此接口不会限制此处的灵活性。)
但即使如果你使用基于抽象的类,我建议保守地使用protected
字段。或者,更一般地说,要注意从类继承的含义,如“第17项 - 继承的设计和文档,或者禁止它”"Effective Java" by Joshua Bloch中所述。。
在许多情况下,您不希望继承类具有对字段的完全访问权限。因此,您至少应该考虑创建字段private
,并且只为您要授予继承类的访问类型提供protected
方法。
public interface Tree{
public void grow();
}
abstract class AbstractTree implements Tree {
// Do the values of these fields ever change? If not,
// then make them final, and set them only in the
// constructor
private final String barColor;
private final int maxHeight;
private final boolean evergreen;
protected AbstractTree(...) { ... }
// Subclasses are only allowed to read (but not write) these fields
protected final String getBarColor() { return barColor; }
protected final intgetMaxHeight() { return maxHeight; }
protected final boolean isEvergreen() { return evergreen; }
}