É um “cheiro de padrão” colocar getters como “FullName” ou “FormattedPhoneNumber” no seu modelo?

13

Estou trabalhando em um aplicativo ASP.NET MVC e tenho adquirido o hábito de colocar o que parecem ser úteis e convenientes getters em minhas classes de modelo / entidade.

Por exemplo:

public class Member
{
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string PhoneNumber { get; set; }

    public string FullName
    {
        get { return FirstName + " " + LastName; }
    }

    public string FormattedPhoneNumber
    {
        get { return "(" + PhoneNumber.Substring(0, 3) + ") " + PhoneNumber.Substring(3, 3) + "-" + PhoneNumber.Substring(6); }
    }
}

Eu estou querendo saber as pessoas pensam sobre o FullNamee FormattedPhoneNumbergetters.

Eles facilitam a criação de formatos de dados padronizados em todo o aplicativo e parecem economizar muito código repetido, mas definitivamente pode-se argumentar que o formato dos dados é algo que deve ser tratado no mapeamento de modelo para modelo de exibição.

De fato, originalmente eu estava aplicando esses formatos de dados na camada de serviço em que faço o mapeamento, mas estava se tornando um fardo precisar constantemente escrever formatadores e aplicá-los em muitos lugares diferentes. Por exemplo, eu uso "Nome completo" na maioria das visualizações e ter que digitar algo como em model.FullName = MappingUtilities.GetFullName(entity.FirstName, entity.LastName);todo o lugar parecia muito menos elegante do que apenas digitar model.FullName = entity.FullName(ou, se você usar algo como o AutoMapper, provavelmente não digita nada).

Então, onde você desenha a linha quando se trata de formatação de dados. É "bom" fazer a formatação de dados no seu modelo ou é um "cheiro de padrão"?

Nota: eu definitivamente não tenho nenhum html no meu modelo. Eu uso ajudantes html para isso. Estou falando estritamente sobre formatação ou combinação de dados (e especialmente dados usados ​​com freqüência).

devuxer
fonte
1
Fazer isso pode ser conveniente. Mas espere e ore para que você nunca precise internacionalizar esse código.
btilly
@btilly, bom ponto, mas estou com cerca de 99,99% de confiança de que não vou.
devuxer
Específico para FullName e PhoneNumber, definitivamente. A pergunta é específica sobre eles porque eles têm formatos inconsistentes em diferentes culturas ou o @DanM simplesmente escolheu exemplos que não se internacionalizam bem para uma pergunta mais geral?
Greg Jackson
@ Greg Jackson, definitivamente a escada. Como o ammoQ apontou, PhoneNumberprovavelmente pertence à sua própria classe (que eu implementei agora). Mas FullNamefoi realmente o que me motivou a escrever a pergunta. Mas estou interessado em descobrir se, em geral, faz sentido colocar a formatação / penteado de dados etc. no modelo para coisas que serão aplicadas em todo o aplicativo. A partir das respostas abaixo, parece que isso não é um antipadrão, mas a decisão deve ser tomada com cuidado.
Devuxer
Lembre-se de que essa formatação específica para o nome completo também pode não ser internacionalizada. Na Ásia Oriental, por exemplo, a ordem dos nomes é inversa à do mundo ocidental. Você realmente precisa lidar explicitamente com isso? Talvez não, mas lembre-se de que existem muitas coisas complicadas que você encontrará ao formatar dados.
Greg Jackson

Respostas:

9

No seu exemplo, eu gosto do FullNamegetter (por todos os motivos que você deu), mas não gosto do getter FormattedPhoneNumber. A razão é: provavelmente não é assim tão fácil (uma vez que você tem números internacionais de telefone etc.) e se você colocar a lógica de formatação números de telefone em um método de Member, as chances são que você vai precisar para refatorar (ou copiar e colar caugh ) uma vez que você também precisa de um número de telefone formatado Institution, Vendoretc.

EDIT: IMO, seria melhor ter uma PhoneNumberclasse com um Formattedgetter.

user281377
fonte
+1 e obrigado. Mas e se eu realmente colocar meu código de formatação de número de telefone em um método de extensão? Qualquer modelo poderia usá-lo e não haveria refatoração ou cópia / colagem. Essa solução ainda seria muito menos repetitiva do que aplicar o formatador a todos os números de telefone que aparecem em todas as visualizações.
Devuxer
3
Usar um método de extensão para isso seria um caminho rápido para o antipadrão de "decomposição funcional". Usando um método de extensão (para a Stringturma, presumo), você "ensina" a Stringturma como formatar números de telefone. É realmente responsabilidade da Stringclasse saber sobre números de telefone? Acho que não. Usados ​​dessa maneira, os métodos de extensão são um açúcar sintático para deixar algo que claramente não é orientado a objetos.
user281377
1
Ok, esqueça que eu disse o método de extensão. Finja que disse o método utilitário ou a classe do formatador. Estou simplesmente dizendo que a refatoração / colagem de cópias não deve ser necessária, independentemente de eu ter um getter de modelo ou fazer a formatação em outro lugar.
devuxer
1
E eu gosto da ideia de uma PhoneNumberaula. Estou planejando fazer isso de qualquer maneira, porque também tenho uma PhoneTypepropriedade.
devuxer
Não gosto da ideia de ter PhoneNumbercomo uma classe de instância porque os dados são nativos string. Em vez disso, deve ser uma classe estática com métodos como public static string Format(string phoneNumber, PhoneNumberStyle style).
Sr. Anderson
5

O que você precisa considerar ao escrever um código: está correto? É legível? É eficiente? É sustentável? Eu argumentaria, como @btilly mencionou, que não é sustentável devido à formatação específica da cultura, mas a questão parece ser mais geral do que isso.

Usar acessadores como esses torna seu código mais legível e, dependendo de como você o usa, pode tornar outras partes do código muito mais limpas. Na minha opinião, isso não cheira nada. Começaria a cheirar se você tivesse acessadores de formatação para qualquer tipo de string que você queira imprimir ( public string FirstLastName; public string FullName; public string FullNameWithMiddleInitial; public string PhoneNumberWithAreaCode; public string PhoneNumberWithoutAreaCode; public string PhoneNumberWithCountryCode;, etc)

Ou, em outras palavras, o uso de um padrão não faz com que seu código tenha "cheiro de padrão" automaticamente. Você precisa abusar dele se quiser ganhar esse atributo.

Greg Jackson
fonte
Obrigado, Greg. +1. Eu concordo com você sobre colocar todas as combinações. Na verdade, estou apenas tentando encontrar a maneira mais limpa de padronizar como os dados são visualizados.
devuxer
3

Quebra o princípio da responsabilidade única. Por que não fazer uma classe de número de telefone, etc ...?

Edward Strange
fonte
Ok, eu realmente concordo com isso (veja a discussão na resposta da muniçãoQ), mas devo também fazer uma FullNameaula?
Devuxer
Sim, você deve, mas deve corrigir a ortografia de "FullName" para "FoolName". Ou talvez "PersonalName", pois é o nome de uma pessoa, e não um nome completo.
kevin Cline
1
Verdade? A menos que se espere que a classe dada cresça, o que exatamente há de errado com ela? Mesmo que cresça, quão difícil seria re-fatorar então?
Job
@ DanM: Então é isso que você pede, ou seja, peça para que digitem seu nome completo. Se você está classificando pelo primeiro nome, está realmente classificando na primeira letra (depois na segunda e assim por diante) do primeiro nome (depois na próxima e assim por diante), para que os nomes sejam classificados da mesma forma.
Matt Ellen
@DanM vamos continuar esta discussão no chat
Matt Ellen
1

Para o seu exemplo, não considero muito difícil usar formatos específicos. É uma ou duas e todas as partes do aplicativo usam o mesmo formato.

O ponto em que essa decisão começaria a ser interrompida é quando você tem os mesmos dados em vários locais diferentes, exigindo formatos diferentes .

Se isso acontecesse, eu ficaria tentado a puxar a Memberturma de volta para:

public class Member
{
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string PhoneNumber { get; set; }  
}

E, em seguida, faça adaptadores diferentes para cada destino. Por exemplo, suponha que as informações sejam necessárias no formato CSV:

public static class CSVMemberAdapter
{
    public static string ToCSV(this Member mbr)
    {
         return mbr.Id + "," + mbr.LastName + "," + mbr.FirstName, "," mbr.PhoneNumber;
    }
}

Sempre assumindo que você limpou os dados para que não haja vírgulas etc. nas strings.

O adaptador não precisa ser um método de extensão, mas, para esse caso de simulação, parece se encaixar.

Peter K.
fonte
Já faz um tempo desde que eu escrevi C #, mas certamente existem classes de serialização que podem lidar com isso, em vez de escrever tediosamente métodos como toCSV repetidamente, repetidamente, repetidamente, repetidamente, repetidamente, repetidamente, repetidamente, repetidamente, repetidamente. e mais e mais e mais e mais e mais ...
kevin Cline
@ Kevin Cline: Depende do que você precisa em cada pia. Se tudo o que eles precisam é XML serializado, tudo bem. Muitos sistemas não. Se isso tiver que ser feito overtantas vezes quanto isso, então há algo errado com o design.
Peter K.
normalmente haveria muitas classes para serializar. Deve ser possível escrever um único CSV ou outro serializador que possa lidar com a maioria das classes via reflexão, em vez de codificá-las manualmente, como no seu exemplo.
kevin Cline
@ Kevin Cline: Concordo violentamente! Eu estava escrevendo algo muito específico para a pergunta. Exemplos simples e concretos tendem a explicar melhor as coisas.
Peter K.