Предложение объектно-ориентированного проектирования

Вот мой код:

class Soldier {
public:
   Soldier(const string &name, const Gun &gun);
   string getName();
private:
   Gun gun;
   string name;
};

class Gun {
public:
   void fire();
   void load(int bullets);
   int getBullets();
private:
   int bullets;
}

Я должен назвать все функции членства Оружия по объекту Солдата. Что-то как:

soldier.gun.fire();

или

soldier.getGun().load(15);

Таким образом, какой является лучшим дизайном? Сокрытие оружия возражает как член парламента, не занимающий официального поста и доступ это с getGun () функция. Или создание его общедоступный участник? Или я могу инкапсулировать все эти функции, сделал бы реализацию тяжелее:

soldier.loadGun(15); // calls Gun.load()
soldier.fire(); // calls Gun.fire()

Таким образом, то, какой, Вы думаете, является лучшим?

15
задан pocoa 6 April 2010 в 15:12
поделиться

9 ответов

Я бы посоветовал выбрать второй вариант:

soldier.loadGun(15); // calls Gun.load()
soldier.fire(); // calls Gun.fire()

Изначально это больше работы, но по мере того, как система становится более сложной, вы можете обнаружить, что солдат захочет делать другие вещи до и после стрельбы из оружия (возможно, проверьте если у них достаточно боеприпасов, а затем кричите «Смерть присоски !!» перед выстрелом и бормочите «это должно быть больно» после, и проверьте, нужна ли им перезарядка). Он также скрывает от пользователей класса Солдат ненужные подробности того, как именно стреляют из пистолета.

21
ответ дан 1 December 2019 в 00:18
поделиться

Во-первых, вы нарушите Закон Деметры , получив доступ к Gun извне Класс солдат .

Вместо этого я бы рассмотрел такие методы:

soldier.ArmWeapon(...);
soldier.Attack(...);

Таким образом, вы также могли бы использовать свой кулак, нож, гранату, бейсбольную биту, лазерного кота и т. Д.

11
ответ дан 1 December 2019 в 00:18
поделиться

Инкапсулируйте функции, чтобы обеспечить согласованный пользовательский интерфейс, даже если позже вы измените логику. Соглашение об именах зависит от вас, но обычно я не использую «getFoo ()», а просто «foo ()» в качестве средств доступа и «setFoo ()» в качестве установщиков.

  • по возможности возвращайте ссылку на константу (Действующий элемент C ++ № 3).
  • Предпочитайте константы, перечисления и встроенные строки, а не жестко закодированные числа (пункт 4)
  • предоставляют уникальные соглашения об именах для ваших частных членов, чтобы отличать их от аргументов.
  • Используйте беззнаковые значения там, где они имеют смысл, чтобы переместить ошибки в время компиляции
  • Когда значения констант, например максимумы, применяются ко всему классу. Сделайте их статичными.
  • Если вы планируете наследование, убедитесь, что ваши деструкторы виртуальные.
  • Инициализируйте все члены до нормальных значений по умолчанию

Вот как выглядят классы после этого. CodePad

#include <iostream>
#include <string>
#include <stdint.h>

using namespace std;

class Gun 
{
public:
   Gun() : _bullets(0) {}
   virtual ~Gun() {}
   void fire() {cout << "bang bang" << endl; _bullets--;}
   void load(const uint16_t bullets) {_bullets = bullets;}
   const int bullets() const {return _bullets;}

   static const uint16_t MAX_BULLETS = 17;

protected:
   int _bullets;
 };

class Soldier 
{
public:
   Soldier(const string &name, const Gun &gun) : _name(name), _gun(gun) {}
   virtual ~Soldier() {}
   const string& name() const;
   Gun& gun() {return _gun;}

protected:
   string _name;
   Gun _gun;
};


int main (int argc, char const *argv[])
{
   Gun gun; // initialize
   string name("Foo");
   Soldier soldier(name, gun);

   soldier.gun().load(Gun::MAX_BULLETS);

   for(size_t i = 0; i < Gun::MAX_BULLETS; ++i)
   {
     soldier.gun().fire();
     cout << "I have " << soldier.gun().bullets() << " left!" << endl;
   }
  return 0;
}
0
ответ дан 1 December 2019 в 00:18
поделиться

В самом деле, это во многом зависит от того, какой контроль вы хотите иметь.

Чтобы смоделировать реальный мир, вы можете даже полностью инкапсулировать огнестрельный объект и просто использовать метод soldier.attack (). Затем метод soldier.attack () будет видеть, есть ли у солдата оружие и в каком состоянии находится оружие, и при необходимости стреляет или перезаряжает его. Или, возможно, бросить оружие в цель и убежать, если боеприпасов недостаточно для любой операции ...

