¿Es un "olor a patrón" poner captadores como "FullName" o "FormattedPhoneNumber" en su modelo?

13

Estoy trabajando en una aplicación ASP.NET MVC, y me he acostumbrado a poner lo que parecen útiles y convenientes captadores en mis clases de modelo / entidad.

Por ejemplo:

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); }
    }
}

Me pregunto si la gente piensa en los FullNamey los FormattedPhoneNumbercaptadores.

Facilitan la creación de formatos de datos estandarizados en toda la aplicación, y parecen guardar una gran cantidad de código repetido, pero definitivamente se podría argumentar que el formato de datos es algo que debe manejarse en el mapeo de modelo a modelo de vista.

De hecho, originalmente estaba aplicando estos formatos de datos en mi capa de servicio donde hago mi mapeo, pero se estaba volviendo una carga tener que escribir constantemente formateadores y luego aplicarlos en muchos lugares diferentes. Por ejemplo, utilizo "Nombre completo" en la mayoría de las vistas, y tener que escribir algo parecido en model.FullName = MappingUtilities.GetFullName(entity.FirstName, entity.LastName);todo el lugar parece mucho menos elegante que simplemente escribir model.FullName = entity.FullName(o, si usa algo como AutoMapper, potencialmente no escribir nada).

Entonces, ¿dónde trazas la línea cuando se trata de formatear datos? ¿Está "bien" formatear los datos en su modelo o es un "olor a patrón"?

Nota: Definitivamente no tengo ningún html en mi modelo. Yo uso html helpers para eso. Estoy hablando estrictamente de formatear o combinar datos (y especialmente datos que se usan con frecuencia).

devuxer
fuente
1
Hacer esto puede ser conveniente. Pero espere y ore para que nunca tenga que internacionalizar ese código.
btilly
@btilly, buen punto, pero estoy aproximadamente 99.99% seguro de que no lo haré.
devuxer
Específico para FullName y PhoneNumber, definitivamente. ¿La pregunta es específicamente sobre aquellos porque tienen formatos inconsistentes en diferentes culturas, o @DanM simplemente eligió ejemplos que no se internacionalizan bien para una pregunta más general?
Greg Jackson
@ Greg Jackson, definitivamente la escalera. Como ammoQ señaló, PhoneNumberprobablemente pertenece a su propia clase (que ahora he implementado). Pero FullNamefue realmente el que me motivó a escribir la pregunta. Pero estoy interesado en averiguar si, en general, tiene sentido incluir el formato / peinado de datos, etc. en el modelo para las cosas que se aplicarán en toda la aplicación. De las respuestas a continuación, parece que esto no es un antipatrón, pero la decisión debe tomarse con cuidado.
devuxer
Tenga en cuenta que este formato específico para el nombre completo también puede no internacionalizarse bien. En Asia oriental, por ejemplo, el orden de los nombres es inverso al del mundo occidental. ¿Realmente necesitas manejar esto explícitamente? Tal vez no, pero solo tenga en cuenta que hay muchas cosas difíciles que encontrará al formatear los datos.
Greg Jackson

Respuestas:

9

En su ejemplo, me gusta el FullNamegetter (por todas las razones que ha dado) pero no me gusta el getter FormattedPhoneNumber. La razón es: probablemente no es tan fácil (una vez que tenga los números internacionales de teléfono, etc.) y si se coloca la lógica para formatear números de teléfono en un método de Member, lo más probable es que tendrá que refactor (o copiar y pegar caugh ) una vez que se también necesita un número de teléfono con formato Institution, Vendoretc.

EDITAR: OMI sería mejor tener una PhoneNumberclase con un Formattedgetter.

usuario281377
fuente
+1 y gracias. Pero, ¿qué sucede si realmente pongo mi código de formato de número de teléfono en un método de extensión? Entonces cualquier modelo podría usarlo, y no habría refactorización o copia / pegado. Esta solución aún sería mucho menos repetitiva que aplicar el formateador a cada número de teléfono que aparece en cada vista.
devuxer
3
El uso de un método de extensión para eso sería una forma rápida de antipatrón de "descomposición funcional". Al usar un método de extensión (para la Stringclase, supongo), usted "enseña" a la Stringclase cómo formatear números de teléfono. ¿Es realmente responsabilidad de la Stringclase saber acerca de los números de teléfono? No lo creo. Usados ​​así, los métodos de extensión son azúcar sintáctica para dejar que algo que claramente no está orientado a objetos se vea como lo fue.
user281377
1
Bien, olvide que dije el método de extensión. Imagina que dije método de utilidad o clase de formateador. Simplemente digo que no debería ser necesario refactorizar / pegar copias, independientemente de si tengo un captador de modelos o si formateo en otro lugar.
devuxer
1
Y me gusta la idea de una PhoneNumberclase. He estado planeando hacer eso de todos modos porque también tengo una PhoneTypepropiedad.
devuxer
No me gusta la idea de tener PhoneNumberuna clase de instancia porque los datos son nativos string. Más bien, debería ser una clase estática con métodos como public static string Format(string phoneNumber, PhoneNumberStyle style).
Sr. Anderson el
5

