面向对象编程(OOP):何时创建派生类,何时使用条件语句实现功能?

6

什么时候应该继续制作派生类,什么时候应该只向我的代码添加条件语句? 例如针对导弹的情况。

class Object;
class Projectile : public Object;

class Missile : public Projectile;
class MissileGuided : public Missile;

或者我应该在导弹代码中实现最后一个?
void Missile::Update()
{
    if(homing && ObjectExists(Target))
        TurnTowards(Target.Pos)

    Pos += Motion;
}

我认为对于所有的细节,第二个更好,因为您开始得到各种组合(例如,有些导弹可能不会显示在雷达上,有些可能可以被摧毁,有些可能在原目标被摧毁或超出范围后获取新目标等等)。
然而,在某些情况下,常规抛射物也可能具有导弹的一些属性(例如,可以被摧毁,大型抛射物可能会显示在雷达上等)。
然后,我还可以说抛射物与飞船共享属性(两者都移动,碰撞时会造成伤害,可能会显示在雷达上,可能会被摧毁...)。
最终,一切回归到像3个类一样。
class Entity;
class Object : public Entity;
class Effect : public Entity;

在创建派生类和使用标志或其他方式在方法中实现功能之间划分界限的好方法是什么?

4个回答

9
你可以考虑使用策略模式,而不是同时使用两种方法,并将行为封装在外部类中。然后,可以将这些行为注入到Missile类中,使其成为GuidedMissile或SpaceRocket或其他任何需要的类型。
这样就可以避免Missile类中过多的逻辑分支,并且不需要涉及与深层继承相关的逻辑复杂性。
维基百科收集了该模式在多种语言中使用的示例: http://en.wikipedia.org/wiki/Strategy_pattern
interface INavigable {
  Course calcCourse (Position current, Destination dest);
}


Class GeoStationaryRocketCPU implements INavigable {
  Course calcCourse (Position current, Destination dest) {
     return dest.getShortestRouteTo (current).getCourse();
  };

}

Class GuidedMissileCPU implements INavigable {
  Course calcCourse (Position current, Destination dest) {
      return dest.getLowestAltRouteTo (current).getCourse();
  };

}



class Missile {
  INavigable CPU;

  void setCPU (INavigable CPU) {
     this.CPU = CPU;
  }

  void fly ()
  {

     ...
     course = this.CPU.calcCourse (pos, dest);
     ...
  }

}

正如另一位同事所建议的,您也可以考虑使用装饰器模式。这里有几个设计问题需要在您采用该路线时注意:

  1. 如果要替换对象行为的某个方面,则需要装饰整个类。
  2. 如果修饰类的公共接口(假设是导弹)发生更改(即使涉及与其导航方面无关的某些功能),则必须更改所有修饰类(例如GuidedMissile、Rocket、FireworkRocket等)以传递接口。
  3. 在装饰器模式中,预期装饰器将添加功能,而不是替换它。它通过调用其函数来添加并调用基类方法。然而,在上述情况下,这将导致能够有一个装饰GeoStationaryRocketCPU的GuidedMissileCPU - 显然这不是您想要的。您希望选择不同的导航策略,而不是在彼此之上堆叠导航策略。

尽管如此,如果提供了现成的不可变导弹实现,装饰器可能是“解锁”它并实现策略模式的关键,以提供导弹所需的各种行为。


1
我认为在这种情况下,你应该使用装饰者模式。 每次添加新功能都是对原始导弹类的装饰。 例如,你可以创建一个新的装饰导弹类,它可以被引导(GuidedMissile),并且可以潜水(UnderwaterMissile),只需装饰导弹类即可。 好处是装饰者模式在运行时工作,你不需要构建所有具有静态行为的类,而是可以在运行时组合所需的行为。 请看这里: http://en.wikipedia.org/wiki/Decorator_pattern http://www.dofactory.com/Patterns/PatternDecorator.aspx

1
继承取决于您使用的语言/技术,例如在C++中,您可以从多个类派生一个类,但是在Java、C#、PHP等语言中没有这样的构造,而是有抽象类和接口。我的意思是,如果您有一个类,例如您公司的软件将使用多个数据库管理系统,那么就由您设计一个独立的类集合来实现这样的构造,并且您可以通过使用抽象类并将多态性应用于您的集合来实现它,例如:
    public abstract class Database
    {
         public virtual void Connect();
         public virtual int GetNumberOfTables();

    }

然后您可以派生一个特定于SQL Server或Oracle实现的类

    public class SQLServerDatabase : Database
    {
           public override void Connect()
           {
                   // SQL Implementation
           }

           public override int GetNumberOfTables()
           {
                 // SQL Implementation
           }  

    }


    public class OracleDatabase : Database
    {
           public override void Connect()
           {
                  // Oracle Implementation
           }

           public override int GetNumberOfTables()
           {
                 // Oracle Implementation
           }  
    }

在这样的结构中,您可以使用继承,但避免类逻辑复杂性,就像我在上面的示例中看到的那样。
至于条件语句,您可以使用它来测试是否已销毁对象或垃圾收集器将对象设置为最终化队列,以便您无法再使用它。

0

只需问自己,如何更容易地:

  1. 阅读和理解代码(小项目部分和整个项目)
  2. 将来更改代码(在预测未来需求时要非常小心,通常最好不要提前为“可能”的更改调整您的代码)

此外,有一些非常罕见的情况,类层次结构可能会严重影响性能。

因此,在上述标准更好的情况下创建派生类。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接