Qual é a idéia por trás da nomeação de classes com o sufixo "Info", por exemplo: "SomeClass" e "SomeClassInfo"?


20

Estou trabalhando em um projeto que lida com dispositivos físicos e estou confuso sobre como nomear corretamente algumas classes neste projeto.

Considerando que os dispositivos reais (sensores e receptores) são uma coisa, e sua representação no software é outra, estou pensando em nomear algumas classes com o padrão de nome de sufixo "Info".

Por exemplo, enquanto a Sensorseria uma classe para representar o sensor real (quando está realmente conectado a algum dispositivo em funcionamento), SensorInfoseria usado para representar apenas as características desse sensor. Por exemplo, ao salvar o arquivo, eu serializaria SensorInfoa no cabeçalho do arquivo, em vez de serializar a Sensor, o que nem faria sentido.

Mas agora estou confuso, porque existe um campo intermediário no ciclo de vida dos objetos em que não consigo decidir se devo usar um ou outro, ou como obter um do outro, ou mesmo se as duas variantes devem realmente ser reduzidas a apenas uma classe.

Além disso, a Employeeclasse de exemplo muito comum obviamente é apenas uma representação da pessoa real, mas ninguém sugeriria o nome da classe EmployeeInfo, tanto quanto eu sei.

A linguagem com a qual estou trabalhando é .NET e esse padrão de nomenclatura parece ser comum em toda a estrutura, por exemplo, com estas classes:

  • Directorye DirectoryInfoaulas;
  • Filee FileInfoaulas;
  • ConnectionInfoclasse (sem Connectionclasse correspondente );
  • DeviceInfoclasse (sem Deviceclasse correspondente );

Portanto, minha pergunta é: existe uma lógica comum sobre o uso desse padrão de nomenclatura? Existem casos em que faz sentido ter pares de nomes ( Thinge ThingInfo) e outros casos em que deve existir apenas a ThingInfoclasse ou a Thingclasse sem sua contraparte?


5
Ben Aaronson acertou em sua resposta abaixo; o Infosufixo distingue uma staticclasse que contém métodos utilitários de sua contraparte com estado. Não é uma "melhor prática" como tal; é apenas uma maneira que a equipe do .NET criou para resolver um problema específico. Eles poderiam ter apenas como facilmente chegar a FileUtilitye File, mas File.DoSomething()e FileInfo.FileNameparecem ler melhor.
Robert Harvey

1
Vale a pena notar que esse é um padrão comum, mas com tantas convenções de nomenclatura diferentes quanto idiomas e APIs. Em Java, por exemplo, se você tem uma classe com estado Foo, pode ter uma classe de utilitário não instanciada Foos. Quando se trata de nomear, o importante é a consistência na API e, idealmente, nas APIs da plataforma.
Kevin Krumwiede

1
Sério? "Ninguém sugeriria o nome da classe EmployeeInfo" ?? NINGUÉM? Isso parece um pouco forte para algo não tão óbvio.
ThePopMachine

@ThePopMachine Concordo que a palavra "ninguém" pode ser muito difícil nesse caso, mas Employeeexemplos são encontrados por dezenas, online ou em livros clássicos, enquanto eu ainda não o vi EmployeeInfo(talvez porque um funcionário seja um ser vivo, não uma construção técnica como uma conexão ou um arquivo). Mas, concordou, se a classe EmployeeInfofosse proposta em um projeto, acredito que poderia ser útil.
Heltonbiker

Respostas:


21

Eu acho que "info" é um nome impróprio. Os objetos têm estado e ações: "info" é apenas outro nome para "state", que já está inserido no OOP.

O que você realmente está tentando modelar aqui? Você precisa de um objeto que represente o hardware do software para que outro código possa usá-lo.

É fácil dizer, mas como você descobriu, há mais do que isso. "Representar hardware" é surpreendentemente amplo. Um objeto que faz isso tem várias preocupações:

  • Comunicação de dispositivo de baixo nível, seja conversando com a interface USB, uma porta serial, TCP / IP ou conexão proprietária.
  • Gerenciando estado. O dispositivo está ligado? Pronto para conversar com o software? Ocupado?
  • Manipulação de eventos. O dispositivo produziu dados: agora precisamos gerar eventos para passar para outras classes que estão interessadas.

Certos dispositivos, como sensores, terão menos preocupações do que um dispositivo multifuncional para impressora / scanner / fax. Um sensor provavelmente produz apenas um fluxo de bits, enquanto um dispositivo complexo pode ter protocolos e interações complexos.

