我想问题标题总结了它。是否有一段时间认为 ABC 拥有数据成员是好的设计?我一直想知道是否存在这样的情况。我唯一能想到的都是静态的,即使这样也有点牵强。
9 回答
我不明白为什么 ABC 不能正确地拥有每个实例(也称为非静态)数据成员,因为需要支持它提供给子类的方法。以 ABC 存在以提供模板方法 DP(钩子方法是抽象的)的常见情况为例——如果组织方法的部分功能是更新一些实例变量(例如,计数多少次方法被调用),那么显然这些变量也应该由 ABC 提供。你能更好地解释为什么你认为这是糟糕的设计吗?!
一个抽象类可以有它需要的任何成员来支持它提供给从它继承的类的功能。这并不是说这些子类可以直接访问它们:它们只能通过子类或其客户端进行的方法调用来读取和更改。
我在插件架构中看到了这一点,比如 Paint.NET。
控制反转可能需要这样做。例如,您有一堆采用 Logger 实例的类,它们所基于的抽象类可能有一个构造函数将其存储在成员变量或私有属性中(当然假设您记得调用基本构造函数grin)
没关系,当您的抽象类中的数据成员包含继承类的基本代码时
当数据成员只是为了描述你的类时,我会考虑使用接口
是的,它可以在抽象基类中提供成员变量,目的是它的子类将使用这些成员来进行具体实现。
这是一个具体的例子,使用我们都喜欢的汽车类比。
假设我们创建Car
了一个抽象基类,它有轮子、底盘和引擎的占位符,供其子类使用:
abstract class Car {
Wheels wheels
Chassis chassis
Engine engine
abstract void accelerate();
abstract void decelerate();
}
现在,对于 extends 的类,Car
成员已经可以使用,因此子类的职责是填充这些成员变量:
class NiceCar extends Car {
Decoration decoration;
public NiceCar() {
wheels = new ChromeWheels();
chassis = new LightweightCompositeChassis();
engine = new LotsOfHorsepowerEngine();
decoration = new CoolRacingStripes();
}
void accelerate() {
engine.feedFuel();
}
void decelerate() {
wheels.applyBrakes();
}
}
可以看出,抽象基类可以作为一个蓝图,应该填写组件(成员变量)以获得类的完整功能。在这种情况下,Car
提供了在具体实现中使用的汽车的基本部件。NiceCar
使用这些成员字段并为其自身的功能添加一些字段,例如装饰性油漆作业。
我怀疑您在抽象基类的概念周围画得太紧了。
抽象基类(与纯接口相反)是一个旨在让子类使用其某些功能的类。因此,它将具有一些功能以及旨在被覆盖的方法(接口部分)。这个功能没有任何理由不应该有与之关联的成员变量。
许多框架都基于继承。这些几乎不可避免地会有带有成员变量的抽象类。例如,DirectShow 是 Windows 中的多媒体流框架。源、编码器、解码器等都在所谓的“过滤器”中实现。各种类型的过滤器都有基类。它们中的每一个都将具有用于上游和下游过滤器、协商的媒体类型等的成员变量。
如果它是所有继承类都使用的状态,我认为必须将其移至基类。即使基础是抽象的。我认为大多数重构的人都会同意我的观点。
某些州应该在基地中可能有几个原因。减少代码重复是一个很好的理由。
干杯!
编辑:请告诉我你为什么投反对票,这样我才能改进。谢谢!
正如其他人所提到的,当需要存储状态时,将实例字段添加到任何类型的类中。这适用于抽象类或具体类——没有区别。
为什么要有所作为?毕竟抽象类就像任何类一样,只是它不能被实例化,需要子类化等来完成这个类。