Las cosas que debe tener en cuenta al escribir código: ¿es correcto? ¿Es legible? ¿Es eficiente? ¿Es mantenible? Yo diría, como @btilly mencionó, que no se puede mantener debido al formato específico de la cultura, pero la pregunta parece ser más general que eso.

El uso de accesores como estos hace que su código sea más legible y, dependiendo de cómo lo use, puede hacer que otras partes de su código sean mucho más limpias. En mi opinión, eso no huele en absoluto. Comenzaría a oler si tuviera formateadores para cualquier tipo de cadena que desee imprimir ( public string FirstLastName; public string FullName; public string FullNameWithMiddleInitial; public string PhoneNumberWithAreaCode; public string PhoneNumberWithoutAreaCode; public string PhoneNumberWithCountryCode;, etc.)

O, para decirlo de otra manera, el uso de un patrón no hace que su código tenga automáticamente "olor a patrón". Necesitas abusar de él si quieres ganar ese atributo.

Greg Jackson
fuente
Gracias Greg. +1. Estoy de acuerdo contigo en poner cada combinación. Realmente estoy tratando de encontrar la forma más limpia de estandarizar cómo se ven los datos.
devuxer
3

Rompe el principio de responsabilidad única. ¿Por qué no hacer una clase de número de teléfono, etc.?

Edward extraño
fuente
De acuerdo, en realidad estoy de acuerdo con eso (ver discusión bajo la respuesta de ammoQ), pero ¿también debería hacer una FullNameclase?
devuxer
Sí, debería, pero debe corregir la ortografía de "FullName" a "FoolName". O tal vez "PersonalName", ya que es el nombre de una persona, y no un nombre completo.
Kevin Cline
1
De Verdad? A menos que se espere que la clase dada crezca, ¿qué tiene de malo exactamente? Incluso si crece, ¿qué tan difícil sería re-factorizar entonces?
Trabajo
@DanM: Entonces eso es lo que pides, es decir, pídeles que escriban su nombre legal completo. Si está ordenando por nombre, realmente está ordenando por la primera letra (luego la segunda, etc.) del primer nombre (luego el siguiente, y así sucesivamente), por lo que los nombres se ordenarán de la misma manera.
Matt Ellen
1

Para su ejemplo, no lo veo como un gran problema para usar formatos específicos. Es una o dos y todas las partes de la aplicación usan el mismo formato.

Donde esa decisión comenzaría a romperse es cuando tienes los mismos datos yendo a varios lugares diferentes que requieren diferentes formatos .

Si eso sucediera, me vería tentado a Memberdevolver la clase a:

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

Y luego haga diferentes adaptadores para cada objetivo. Por ejemplo, suponga que la información se requiere en formato CSV:

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

Siempre suponiendo que haya desinfectado los datos para que no haya comas, etc., en las cadenas.

El adaptador no tiene que ser un método de extensión, pero para este caso imaginario parece encajar.

Peter K.
fuente
Ha pasado un tiempo desde que escribí C #, pero seguramente hay clases de serialización que pueden manejar esto, en lugar de escribir tediosamente métodos como toCSV una y otra y otra y otra y otra y otra y otra y otra y otra y otra y otra y otra vez y una y otra y otra y otra y otra vez ...
Kevin Cline
@kevin cline: depende de lo que necesites en cada fregadero. Si todo lo que necesitan es XML serializado, está bien. Muchos sistemas no lo hacen. Si tiene que hacerse overtantas veces como eso, entonces hay algo mal con el diseño.
Peter K.
normalmente habría muchas clases para serializar. Debería ser posible escribir un único CSV u otro serializador que pueda manejar la mayoría de las clases a través de la reflexión, en lugar de codificarlas a mano como su ejemplo.
Kevin Cline
@kevin cline: violentamente de acuerdo! Estaba escribiendo algo muy específico para la pregunta formulada. Los ejemplos concretos y simples tienden a explicar mejor las cosas.
Peter K.