Projetando interfaces para hardware

5

Estou escrevendo uma biblioteca em C ++ que é usada pelos clientes para fazer interface com o hardware. Existem muitos dispositivos diferentes que eu preciso suportar.

Por razões de simplicidade, suponha que eu tenha 2 Widgets, WidgetA e WidgetB, que são dispositivos físicos. Ambos os Widgets se comunicam usando o mesmo protocolo e compartilham muito em comum. Ambos suportam 20 das mesmas configurações genéricas. No entanto, o WidgetA suporta 10 configurações exclusivas e o WidgetB suporta 10 configurações diferentes. O WidgetB também possui algumas funções exclusivas.

O usuário da minha biblioteca pode saber de antemão qual Widget está usando (por exemplo, se um usuário comprasse apenas 1 Widget e quisesse usar minha biblioteca para se comunicar com ele, provavelmente o usaria sabendo que só precisa se comunicar com ele. WidgetA). No entanto, existem outros cenários em que o usuário deseja gravar um aplicativo que manipule uma variedade de Widgets. O tipo de widget teria que ser determinado em tempo de execução, interrogando o dispositivo real.

Então, minha pergunta é: Qual deve ser o design dessa biblioteca? Eu estava pensando nas seguintes opções:

1) Apenas força bruta e coloque todas as funções em uma classe Widget genérica. O Widget físico seria verificado antes de executar um comando e um erro ocorreria se tentasse usar uma função não suportada pelo dispositivo. Isso acaba sendo uma série de funções em uma classe, sem nenhum objetivo à vista, já que os novos Widgets precisam ser suportados no futuro.

class Widget
{
    void setAddress(int);       //generic config setting
    void setUniqueOption1(int); //config only supported by physical WidgetA's
    void setUniqueOption2(int); //config only supported by physical WidgetB's
    ...                         //repeat for all config and functions supported by any Widget!
}

2) Adicione uma classe separada para cada Widget (ou cada Widget que tenha recursos exclusivos). Essa parece ser uma boa ideia, mas os usuários precisariam criar primeiro um Widget genérico primeiro, depois determinar com que tipo de dispositivo físico eles estão falando e criar a classe WidgetA específica. para acessar essa funcionalidade.

class Widget
{
    void setAddress(int);       //generic config setting
}

class WidgetA : public Widget
{
    void setUniqueOption1(int); //config only supported by physical WidgetA's
}

class WidgetB: public Widget
{
    void setUniqueOption2(int); //config only supported by physical WidgetB's
}

3) Crie uma classe virtual pura ConfigOption . Crie classes de configuração para cada Widget que contenha opções que o usuário configura todas de uma vez. O usuário usa apenas a classe Widget genérica que tem uma função setCustomConfig que usa essas classes de configuração de widget personalizadas.

class Widget
{
    void setAddress(int);                      //generic config setting
    void setCustomConfig(const ConfigOption&); //used to set all custom config settings
}

class ConfigOption = 0;

class WidgetA_Config : public ConfigOption
{
    //user sets all the options, then sends the object to the Widget::setCustomConfig
    int optionA1;
    int optionA2;
    int optionA3;
}

class WidgetB_Config : public ConfigOption
{
    int optionB1;
    int optionB2;
}

Esta é minha primeira vez tentando escrever uma boa interface para fazer interface com o hardware, o que está jogando uma chave na minha lógica normal que uso para escrever código. Alguma dessas idéias é a abordagem correta? Ou há algum design que esteja faltando?

Na realidade, há ~ 30 desses Widgets e o potencial para mais a cada dia. Alguns são 99% semelhantes ao que eu consideraria o "Widget base", enquanto outros podem ter esses recursos exclusivos (configuração e funcionalidade).

    
por rwstoneback 10.01.2015 / 19:42
fonte

1 resposta

4

Acho que você pode estar ficando preso tentando fazer com que sua hierarquia de classes se encaixe em uma taxonomia do mundo real, e essa nem sempre é a melhor abordagem.

Primeiro, os objetos quase sempre são criados por algum tipo de fábrica nessa situação. Você chama uma função probe que retorna uma lista de todos os Widgets conectados ao seu sistema, já instanciados. Então você pode configurá-lo.

O modelo que vejo com mais frequência para um dispositivo de hardware é uma estrutura de dados de recursos. Pegue um mouse, por exemplo. Todos eles têm pelo menos dois botões e pelo menos dois eixos, mas alguns têm muito mais. Em vez de ter uma base Mouse classe com classes derivadas como ThreeButtonWithScroll , cada uma delas tem uma coleção Axes e uma Buttons . O usuário da classe não tem permissão para adicionar ou remover um eixo ou botão, mas ele pode definir propriedades em cada um deles.

Digamos que você tenha algum tipo de mouse esquisito sem botões. Sua coleção Buttons seria apenas vazia e, portanto, não configurável. Você não está expondo nenhuma interface que não possa ser usada.

Se a sua lista de recursos for tão grande que você usaria apenas uma pequena parte deles em qualquer dispositivo, poderá criar uma matriz de Features na sua classe Widget . Onde Feature pode ser o único recurso de alguns mouses, como Vibration ou BacklightColor . Eles também podem ser relativamente genéricos, como BooleanFeature("Vibration", disabled, callback) .

    
por 10.01.2015 / 21:50
fonte