2009-03-15 6 views
1

Недавно я написал класс, который отображает кривые B-сплайна. Эти кривые определяются рядом контрольных точек. Первоначально я намеревался использовать восемь контрольных точек, так что я добавил константу в классе, например, так:Лучше хранить константы класса в элементах данных или в методах?

class Curve 
{ 
    public: 
     static const int CONTROL_POINT_COUNT = 8; 
}; 

Теперь я хочу, чтобы расширить этот класс, чтобы произвольное количество контрольных точек. Поэтому я хочу, чтобы изменить это:

class Curve 
{ 
    public: 
     int getControlPointCount() {return _controlPointCount;} 
}; 

Вопрос заключается в том, является ли это не лучше хранить константы в методах, чтобы начать с того, чтобы облегчить приспособляемость. Другими словами, не лучше, начал так:

class Curve 
{ 
    public: 
     int getControlPointCount() {return 8;} 
}; 

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

Это хорошая практика или плохой?

+0

Я честно не понимаю вопроса. что отличается от изменения 8 в методе изменения 8 в инициализаторе статичности? Оба требуют такой же работы. метод имеет недостаток, что он не является чистой постоянной времени компиляции и, следовательно, не может быть использован для некоторых вещей –

ответ

1

Обычно я предпочитаю поддерживать как можно меньше муфт вручную.

Количество контрольных точек на кривой - это количество контрольных точек на кривой. Это не независимая переменная, которая может быть задана по желанию.

Так что я обычно подвергнет константную ссылку стандартный контейнер:

class Curve 
{ 
    private: 
     std::vector<Point>& _controlPoints; 

    public:  
     Curve (const std::vector<Point>& controlPoints) : _controlPoints(controlPoints) 
     { 
     } 

     const std::vector<Point>& getControlPoints() 
     { 
      return _controlPoints; 
     } 
}; 

И если вы хотите знать, сколько контрольных точек, а затем использовать curve.getControlPoints().size(). Я подозреваю, что в большинстве случаев использования вам нужны точки, а также счет в любом случае, и, выставляя стандартный контейнер, вы можете использовать итераторы итератора стандартной библиотеки и встроенные алгоритмы, вместо того, чтобы получать счет и вызов функция, подобная getControlPointWithIndex в петле.

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

typedef std::vector<Point> Curve; 

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

+0

Отличный ответ :) Вы делаете очень хороший вывод о минимизации количества зависимостей. Тем не менее точки сохраняются в моем собственном классе Matrix. Разве вы не чувствуете, что выявление этого уровня детализации реализации потенциально увеличивает связь между клиентом и классом? – fluffels

1

Чтобы лучше ответить на ваш вопрос, нужно также знать, как установлена ​​переменная controlPointCount. Он установлен вне вашего класса? В этом случае вы также должны определить сеттер. Или класс кривых является единственным ответственным за его установку? Установлен ли он только во время компиляции или во время выполнения.

Во всяком случае, во избежании магического числа даже в таком виде:

int getControlPointCount() {return 8;} 

Это лучше:

int getControlPointCount() {return CONTROL_POINT_COUNT;} 

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

+0

Почему вы не могли определить CONTROL_POINT_COUNT как метод? –

+0

Почему бы избежать использования магического номера здесь? – fluffels

+0

@Earwicker: Если это метод, то лучше иметь подпись метода. @fluffels: Магические числа делают код менее читаемым и менее обслуживаемым. Если в какой-то момент вам нужно изменить значение константы, это проще, если есть определение. – kgiannakakis

2
int getControlPointCount() {return _controlPointCount;} 

Это аксессор. Перестановка константы, статической для аксессуар, на самом деле не является коэффициентом усиления, как указывает лампочка. То, что вам действительно нужно Будущее-доказательство, вероятно, пара аксессуаров и мутаторов.

int getControlPointCount() {return _controlPointCount;} // accessor 

Я также бросить в дизайне-сопзе для аксессора и сделать его:

int getControlPointCount() const {return _controlPointCount;} // accessor 

и соответствующее:

void setControlPointCount(int cpc) { _controlPointCount = cpc;} //mutator 

Теперь большая разница со статическим объект состоит в том, что подсчет контрольной точки больше не является атрибутом уровня класса, а экземпляром уровня один. Это изменение дизайна . Вы так хотите?

Nit: Ваш статический подсчет уровня класса равен public и, следовательно, не нуждается в аксессуаре.