5
ответ дан 1 December 2019 в 00:18
поделиться

Если вы раскрываете gun, вы позволяете вещи за пределами функций-членов Gun, что, вероятно, не очень хорошая идея:

soldier.gun = anotherGun; // where did you drop your old gun?

Если вы используете getGun(), вызовы выглядят немного уродливо, но вы можете добавить функции к Gun без изменения Soldier.

Если вы инкапсулируете функции (что я рекомендую), вы можете модифицировать Gun или вводить другие (производные) классы Gun без изменения интерфейса Soldier.

3
ответ дан 1 December 2019 в 00:18
поделиться

Предоставьте "getGun ()" или просто "gun ()".

Представьте, что однажды вам может понадобиться сделать этот метод более сложным:

Gun* getGun() {
  if (!out_of_bullets_) {
    return &gun_;
  } else {
    PullPieceFromAnkle();
    return &secret_gun_;
  }
}

Кроме того, вы можете захотеть предоставить константный метод доступа, чтобы люди могли использовать константную пушку на константном солдате:

const Gun &getGun() const { return gun_; }
1
ответ дан 1 December 2019 в 00:18
поделиться

Обычно мое решение основывается на природе класса контейнера (в данном случае, Soldier). Либо он полностью является POD, либо нет. Если это не POD, я делаю все члены данных приватными и предоставляю методы-доступники. Класс является POD, только если у него нет инвариантов (т.е. внешний агент не может сделать его состояние непоследовательным, изменяя его члены). Ваш класс солдата больше похож на не-POD, поэтому я бы выбрал вариант с методом доступа. Будет ли он возвращать const-ссылку или обычную ссылку - это ваше собственное решение, основанное на поведении fire() и других методов (изменяют ли они состояние оружия или нет).

BTW, Bjarne Stroustrup немного рассказывает об этом вопросе на своем сайте: http://www.artima.com/intv/goldilocks3.html

В качестве примечания: я знаю, что это не совсем то, о чем вы спрашивали, но я бы посоветовал вам также рассмотреть многочисленные упоминания, сделанные в других ответах на закон Деметры: раскрывать методы действия (которые действуют на оружие) вместо всего объекта оружия через метод getter. Поскольку у солдата "есть" оружие (оно в его руке, и он нажимает на курок), мне кажется более естественным, что другие действующие лица "просят" солдата выстрелить. Я знаю, что это может быть утомительно, если у оружия много методов, но, возможно, их можно сгруппировать в более высокоуровневые действия, которые солдат раскрывает.

2
ответ дан 1 December 2019 в 00:18
поделиться

Не существует золотого правила, которое применялось бы в 100% случаев. Это действительно суждение, зависящее от ваших потребностей.

Это зависит от того, сколько функций вы хотите скрыть / запретить для оружия от доступа к Solider.

Если вы хотите иметь доступ только для чтения к Gun, вы можете вернуть константную ссылку на свой собственный член.

Если вы хотите предоставить только определенные функции, вы можете создать функции-оболочки. Если вы не хотите, чтобы пользователь пытался изменить настройки оружия через Soldier, сделайте функции оболочки.

В общем, я рассматриваю Gun как собственный объект, и если вы не против раскрыть всю функциональность Gun и не возражаете, чтобы что-то было изменено с помощью объекта Soldier, просто сделайте его общедоступным.

Вам, вероятно, не нужна копия оружия, поэтому, если вы создаете метод GetGun (), убедитесь, что вы не возвращаете копию оружия.

Если вы хотите, чтобы ваш код был простым, поручите солдату иметь дело с оружием. Должен ли ваш другой код работать напрямую с оружием? Или солдат всегда умеет работать / перезаряжать свое собственное ружье?

1
ответ дан 1 December 2019 в 00:18
поделиться

Согласно закону Деметры, функции заключаются в инкапсуляцию.

http://en.wikipedia.org/wiki/Law_of_Demeter

Таким образом, если вам нужно какое-то взаимодействие между солдатом и оружием, у вас есть место для вставки кода.

Изменить: нашел соответствующую статью по ссылке в Википедии: http://www.ccs.neu.edu/research/demeter/demeter-method/LawOfDemeter/paper-boy/demeter. pdf Пример с газетчиком очень, очень похож на пример с солдатом, который вы публикуете.

7
ответ дан 1 December 2019 в 00:18
поделиться
Другие вопросы по тегам:

Похожие вопросы: