Compartir a través de



Marzo de 2019

Volumen 34, número 3

[El programador ocupado]

Programación con Naked: Propiedades de Naked

Por Ted Neward| Marzo de 2019

Ted NewardBienvenidos de nuevo, usuarios de NOF. (He decidido que suena mejor que llamar a los usuarios de objetos Naked "programadores Naked"). En mi último artículo, empecé a crear el modelo de dominio para mi sistema de conferencias, que me permitió almacenar oradores, pero, de momento, tiene una configuración bastante sencilla. No he hecho nada de lo que habría que hacer normalmente, como comprobar que los nombres o apellidos no están vacíos o admitir un campo "topics" que forme parte de un conjunto de opciones enlazado, etc. Son todo cosas razonables que es recomendable admitir en una UI (así como un modelo de datos) y, además, si queremos usar este enfoque de Naked en escenarios reales, también será necesario que las admita.

Por suerte, los diseñadores de NOF ya sabían todo eso.

Conceptos de Naked

Volvamos atrás y hablemos sobre cómo NOF controla esto en términos generales antes de adentrarnos en casos concretos.

Recuerde que el objetivo de NOF es evitar tener que escribir código de UI que se podría señalar con algunos aspectos de los propios objetos de dominio. La mejor forma de hacer una señalización de ese tipo es usar atributos personalizados. Básicamente, se usan atributos personalizados NOF para anotar varios elementos del objeto de dominio (propiedades y métodos, principalmente) y el cliente NOF entiende, de acuerdo con la presencia del atributo o los datos que contiene el atributo, que debe personalizar la UI para ese objeto de alguna manera. Tenga en cuenta que, con NakedObjects, no necesita definir muchos de estos atributos personalizados, ya que vienen "gratis" con el espacio de nombres System.ComponentModel de la distribución estándar de .NET: esto es reusabilidad.

Sin embargo, a veces no es tan simple como "siempre debería ser así". Por ejemplo, si algunas propiedades se deben deshabilitar según el estado interno del objeto (por ejemplo, una propiedad de baja parental que se debe deshabilitar si el empleado no tiene pareja o hijos), será necesario ejecutar código, y eso es algo que un atributo personalizado no puede hacer. En esas situaciones, NOF depende de la convención, por lo que, en concreto, buscará métodos con un nombre específico en la clase. Si la propiedad de baja parental se denomina OnLeave, se ejecutará el método NOF para determinar si la opción para deshabilitar la propiedad OnLeave debe llamarse DisableOnLeave.

Veamos cómo funciona esto en la práctica.

Oradores en Naked (segunda parte)

Actualmente, la clase Speaker tiene solo tres propiedades: FirstName, LastName y Age. No se cuenta la propiedad Id, que no es visible para el usuario, ni la propiedad FullName, que se calcula independientemente de las propiedades FirstName y LastName, ya que el usuario no las puede modificar y no son relevantes en este caso o por lo menos aún. No tendría sentido que este sistema de conferencias permitiera nombres o apellidos en blanco, ni una edad negativa. Primero, corrijamos esto.

La especificación de nombres distintos de cero es una de las validaciones más fáciles de aplicar, ya que es estática. No se requiere ninguna lógica compleja: la longitud de las cadenas asignadas a cada propiedad debe ser mayor que cero. Esto se controla mediante el atributo StringLength en todas las propiedades, como se muestra a continuación:

[StringLength(100,
  ErrorMessage = "First name must be between 1 and 100 characters",
  MinimumLength = 1)]
public virtual string FirstName { get; set; }
[StringLength(100,
  ErrorMessage = "Last name must be between 1 and 100 characters",
  MinimumLength = 1)]
public virtual string LastName { get; set; }

De este modo, se resuelve el problema de los nombres vacíos.

La edad es incluso más fácil, ya que puedo usar el atributo personalizado Range para especificar los intervalos de edad mínima y máxima aceptables. ¿Realmente consideraría la posibilidad de presentar a un orador menor de 21 años? Posiblemente, ya que quiero animar a los jóvenes en edad escolar a hablar, pero sería difícil aceptar a alguien menor de 13 años, probablemente. Así pues, el atributo Range tendría este aspecto:

[Range(13, 90, ErrorMessage = "Age must be between 13 and 90")]
public virtual int Age { get; set; }

Tenga en cuenta que los atributos StringLength y Range también pueden tomar el valor ErrorMessageResourceName, en caso de que se almacenen mensajes de error en los recursos (algo que se debería hacer para facilitar la internacionalización).

Compile, ejecute y observe cómo, ahora, la UI aplicará estas restricciones automáticamente. Y, lo que es aún mejor, en la medida de lo posible, las restricciones también se aplicarán a la base de datos. ¡Genial!

De por sí, estos atributos actúan, básicamente, como validaciones de modelos de datos, con una pequeña UI para admitirlos. Sin embargo, a menudo es necesario cambiar elementos de la UI que no tienen nada que ver con la validación de datos. Por ejemplo, actualmente, los atributos del objeto Speaker se muestran en orden alfabético, lo que no tiene mucho sentido. Sería mucho más realista (y útil) si el primer valor mostrado fuera el nombre completo, seguido de los campos individuales de nombre, apellido y edad (así como cualquier otra información demográfica que necesite capturar y usar).

Aunque, sin duda, esto podría convertirse en un momento "Bueno, ha sido divertido, pero ha llegado el momento de desglosar y compilar nuestra propia UI", no tiene por qué ser así. Se trata de un requisito común, y NOF lo contempla mediante el atributo MemberOrder. Usando este atributo, puedo establecer un "orden" en el que deben aparecer los atributos en la UI. Así que, por ejemplo, si quiero que el atributo FullName aparezca primero en la UI, puedo usar MemberOrder y pasar la posición ordinal relativa "1", como se muestra a continuación:

[Title]
[MemberOrder(1)]
public string FullName { get { return FirstName + " " + LastName; } }

A continuación, me gustaría mostrar el nombre, apellido y edad, pero aquí puedo empezar a meterme en problemas. A medida que voy agregando campos nuevos a esta clase con el tiempo (por ejemplo, "middle name" o "email"), intentar mantener todas las posiciones ordinales en orden puede ser complicado: si muevo LastName a la posición 5, tengo que buscar todo lo que aparece en la posición 5 (y posteriores) y asignar las posiciones correctas. Es una lata.

Por suerte, MemberOrder tiene un truco excelente para hacerlo: La propia posición puede ser un valor de punto flotante, lo que permite agrupar campos, de modo que puedo marcar "FirstName", "LastName" y "Age", como las posiciones ordinales "2.1", "2,2" y "2.3", respectivamente, lo que significa, básicamente, que el grupo "2" puede ser información demográfica y que cualquier nueva información demográfica sobre el orador solo requerirá la reorganización de los miembros de ese grupo en particular, como se muestra en la figura 1.

Figura 1 Agrupación de campos

[MemberOrder(2.1)]
[StringLength(100,
  ErrorMessage = "First name must be between 1 and 100 characters",
  MinimumLength = 1)]
public virtual string FirstName { get; set; }
[MemberOrder(2.2)]
[StringLength(100,
  ErrorMessage = "Last name must be between 1 and 100 characters",
  MinimumLength = 1)]
public virtual string LastName { get; set; }
[Range(13, 90, ErrorMessage = "Age must be between 13 and 90")]
[MemberOrder(2.3)]
public virtual int Age { get; set; }

Tenga en cuenta que no hay nada realmente especial acerca de los propios valores, que se usan en relación entre sí y no representan ninguna ubicación concreta en la pantalla. De hecho, podría haber usado 10, 21, 22 y 23, si quisiera. NOF tiene la precaución de señalar que estos valores se comparan de manera lexicográfica (comparaciones basadas en cadena) y no numérica: use el esquema que tenga sentido para usted.

¿Qué ocurre si los usuarios no saben si el campo Age debe tener un valor en años o en días? Puede parecerle totalmente obvio, pero recuerde que no todas las personas ven el mundo de la misma manera. Aunque, probablemente, no es una información que deba estar presente en la UI explícitamente, debe ser algo que pueda indicar al usuario de algún modo. En NOF, el atributo "DescribedAs" se usa para indicar cómo se debe describir la propiedad, lo que, normalmente, adopta la forma de información sobre herramientas en el área de entrada. Sin embargo, debe recordar que es posible que un cliente NOF determinado use otra forma para indicarlo. Por ejemplo, si surge un cliente NOF para teléfonos, que son táctiles, la información sobre herramientas no funciona bien para ese formato. En esa situación, el cliente NOF hipotético podría usar un mecanismo diferente más adecuado para esa plataforma a fin de describir la propiedad.

Los oradores también necesitan una biografía. Ay, cómo podría olvidarme, es el único momento en que los oradores pueden escribir exclusivamente sobre sí mismos y las increíbles cosas que hacen. Es broma; si hay algo que los oradores odian es escribir su propia biografía. La biografía es un atributo fácil de agregar a la clase, pero la mayoría de biografías necesitan más de una palabra o dos y, si observamos la UI que ha generado NOF hasta ahora, el resto de cadenas son cuestión de una sola línea. Por este motivo, NOF proporciona el atributo "MultiLine": para indicar que este campo debe mostrarse en un área de entrada de texto más grande que la cadena típica.

Pero, en este caso, debo ser precavido con la biografía de un orador, ya que la entrada de formato libre ofrece la posibilidad de abuso: podría querer o necesitar filtrar ciertas palabras para que la gente no se lleve una impresión incorrecta acerca de la conferencia. Simplemente, no puedo incluir oradores en mi evento con biografías que incluyan palabras como COBOL. Por suerte, NOF permite validar las entradas buscando e invocando métodos para la clase Speaker que coincidan con una convención Validate[Propiedad], como se muestra a continuación:

[MemberOrder(4)]
[StringLength(400, ErrorMessage = "Keep bios to under 400 characters, please")]
public virtual string Bio { get; set; }
public string ValidateBio(string bio)
{
  if (bio.IndexOf("COBOL") > -1)
    return "We are terribly sorry; nobody wants to hear that";
  else
    return "";
}

Resumen

NOF tiene una amplia variedad de opciones disponibles para describir un objeto de dominio en términos que faciliten la representación automática de la UI adecuada a fin de aplicar límites de dominio, pero, de momento, este modelo es muy sencillo. En el siguiente artículo, examinaré cómo aplicar NOF a un tema más complejo: las relaciones entre objetos. Los oradores deben poder especificar los temas sobre los que van a hablar y, sobre todo, las charlas que proponen. Pero ya no me queda espacio este mes, así que, mientras tanto... ¡Disfrute programando!


Ted Neward es un consultor de politecnología, orador y mentor residente en Seattle. Ha escrito una gran cantidad de artículos, es autor y coautor de una docena de libros y realiza conferencias por todo el mundo. Puede ponerse en contacto con él en ted@tedneward.com o leer su blog en blogs.tedneward.com.

Gracias al siguiente experto técnico por revisar este artículo: Richard Pawson
La carrera de Richard Pawson en informática comenzó en 1977 cuando empezó a trabajar para una empresa que fabricaba calculadoras de bolsillo. Tres semanas después de su incorporación, la empresa anunció el primer equipo personal del mundo: Commodore PET. En los años transcurridos, Richard ha sido periodista tecnológico, ingeniero de robótica, diseñador de juguetes electrónicos, consultor de administración y desarrollador de software.  Los objetos Naked nacieron en su tesis en 2003 y, desde entonces, Richard ha administrado el desarrollo del marco de código abierto de objetos Naked. En su tiempo libre está restaurando un cupé Daimler descapotable de 1939 que, originalmente, perteneció al rey Jorge VI de Inglaterra.