De qualquer forma, voltando à sua pergunta específica, existem várias maneiras de fazer isso, dependendo de seus requisitos específicos e da complexidade da interação do hardware.

Aqui está um exemplo de como eu projetaria a hierarquia de classes para um sensor de temperatura:

  • ITemperatureSource: interface que representa qualquer coisa que possa produzir dados de temperatura: um sensor, pode até ser um invólucro de arquivo ou dados codificados (pense em testes simulados).

  • Acme4680Sensor: sensor do modelo 4680 da ACME (ótimo para detectar quando o Roadrunner está próximo). Isso pode implementar várias interfaces: talvez esse sensor detecte temperatura e umidade. Este objeto contém um estado no nível do programa, como "o sensor está conectado?" e "qual foi a última leitura?"

  • Acme4680SensorComm: usado apenas para comunicação com o dispositivo físico. Não mantém muito estado. É usado para enviar e receber mensagens. Possui um método C # para cada uma das mensagens que o hardware entende.

  • HardwareManager: usado para obter dispositivos. Esta é essencialmente uma fábrica que armazena em cache instâncias: deve haver apenas uma instância de um objeto de dispositivo para cada dispositivo de hardware. É preciso ser inteligente o suficiente para saber que, se o encadeamento A solicita o sensor de temperatura ACME e o encadeamento B solicita o sensor de umidade ACME, esses objetos são realmente o mesmo objeto e devem ser retornados aos dois encadeamentos.


No nível superior, você terá interfaces para cada tipo de hardware. Eles descrevem as ações que seu código C # executaria nos dispositivos, usando tipos de dados C # (não por exemplo, matrizes de bytes que o driver de dispositivo bruto pode usar).

No mesmo nível, você tem uma classe de enumeração com uma instância para cada tipo de hardware. O sensor de temperatura pode ser de um tipo, o sensor de umidade, outro.

Um nível abaixo disso são as classes reais que implementam essas interfaces: elas representam um dispositivo semelhante ao Acme4680Sensor I descrito acima. Qualquer classe específica pode implementar várias interfaces se o dispositivo puder executar várias funções.

Cada classe de dispositivo tem sua própria classe Comm (comunicação) privada que lida com a tarefa de baixo nível de conversar com o hardware.

Fora do módulo de hardware, a única camada visível é a interface / enum mais o HardwareManager. A classe HardwareManager é a abstração de fábrica que lida com a instanciação de classes de dispositivos, instâncias de cache (você realmente não deseja duas classes de dispositivos conversando com o mesmo dispositivo de hardware), etc. Uma classe que precisa de um tipo específico de sensor solicita que o HardwareManager obtenha o dispositivo para a enum em particular, que ele descobre se já está instanciado, se não como criá-lo e inicializá-lo etc.

O objetivo aqui é dissociar a lógica de negócios da lógica de hardware de baixo nível. Quando você estiver escrevendo um código que imprime os dados do sensor na tela, esse código não deve se importar com o tipo de sensor que você possui, e apenas se esse desacoplamento estiver no local, centralizado nessas interfaces de hardware.


Exemplo de diagrama de classe UML mostrando o design descrito nesta resposta

Nota: existem associações entre o HardwareManager e cada classe de dispositivo que eu não desenhei porque o diagrama teria se transformado em sopa de flechas.


Resposta muito interessante. Eu pediria uma edição rápida: deixe mais explícito qual é a herança / contenção / colaboração entre as quatro classes que você mencionou. Muito obrigado!
heltonbiker

Eu adicionei um diagrama de classes UML. Isso ajuda?

2
Eu diria que esta é uma resposta matadora , e não só vai me ajudar muito, mas acredito que poderia ajudar muito mais pessoas no futuro. Além disso, o exemplo de hierarquia de classes que você fornece mapeia nossos domínios de problemas e soluções notavelmente bem. Muito obrigado novamente!
Heltonbiker

Quando li a pergunta, percebi que havia mais coisas acontecendo, por isso exagerei um pouco e compartilhei todo o design. Isso é mais do que um simples problema de nomeação, porque os nomes são indicativos do design. Eu trabalhei com sistemas criados dessa maneira e eles funcionaram bastante bem.

11

Pode ser um pouco difícil encontrar uma única convenção unificadora aqui, porque essas classes estão espalhadas por vários espaços de nomes ( ConnectionInfoparece estar dentro CrystalDecisionse DeviceInfodentro System.Reporting.WebForms).

