É melhor constantes da classe loja em membros de dados ou métodos?
Pergunta
Eu escrevi recentemente uma classe que torna curvas B-spline. Estas curvas são definidos por um número de pontos de controlo. Originalmente, eu tinha a intenção de usar oito pontos de controle, então eu adicionei uma constante para a classe, assim:
class Curve
{
public:
static const int CONTROL_POINT_COUNT = 8;
};
Agora eu quero estender esta classe para permitir que uma quantidade arbitrária de pontos de controle. Então eu quero mudar isso para:
class Curve
{
public:
int getControlPointCount() {return _controlPointCount;}
};
A questão é se não melhor é armazenar constantes em métodos para começar, para facilitar a adaptabilidade. Em outras palavras, não é melhor ter começado assim:
class Curve
{
public:
int getControlPointCount() {return 8;}
};
A vantagem disto é que eu poderia ter apenas mudou um símbolo no método em questão, em vez de se movimentar constantes etc.
Esta é uma boa prática ou ruim?
Solução
Normalmente sou a favor de manter o menor número de acoplamentos manualmente possível.
O número de pontos de controlo na curva é, assim, o número de pontos de controlo na curva. Não é uma variável independente que pode ser ajustado à vontade.
Então, eu costumo exporia uma referência contêiner padrão const:
class Curve
{
private:
std::vector<Point>& _controlPoints;
public:
Curve ( const std::vector<Point>& controlPoints) : _controlPoints(controlPoints)
{
}
const std::vector<Point>& getControlPoints ()
{
return _controlPoints;
}
};
E se você quiser saber quantos pontos de controle, em seguida, usar curve.getControlPoints().size()
. Eu suspeito que, na maioria dos casos de uso que você iria querer os pontos, bem como a contagem de qualquer maneira, e expondo um contêiner padrão que você pode usar a expressões idiomáticas iteradoras e built-in algoritmos da biblioteca padrão, em vez recebendo a contagem e chamada uma função como getControlPointWithIndex
num ciclo.
Se não há realmente nada mais na classe curva, eu poderia até mesmo ir tão longe como:
typedef std::vector<Point> Curve;
(muitas vezes uma curva não vai tornar-se, como uma classe processador pode ter detalhes sobre o pipeline de processamento, deixando uma curva como puramente o artefato geométrica)
Outras dicas
int getControlPointCount() {return _controlPointCount;}
Este é um acessor. Trocar uma estática const para um acessor não é realmente um ganho como litb assinalou. O que você realmente precisa para à prova de futuro é provavelmente um par de acessor e modificador.
int getControlPointCount() {return _controlPointCount;} // accessor
Eu também jogar em um projeto-const para o acessor e torná-lo:
int getControlPointCount() const {return _controlPointCount;} // accessor
e o correspondente:
void setControlPointCount(int cpc) { _controlPointCount = cpc;} //mutator
Agora, a grande diferença com um objeto estático é que a contagem de ponto de controle não é mais um atributo de nível de classe mas um nível de instância. Esta é uma alteração de design . Você quer que ele desse jeito?
Nit:. A contagem estática nível de classe é public
e, portanto, não precisa de um sistema de acesso
Para responder melhor a sua pergunta, a pessoa também deve saber como a variável controlPointCount está definido. É definido fora de sua classe? Neste caso, você também deve definir um setter. Ou a classe Curve é o único responsável por defini-lo? É definido apenas em tempo de compilação ou também em tempo de execução.
De qualquer forma, evitar um número mágico, mesmo nesta forma:
int getControlPointCount() {return 8;}
Este é melhor:
int getControlPointCount() {return CONTROL_POINT_COUNT;}
Um método tem a vantagem de que você pode modificar a implementação interna (use um valor constante, ler a partir de um arquivo de configuração, alterar o valor dinamicamente), sem afetar o externo da classe.
class Curve
{
private:
int _controlPointCount;
void setControlPointCount(int cpc_arg)
{
_controlPointCount = cpc_arg;
}
public:
curve()
{
_controlPointCount = 8;
}
int getControlPointCount() const
{
return _controlPointCount;
}
};
Vou criar um código como este, com a função set em privado, de modo que nenhum corpo pode jogar com contagem de pontos de controle, até que passar para a próxima fase da development..where atualização começamos a atualizar a contagem de ponto de controle em tempo de execução. naquele tempo, podemos passar este método set do privado ao alcance do público.
Enquanto a compreensão da questão, tenho uma série de problemas conceituais com o exemplo:
- O que é o valor de retorno para
getControlPointCount()
quando o número de pontos de controle não é limitado?- É MAXINT?
- É o número atual de pontos de controle na curva (quebrando assim a lógica que diz que este é o maior número possível de pontos?)
- O que acontece quando você realmente tentar criar uma curva com pontos maxint? Você vai ficar sem memória eventualmente.
A interface em si parece problemático para mim. Como outras classes de coleção padrão, a classe deveria ter encapsulado sua limitação no número de pontos, e sua AddControlPoint()
deveria ter retornado um erro se uma limitação do tamanho, memória ou qualquer outra violação ocorreu.
Quanto à resposta específica, eu concordo com kgiannakakis: a função de membro permite mais flexibilidade
.I tendem a usar a configuração + constante (valor padrão) para todos os valores 'estável' através da execução do programa. Com constantes simples para valores que não pode mudar (360 graus -> 2 pi radianos, 60 segundos -> 1 minuto). Ou cujos mudança iria quebrar o código de execução (mínimo / valores máximos para os algoritmos que os tornam instáveis)
Você está lidando com alguns problemas de design diferentes. Primeiro você deve saber se o número de pontos de controle é um valor do nível de classe ou instância. Então se é uma constante em qualquer um dos dois níveis.
Se todas as curvas devem compartilhar o mesmo número de pontos de controle em sua aplicação então é um valor de nível de classe (estática). Se diferentes curvas pode ter um número diferente de pontos de controle, então não é um valor de nível de classe, mas sim um nível de instância um.
Neste caso, se o número de pontos de controle será constante durante toda a vida da curva, então é uma constante nível de instância, se ele pode mudar, então não é constante a este nível também.
// 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() );
}
Por tipo integral Estou normalmete usando:
class Curve
{
public:
enum
{
CONTROL_POINT_COUNT = 8
};
};
Se constante não precisa de quaisquer entidades, exceto implementação de classe I declarar constantes em arquivo .cpp *.
namespace
{
const int CONTROL_POINT_COUNT = 8;
}
Em geral, todos os seus dados deve ser privado e acessados ??através de getters e setters. Caso contrário, você violar o encapsulamento. Ou seja, se você expor os dados subjacentes você se tranca e sua classe em uma representação particular de que os dados subjacentes.
Neste caso específico, eu teria feito a algo como o seguinte eu penso:
class Curve
{
protected:
int getControlPointCount() {return _controlPointCount;}
int setControlPointCount(int c) { _controlPointCount = c; }
private:
static int _controlPointCount = 0;
};
Constantes em geral não deve ser definido dentro de métodos. O exemplo que você está escolhendo tem duas características únicas. Primeiro, é um getter; em segundo lugar, o tipo a ser devolvido é um int. Mas o ponto de constantes que definem é usá-los mais de uma vez, e para ser capaz de se referir a eles de uma forma conveniente. Typing "8" em oposição a "controlPointCount" pode poupar tempo e pode não parecer a incorrer em um custo de manutenção, mas isso não será tipicamente verdadeiro se você sempre definir constantes dentro de métodos.