ООП: Когда создавать производные классы, а когда реализовывать функции с помощью условных выражений? - PullRequest
6 голосов
/ 19 октября 2008

Когда я должен продолжать создавать производные классы, и когда я должен просто добавить условные выражения в мой код? например для ракеты

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 голосов
/ 19 октября 2008

Возможно, вы захотите использовать шаблон стратегии вместо обоих подходов и инкапсулировать поведение во внешних классах. Затем поведение может быть введено в класс 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);
     ...
  }

}

Как предлагает другой коллега, вы можете рассмотреть возможность использования шаблона Decorator. Просто чтобы выделить пару вопросов дизайна, которые могут быть важны в вашем контексте, если вы выберете этот путь:

  1. Чтобы заменить только определенный аспект поведения объекта, вам нужно украсить весь класс.
  2. Если публичный интерфейс декорированного класса (скажем, ракета) когда-либо будет изменен (даже некоторые функциональные возможности, не связанные с его аспектом навигации), вам придется изменить все декорирующие классы (например, GuidedMissile, Rocket, FireworkRocket и т. Д.) На передать интерфейс.
  3. В Decorator вы ожидаете, что декоратор добавит функциональность, а не заменит ее. Он работает, добавляя вызов своих функций, затем вызывая метод базового класса. В приведенном выше сценарии это приведет к возможности иметь GuidedMissileCPU, который украшает GeoStationaryRocketCPU - очевидно, это не то, что вам нужно. Вы хотите, чтобы он выбрал другую стратегию навигации, а не стратегии навигации слоя друг на друга.

Тем не менее, когда предоставляется готовая неизменная реализация декорации Ракеты, это может быть ключом к ее «разблокировке» и реализации шаблона стратегии, чтобы обеспечить Ракету всевозможными необходимыми поведениями.

1 голос
/ 19 октября 2008

Я думаю, что в этом случае вам следует использовать Pattern Decorator. Каждая новая функциональность, которую вы добавляете, является украшением оригинального класса Missile. Например, вы можете создать новый декорированный класс Missile, который может быть управляемым (GuidedMissile) и который может уходить под воду (UnderwaterMissile), просто украшая класс Missile. Хорошо, что шаблон Decorator работает во время выполнения, и вам не нужно создавать все эти классы со статическим поведением, но вы можете составить необходимое поведение во время выполнения. Посмотрите здесь: http://en.wikipedia.org/wiki/Decorator_pattern http://www.dofactory.com/Patterns/PatternDecorator.aspx

1 голос
/ 19 октября 2008

Наследование зависит от языка / технологии, которую вы используете, например, в 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 голосов
/ 19 октября 2008

Просто спросите себя, как было бы проще:

  1. прочитать и понять код (небольшая часть проекта и весь проект)
  2. изменить код в будущем (будьте очень осторожны при прогнозировании будущих потребностей, обычно лучше не адаптировать ваш код для возможных изменений)

Также есть несколько действительно редких случаев, когда иерархия классов может серьезно повлиять на производительность.

Поэтому создавайте производные классы, когда это лучше для вышеуказанных критериев.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...