Olhando para esses exemplos, porém, parece haver dois usos distintos do sufixo:

  • Distinguindo uma classe que fornece métodos estáticos com uma classe que fornece métodos de instância. Este é o caso das System.IOclasses, conforme sublinhado por suas descrições:

    Diretório :

    Expõe métodos estáticos para criar, mover e enumerar através de diretórios e subdiretórios. Essa classe não pode ser herdada.

    DirectoryInfo :

    Expõe métodos de instância para criar, mover e enumerar através de diretórios e subdiretórios. Essa classe não pode ser herdada.

    Infoparece uma escolha um pouco estranha aqui, mas torna a diferença relativamente clara: uma Directoryclasse poderia razoavelmente representar um diretório específico ou fornecer métodos auxiliares gerais relacionados ao diretório sem manter nenhum estado, enquanto DirectoryInfosó poderia realmente ser o primeiro.

  • Enfatizando que a classe contém apenas informações e não fornece comportamento razoavelmente esperado do nome sem sufixo .

    Eu acho que a última parte dessa frase pode ser a peça do quebra-cabeça que distingue, digamos, ConnectionInfode EmployeeInfo. Se eu tivesse uma classe chamada Connection, esperaria razoavelmente que ela realmente me fornecesse a funcionalidade que uma conexão possui - eu estaria procurando métodos como void Open()etc. No entanto, ninguém em sã consciência esperaria que uma Employeeclasse pudesse realmente faça o que um real Employeefaz ou procure métodos como void DoPaperwork()ou bool TryDiscreetlyBrowseFacebook().


1
Muito obrigado pela sua resposta! Acredito que o primeiro marcador em sua resposta descreva definitivamente o que acontece nas classes .NET, mas o segundo tem mais valor (e, a propósito, é diferente), porque revela melhor a abstração pretendida: algo a ser usado vs. coisa a ser descrita. Era difícil escolher qual resposta aceitar.
Heltonbiker

4

Em geral, um Infoobjeto encapsula informações sobre o estado de um objeto em algum momento no tempo . Se eu pedir ao sistema que examine um arquivo e me forneça um FileInfoobjeto associado ao seu tamanho, esperaria que esse objeto informasse o tamanho do arquivo no momento em que a solicitação foi fornecida (ou, para ser mais preciso, o tamanho de o arquivo em algum momento entre quando a chamada foi feita e quando ela retornou). Se o tamanho do arquivo mudar entre a hora em que a solicitação retorna e a hora em que o FileInfoobjeto é examinado, eu não esperaria que essa alteração se refletisse no FileInfoobjeto.

Observe que esse comportamento seria muito diferente daquele de um Fileobjeto. Se uma solicitação para abrir um arquivo de disco no modo não exclusivo produz um Fileobjeto que possui uma Sizepropriedade, eu esperaria que o valor retornado desse modo mudasse quando o tamanho do arquivo de disco mudar, pois o Fileobjeto não representa apenas o estado de um arquivo - representa o próprio arquivo .

Em muitos casos, os objetos anexados a um recurso devem ser limpos quando seus serviços não forem mais necessários. Como os *Infoobjetos não se anexam aos recursos, eles não requerem limpeza. Como conseqüência, nos casos em que um Infoobjeto satisfaz os requisitos de um cliente, pode ser melhor usar um código do que usar um objeto que represente o recurso subjacente, mas cuja conexão com esse recurso precisaria ser limpa.


1
Isso não está exatamente errado, mas não parece explicar muito bem os padrões de nomenclatura do .NET, pois a Fileclasse não pode ser instanciada e os FileInfoobjetos são atualizados com o sistema de arquivos subjacente.
Nathan Tuggy

@ NathanTuggy Concordo que isso não se relaciona bem com esta convenção de nomenclatura no .NET, mas em relação ao meu problema atual, acredito que seja muito relevante e possa induzir uma solução consistente. I upvoted esta resposta, mas está sendo difícil escolher apenas um para aceitar ...
heltonbiker

@ NathanTuggy: eu não recomendaria o uso do .NET como modelo de qualquer tipo de consistência. É uma estrutura boa e útil que encapsula muitas boas idéias, mas algumas idéias ruins também são capturadas na mistura.
Supercat 8/15

@ supercat: Claro; parece estranho responder a uma pergunta sobre "por que o .NET faz as coisas desta maneira?" com "seria uma boa ideia fazer as coisas% THIS_WAY%".
Nathan Tuggy

@ NathanTuggy: Eu não me lembrava dos detalhes exatos das aulas em questão, mas pensei que FileInfotinha apenas informações capturadas estaticamente. Não é? Além disso, nunca tive oportunidade de abrir arquivos no modo não exclusivo, mas deveria ser possível, e esperaria que exista um método que relate o tamanho atual de um arquivo, mesmo que o objeto usado para abrir não seja chamada File(normalmente eu só uso ReadAllBytes, WriteAllBytes, ReadAllText, WriteAllText, etc.).
supercat