0

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

В данном конкретном случае я бы сделал что-то вроде следующего, я думаю:

class Curve 
{ 

    protected: 

     int getControlPointCount() {return _controlPointCount;} 
     int setControlPointCount(int c) { _controlPointCount = c; } 

    private: 

     static int _controlPointCount = 0; 
}; 
+0

Мне не нравится смешивать статические и нестатические детали. Если вы переходите на статический атрибут, инициализируйте его в объявлении переменной, а не в каждой конструкции объекта. Оба сеттера и геттеры должны быть статичными. –

+0

(продолжение) Ваша реализация выше сбросит переменную до 0 всякий раз, когда создается новый экземпляр, отбрасывая предыдущие наборы –

0
class Curve 
{ 
    private: 
     int _controlPointCount; 

     void setControlPointCount(int cpc_arg) 
     { 
      _controlPointCount = cpc_arg; 
     } 

    public:  
     curve() 
     { 
      _controlPointCount = 8; 
     } 

     int getControlPointCount() const 
     { 
      return _controlPointCount; 
     } 
}; 

Я создам такой код, с заданной функцией в частном порядке, так что ни один орган не может играть с подсчетом контрольной точки, пока мы не перейдем к следующему этапу разработки. Мы обновляем начало обновления контрольной точки во время выполнения. в то время мы можем перенести этот метод набора из частного в общедоступный.

0

Хотя понимание вопроса, у меня есть ряд концептуальных проблем с примера:

  • Что возвращаемое значение для getControlPointCount(), когда количество контрольных точек не ограничено?
    • Это MAXINT?
    • Является ли это текущее число контрольных точек на кривой (таким образом, нарушая логику, которая говорит, что это максимально возможное количество баллов?)
  • Что происходит, когда вы на самом деле пытаются создать кривую с MaxInt точки? В конечном итоге у вас закончится память.

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

Что касается конкретного ответа, я согласен с kgiannakakis: функция-член обеспечивает большую гибкость.

0

Я использую конфигурацию + константу (значение по умолчанию) для всех «стабильных» значений посредством выполнения программы. С равными константами для значений, которые не могут измениться (360 градусов -> 2 pi радиан, 60 секунд -> 1 минута) или чье изменение нарушит текущий код (минимальные/максимальные значения для алгоритмов, которые делают их неустойчивыми).

Вы имеете дело с некоторыми различными проблемами дизайна. Сначала вы должны знать, является ли количество контрольных точек значением класса или экземпляра. Затем, является ли это константой на любом из двух уровней.

Если все кривые должны иметь одинаковое количество контрольных точек в приложении, это значение уровня класса (статическое). Если разные кривые могут иметь разное количество контрольных точек, то это не значение уровня класса, а скорее уровень экземпляра.

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

// Assuming that different curves can have different 
// number of control points, but that the value cannot 
// change dynamically for a curve. 
class Curve 
{ 
public: 
    explicit Curve(int control_points) 
     : control_points_(control_points) 
    {} 
    // ... 
private: 
    const int control_points_; 
}; 

namespace constant 
{ 
    const int spline_control_points = 8; 
} 
class Config 
{ 
public: 
    Config(); 
    void readFile(std::string const & file); 

    // returns the configured value for SplineControlPoints or 
    // constant::spline_control_points if the option does not 
    // appear in config. 
    int getSplineControlPoints() const; 
}; 

int main() 
{ 
    Config config; 
    config.readFile("~/.configuration"); // read config 

    Curve c(config.getSplineControlPoints()); 
} 
0

Для интегрального типа я обычно работают на жидком с помощью:

class Curve 
{ 
    public: 
     enum 
     { 
      CONTROL_POINT_COUNT = 8 
     }; 
}; 

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

namespace 
{ 
const int CONTROL_POINT_COUNT = 8; 
} 
0

Константы вообще не должны определяться внутри методов. Выбранный пример имеет две уникальные функции. Во-первых, это геттер; во-вторых, возвращаемый тип - это int. Но точка определения констант заключается в том, чтобы использовать их более одного раза и иметь возможность обращаться к ним удобным образом. Ввод «8» в отличие от «controlPointCount» может сэкономить ваше время и может показаться нецелесообразным для расходов на обслуживание, но это обычно не так, если вы всегда определяете константы внутри методов.

 Смежные вопросы

  • Нет связанных вопросов^_^