Qual é melhor: um monte de getters ou 1 método com um parâmetro de string de seleção?

15

Nosso domínio de conhecimento envolve pessoas caminhando sobre uma placa de registro de pressão com os pés descalços. Fazemos reconhecimento de imagem que resulta em objetos da classe 'Foot', se um pé humano for reconhecido nos dados do sensor.

Existem vários cálculos que devem ser realizados nos dados do pé.

Agora, qual API seria melhor:

class Foot : public RecognizedObject  { 
  MaxPressureFrame getMaxPressureFrame();
  FootAxis getFootAxis();
  AnatomicalZones getAnatomicalZones();

  // + similar getters for other calculations

  // ...
}

Ou:

class Foot : public RecognizedObject {
  virtual CalculationBase getCalculation(QString aName);

  // ...
}

Agora, existem muitos prós e contras que posso sugerir, mas não posso decidir quais são os mais importantes. Note que este é um aplicativo de usuário final, não uma biblioteca de software que vendemos.

Algum conselho?

Alguns pro para a primeira abordagem podem ser:

  • KISS - tudo é muito concreto. A API, mas a implementação também.
  • valores de retorno strongmente tipados.
  • herdar desta classe é à prova de erros. Nada pode ser substituído, apenas adicionado.
  • A API é muito fechada, nada entra, nada pode ser substituído, portanto, menos pode dar errado.

Alguns trapaceiros:

  • O número de getters aumenta, pois cada novo cálculo que inventamos é adicionado à lista
  • É mais provável que a API
  • mude e, se forem introduzidas alterações significativas, precisamos de uma nova versão da API, um Foot2.
  • em caso de reutilização da turma em outros projetos, talvez não precisemos de todos os cálculos

Alguns profissionais para a segunda abordagem:

  • mais flexível
  • é menos provável que a API mude (supondo que a abstração esteja correta, senão, a mudança custará mais)

Alguns trapaceiros:

  • vagamente digitado. Precisa de lançamentos em todas as chamadas.
  • o parâmetro de string - eu tenho sentimentos ruins sobre isso (ramificação em valores de string ...)
  • Não há nenhum caso / requisito de uso atual que exija a flexibilidade extra, mas pode haver no futuro.
  • a API impõe restrições: todo cálculo precisa derivar de uma classe base. Obter um cálculo será forçado através deste método 1, e passar parâmetros extras será impossível, a menos que planejemos uma maneira ainda mais dinâmica e super flexível de passar parâmetros que aumentem ainda mais a complexidade.
por Bgie 28.01.2014 / 15:23
fonte

2 respostas

6

Acho que na primeira abordagem valerá a pena. Strings mágicas podem criar os seguintes problemas: Erros de digitação, uso incorreto, segurança de tipo de retorno não trivial, falta de conclusão de código, código confuso (essa versão tinha esse recurso? Acho que descobriremos em tempo de execução). O uso de enums resolverá alguns desses problemas, mas vamos analisar os contras que você levantou:

  • O número de getters aumenta, pois cada novo cálculo que inventamos é adicionado à lista

Na verdade, isso pode ser irritante, mas mantém as coisas legais e rigorosas e fornece a conclusão de código em qualquer lugar do projeto em qualquer IDE moderno, com comentários de cabeçalho muito mais úteis do que enums.

    É mais provável que a API
  • mude e, se forem introduzidas alterações significativas, precisamos de uma nova versão da API, um Foot2.

É verdade, mas isso é realmente um grande pro;) você pode definir interfaces para APIs parciais, e então você não precisa recompilar as classes dependentes que não são afetadas por APIs mais novas (portanto, não há necessidade de Foot2). Isso permite um melhor desacoplamento, a dependência é agora da interface e não da implementação. Além disso, se uma interface existente for alterada, você terá um erro de compilação nas classes dependentes, o que é ótimo para evitar o código obsoleto.

  • em caso de reutilização da turma em outros projetos, talvez não precisemos de todos os cálculos

Eu não vejo como usar strings mágicas ou enums ajudará com isso ... Se bem entendi, ou você inclui o código na classe Foot ou o divide em algumas classes menores, e isso vale para ambas as opções

    
por 28.01.2014 / 17:16
fonte
12

Eu recomendaria a opção 3: deixe claro que os cálculos não são uma parte intrínseca da abstração de um Foot , mas operem nele. Então você pode dividir Foot e os cálculos em classes separadas, como esta:

class Foot : public RecognizedObject {
public:
    // Rather low-level API to access all characteristics that might be needed by a calculation
};

class MaxPressureFrame {
public:
    MaxPressureFrame(const Foot& aFoot); // Performs the calculation based on the information in aFoot
    //API for accessing the results of the calculation
};

// Similar classes for other calculations

Dessa forma, você ainda terá uma strong digitação dos cálculos e poderá adicionar novos códigos sem afetar o código existente (a menos que você tenha cometido um erro grave na quantidade de informações exposta por Foot ).

    
por 28.01.2014 / 17:02
fonte