2

Considerando que os dispositivos reais (sensores e receptores) são uma coisa, e sua representação no software é outra, estou pensando em nomear algumas classes com o padrão de nome de sufixo "Info".

Por exemplo, enquanto a Sensorseria uma classe para representar o sensor real (quando está realmente conectado a algum dispositivo em funcionamento), SensorInfoseria usado para representar apenas as características desse sensor. Por exemplo, ao salvar o arquivo, eu serializaria SensorInfoa no cabeçalho do arquivo, em vez de serializar a Sensor, o que nem faria sentido.

Eu não gosto dessa distinção. Todos os objetos são "representações [s] no software". É o que a palavra "objeto" significa.

Agora, pode fazer sentido separar informações sobre um periférico do código real que faz interface com o periférico. Assim, por exemplo, um Sensor possui um SensorInfo, que contém a maioria das variáveis ​​de instância, juntamente com alguns métodos que não requerem hardware, enquanto a classe Sensor é responsável por interagir de fato com o sensor físico. Você não tem um, a Sensormenos que seu computador tenha um sensor, mas você poderia ter um plausível SensorInfo.

O problema é que esse tipo de design pode ser generalizado para (quase) qualquer classe. Então você tem que ter cuidado. Você obviamente não teria SensorInfoInfoaula, por exemplo. E se você tem uma Sensorvariável, pode se ver violando a lei de Demeter interagindo com seu SensorInfomembro. Nada disso é fatal, é claro, mas o design da API não é apenas para autores de bibliotecas. Se você manter sua própria API limpa e simples, seu código será mais sustentável.

Recursos de sistema de arquivos como diretórios, na minha opinião, estão muito próximos dessa borda. Há algumas situações em que você deseja descrever um diretório que não é acessível localmente, é verdade, mas o desenvolvedor médio provavelmente não está em uma dessas situações. Complicar a estrutura de classes dessa maneira é, em minha opinião, inútil. Contraste a abordagem do Python em pathlib: Há uma classe única que "provavelmente é o que você precisa" e várias classes auxiliares que a maioria dos desenvolvedores pode ignorar com segurança. Se você realmente precisar deles, no entanto, eles fornecem basicamente a mesma interface, apenas com métodos concretos removidos.


Obrigado pela sua resposta e parabéns pelo construto "tenha um";) Sua resposta me lembra o desconforto causado por alguns pelo conceito "DTO" (objeto de transferência de dados) - ou por seu irmão, o objeto de parâmetro - que é desaprovado. por mais fanáticos ortodoxos de OO porque geralmente não contém métodos (afinal de contas, é um objeto de transferência de dados ). Nesse sentido, e resumindo também o que os outros disseram, acho que SensorInfoseria uma espécie de DTO e / ou também como um "instantâneo" ou mesmo uma "especificação", representando apenas a parte de dados / estado do Sensorobjeto real que "pode ​​nem estar lá".
Heltonbiker

Como na maioria dos problemas de design, não há regras rígidas. No exemplo do pathlib que forneci, PureWindowsPathfunciona um pouco como um objeto Info, mas possui métodos para fazer coisas que não exigem um sistema Windows (por exemplo, pegar um subdiretório, separar uma extensão de arquivo etc.). Isso é mais útil do que apenas fornecer uma estrutura glorificada.
Kevin

1
Mais uma vez, parabéns pela parte "glorificada da estrutura" :). Isso me lembra uma citação relacionada do livro "Código Limpo" do tio Bob, capítulo 6: "Programadores maduros sabem que a idéia de que tudo é um objeto é um mito. Às vezes você realmente deseja estruturas de dados simples com procedimentos operando neles".
Heltonbiker #

2

Eu diria que o contexto / domínio é importante, pois temos código de lógica de negócios de alto nível e modelos de baixo nível, componentes de arquitetura e assim por diante ...

'Info', 'Dados', 'Gerente', 'Objeto', 'Classe', 'Modelo', 'Controlador' etc. podem ser sufixos malcheirosos, especialmente em um nível inferior, pois cada objeto tem alguma informação ou dados, portanto essa informação não é necessária.

Os nomes de classe do domínio comercial devem ser como todos os interessados ​​falam sobre o assunto, não importa se ele soa estranho ou não é um idioma 100% correto.

Bons sufixos para estruturas de dados são, por exemplo, 'Lista', 'Mapa' e, para os padrões de sugestão 'Decorador', 'Adaptador', se você achar necessário.

