C# 孙子类从其父类调用方法的最佳OOP实践
我想知道构造类的最佳实践,以便在类有祖辈时调用父方法 假设我有这样一个场景:C# 孙子类从其父类调用方法的最佳OOP实践,c#,oop,C#,Oop,我想知道构造类的最佳实践,以便在类有祖辈时调用父方法 假设我有这样一个场景: public class Vehicle { // parent public bool moves; public bool flies; public float maxSpeed; public virtual void Initialize () { moves = true; } } public class Airplane : Vehicle
public class Vehicle { // parent
public bool moves;
public bool flies;
public float maxSpeed;
public virtual void Initialize () {
moves = true;
}
}
public class Airplane : Vehicle { // child
public override void Initialize () {
base.Initialize ();
flies = true;
}
}
public class Jet : Airplane { // grandchild
public override void Initialize () {
base.Initialize ();
maxSpeed = 400f;
}
}
现在,如果我实例化一个Jet对象,它将运行车辆初始化,但不运行飞机父代码(意味着它不会飞行)
有一些解决办法。一种是将飞机代码移动到每个子类,但这样做会破坏使用OOP的目的。解决这个问题的最佳做法是什么
注意:我不能使用构造函数,因为这是一个Unity3D应用程序,连接到游戏对象的所有类都应该继承自MonoBehavior类,它有自己的初始化函数,如Awake()和Start(),并且不允许构造函数。我会创建接口,远离继承。用构图代替。换句话说,makejet包含飞机和车辆两个类(我假设一架飞机有飞机没有的特殊能力或属性)。并使用委托人方法 它使您的项目不那么脆弱。换句话说,如果你改变你的基类,你必须担心你的孩子(甚至是曾孙)是如何处理的。但是,如果包含它,只要接口相同,容器类就不会在意 这不是违背OOP的目的,而是更好的使用方法。我们仍然有这些独立的对象,并且不是复制粘贴代码。然而,我们正在阻止继承的使用 我建议你阅读这篇以星球大战为主题的娱乐性博客文章。 * 如果这不是你的事,那就用谷歌搜索“合成重于继承”,享受火焰之战吧 我想知道构造类的最佳实践,以便在类有祖辈时调用父方法 您不能这样做 问题的假设是,从飞机衍生的喷气式飞机需要知道飞机从车辆衍生的实现细节。那是个错误的假设。最佳实践是将飞机视为一个黑匣子(没有双关语),而不利用对其实现细节的了解 Initialize方法有一个约定:它初始化某些内容。如果你想叫它,就叫它。它负责确保满足其不变量;让它做它的工作 现在,在这种特殊情况下,你不需要做任何事情。您没有编写初始化方法,当然也没有调用基。如果需要字段初始值设定项,则需要编写字段初始值设定项 让我们考虑一个更好的例子。假设我们有一个可以重新绘制的显示曲面:
interface IUserInterface { void Redraw(); }
现在我们可以提出一个层次结构,其中的规则是:重画时,还必须调用超类:
abstract class ControlWithChildren : IUserInterface
{
...
protected IEnumerable<IUserInterface> children;
public virtual void Redraw() {
foreach(var child in children) child.Redraw();
}
class Panel : ControlWithChildren
{
...
public override void Redraw()
{
... redraw panel elements ...
base.Redraw();
}
}
class PanelWithTitle : Panel
{
...
public override void Redraw()
{
... redraw title element ...
base.Redraw();
}
}
带子对象的抽象类控件:IUserInterface
{
...
受保护的无数儿童;
公共虚拟空重绘(){
foreach(children中的var child)child.Redraw();
}
类面板:带孩子的控件
{
...
公共覆盖无效重绘()
{
…重新绘制面板元素。。。
base.Redraw();
}
}
类别PanelWithTitle:Panel
{
...
公共覆盖无效重绘()
{
…重新绘制标题元素。。。
base.Redraw();
}
}
好了。PanelWithTitle只处理重画标题的问题,并将其余工作推迟到Panel。Panel重画面板背景,并将其余工作推迟到ControlWithChildren。PanelWithTitle不需要了解ControlWithChildren的重画语义。。如果它确实需要知道这一点,那么面板的实现有问题。什么是构造函数?@EdPlunkett您可以在此处了解有关构造函数的更多信息:p@RufusL如果网络上有一些资源可以自动回答这样的问题就好了。如果这些代码是以一种无需手动编辑即可编译的方式编写的,那就太好了。否代码中的东西很有道理。为什么使用初始化方法而不是构造函数?为什么初始值设定项是私有的?为什么它们不是虚拟的?为什么你相信Jet对象不会调用基方法?为什么字段初始值设定项不是字段初始值设定项?我不能理解这个问题的头绪。飞机包含一个飞行器?这是一架货机吗?“我假设一架喷气式飞机具有飞机所不具备的特殊能力或特性”。它有一个喷气发动机。你不理解合成的概念。你在建模一个有关系而不是有关系时使用合成。此外,你不只是扔掉继承,而是与合成一起使用。飞机是一辆交通工具,喷气机是一架飞机。在一个正确合成的类中,你仍然会有一辆交通工具d飞机,但飞机只是定义了组成飞机的部件组成,所以飞机只需更换部件组成。但是如果飞机比飞机有更多的功能,你就需要创建一个新类。你链接中的家伙甚至显示了这一点。我想你误读了他的问题。他问如何调用
父方法问题是,父方法被跳过,祖父母被调用。至少我认为他是这么说的。@RufusL:当然,他这么说,没有证据,也没有编译或运行的代码。问题是:在一个有大基的世界里,调用基方法的最佳实践是什么,这个问题表明了这一点对于类与其grandbase之间的关系,可能存在一种不好的心态。这里更重要的不是OP修复了未显示的错误代码中的打字错误。最重要的是他们开始正确思考类型层次结构。
abstract class ControlWithChildren : IUserInterface
{
...
protected IEnumerable<IUserInterface> children;
public virtual void Redraw() {
foreach(var child in children) child.Redraw();
}
class Panel : ControlWithChildren
{
...
public override void Redraw()
{
... redraw panel elements ...
base.Redraw();
}
}
class PanelWithTitle : Panel
{
...
public override void Redraw()
{
... redraw title element ...
base.Redraw();
}
}