No cenário do seu sensor, eu não esperaria SensorInfosalvar qual é o seu sensor, mas SensorSpec. Infoimho é mais uma informação derivada, como FileInfoé algo como o tamanho, você não salva ou o caminho do arquivo que é construído a partir do caminho e do nome do arquivo, etc.

Outro ponto:

Há apenas duas coisas difíceis na Ciência da Computação: invalidação de cache e nomeação de coisas.

- Phil Karlton

Isso sempre me lembra de pensar em um nome apenas por alguns segundos e, se não encontrei, uso nomes estranhos e marquei 'TODO'. Sempre posso alterá-lo mais tarde, pois meu IDE fornece suporte à refatoração. Não é um slogan da empresa que deve ser bom, mas apenas alguns códigos que podemos mudar sempre que quisermos. Tenha isso em mente.


1

O ThingInfo pode servir como um excelente Proxy somente leitura para o Thing.

consulte http://www.dofactory.com/net/proxy-design-pattern

Proxy: "Forneça um substituto ou espaço reservado para outro objeto para controlar o acesso a ele".

Normalmente, o ThingInfo terá propriedades públicas sem setters. Essas classes e os métodos da classe são seguros de usar e não confirmarão nenhuma alteração nos dados de backup, no objeto ou em qualquer outro objeto. Nenhuma mudança de estado ou outros efeitos colaterais ocorrerão. Eles podem ser usados ​​para relatórios e serviços da Web ou em qualquer lugar em que você precise de informações sobre o objeto, mas queira limitar o acesso ao próprio objeto.

Use o ThingInfo sempre que possível e limite o uso do Thing real aos horários em que você realmente precisa alterar o objeto Thing. Isso torna a leitura e a depuração consideravelmente mais rápidas quando você se acostuma a usar esse padrão.


Excelente. Hoje cedo eu estava analisando minhas necessidades arquitetônicas, em relação às classes que usei na pergunta, e cheguei a essa mesma conclusão. No meu caso, eu tenho um Receiverque recebe fluxos de dados de muitos Sensors. A idéia é: o receptor deve abstrair os sensores reais. Mas o problema é: preciso das informações do sensor para poder escrevê-las em algum cabeçalho do arquivo. A solução: cada IReceiverum terá uma lista de SensorInfo. Se eu enviar comandos ao receptor que impliquem alteração do estado do sensor, essas alterações serão refletidas (via getter) no respectivo SensorInfo.
Heltonbiker

(continuação ...) ou seja: não falo com os próprios sensores, falo apenas com o receptor por meio de comandos de nível superior, e o receptor, por sua vez, conversa com cada um dos sensores. Mas, eventualmente, preciso de informações dos sensores, que recebo da instância Receiver por meio de sua List<SensorInfo>propriedade readonly.
Heltonbiker

1

Até agora, ninguém nesta questão parece ter percebido o real motivo dessa convenção de nomes.

A DirectoryInfonão é o diretório. É um DTO com dados sobre o diretório. Pode haver muitas instâncias desse tipo descrevendo o mesmo diretório. Não é uma entidade. É um objeto de valor descartável. A DirectoryInfonão representa o diretório real. Você também pode pensar nisso como um identificador ou controlador para um diretório.

Compare isso com uma classe chamada Employee. Esse pode ser um objeto de entidade ORM e é o único objeto que descreve esse funcionário. Se fosse um objeto de valor sem identidade, deveria ser chamado EmployeeInfo. Um Employeerealmente representa o funcionário real. Uma classe DTO semelhante a valor chamada EmployeeInfoclaramente não representaria o funcionário, mas a descreveria ou armazenaria dados sobre ela.

Na verdade, há um exemplo na BCL em que ambas as classes existem: A ServiceControlleré uma classe que descreve um serviço do Windows. Pode haver qualquer número desses controladores para cada serviço. Uma ServiceBase(ou uma classe derivada) é o serviço real e não faz sentido conceitualmente ter várias instâncias dele por serviço distinto.


Isso soa semelhante ao Proxypadrão mencionado por @Shmoken, não é?
Heltonbiker

@heltonbiker, não precisa necessariamente ser um proxy. Pode ser um objeto que não esteja conectado de forma alguma à coisa que descreve. Por exemplo, você pode obter um EmployeeInfode um serviço da web. Isso não é um proxy. Exemplo adicional: Um AddressInfonem sequer tem algo que pode proxy porque um endereço não é uma entidade. É um valor independente.
usr

1
Entendo, isso faz sentido!
Heltonbiker
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.