Pregunta

estado viendo algunos videos Greg Young últimamente y estoy tratando de entender por qué hay una actitud negativa hacia Setter en objetos de dominio. Pensé objetos de dominio se supone que es "pesado" con la lógica de DDD. ¿Hay buenos ejemplos en línea del mal ejemplo y entonces manera de corregirlo? Ejemplos o explicaciones son buenas. ¿Esto sólo se aplica a los eventos almacenados de manera CQRS o se aplica esto a todos DDD?

¿Fue útil?

Solución

estoy contribuyendo esta respuesta para complementar Roger Alsing respuesta sobre invariantes con otras razones.

La información semántica

Roger explicó claramente que los emisores de propiedad no llevan información semántica. Permitir que un colocador de una propiedad como Post.PublishDate puede añadir confusión ya que no podemos saber con seguridad si el mensaje ha sido publicado o si la fecha de publicación ha sido ajustada. No podemos saber con certeza que esto es todo lo que se necesita para publicar un artículo. El objeto "interfaz" no muestra su "intención" claridad.

Creo, sin embargo, que las propiedades como "Habilitado" hacer transportando el suficiente semántica para ambos "ajuste" "conseguir" y. Es algo que debería ser efectiva inmediatamente y no puedo ver la necesidad de setActive () o Activar () / Desactivar () métodos para la sola razón de la falta de semántica en la incubadora propiedad.

invariantes

Roger también se refirió a la posibilidad de romper invariantes a través de los emisores de propiedad. Esta es toda la razón, y uno debe tomar propiedades que trabajan en conjunto para proporcionar un "valor invariante combinado" (como los ángulos de triángulo para usar el ejemplo de Roger) como propiedades de sólo lectura y crear un método para ponerlos todos juntos, lo que puede validar todas las combinaciones en un solo paso.

inicialización fin Propiedad / establecimiento de dependencias

Esto es similar al problema con invariantes ya que causa problemas con las propiedades que deben ser validados / cambiaron juntos. Imagine el siguiente código:

    public class Period
    {
        DateTime start;
        public DateTime Start
        {
            get { return start; }
            set
            {
                if (value > end  && end != default(DateTime))
                    throw new Exception("Start can't be later than end!");
                start = value;
            }
        }

        DateTime end;
        public DateTime End
        {
            get { return end; }
            set
            {
                if (value < start && start != default(DateTime))
                    throw new Exception("End can't be earlier than start!");
                end = value;
            }
        }
    }

Este es un ejemplo de validación ingenua "colocador" que causa dependencias de orden de acceso. El código siguiente ilustra este problema:

        public void CanChangeStartAndEndInAnyOrder()
        {
            Period period = new Period(DateTime.Now, DateTime.Now);
            period.Start = DateTime.Now.AddDays(1); //--> will throw exception here
            period.End = DateTime.Now.AddDays(2);
            // the following may throw an exception depending on the order the C# compiler 
            // assigns the properties. 
            period = new Period()
            {
                Start = DateTime.Now.AddDays(1),
                End = DateTime.Now.AddDays(2),
            };
            // The order is not guaranteed by C#, so either way may throw an exception
            period = new Period()
            {
                End = DateTime.Now.AddDays(2),
                Start = DateTime.Now.AddDays(1),
            };
        }

Dado que no podemos cambiar la fecha de inicio más allá de la fecha de finalización en un objeto periodo (a no ser que se trata de un "vacío" período, con las dos fechas definido como predeterminado (DateTime) - sí, no es un gran diseño, pero lo que quiero decir ...) tratando de establecer la fecha de inicio primero será una excepción.

Se vuelve más grave cuando usamos inicializadores de objeto. Puesto que C # no garantiza ninguna orden de asignación, no podemos hacer ninguna suposición segura y el código puede o no puede lanzar una excepción en función de las opciones del compilador. MALO!

Esto es en última instancia un problema con el diseño de las clases. Dado que la propiedad no puede "saber" que está actualizando ambos valores, no se puede "apagar" la validación hasta que ambos valores son en realidad cambió. Usted debe o bien hacer ambas propiedades de sólo lectura y proporcionar un método para fijar las dos al mismo tiempo (pérdida de la función de inicializadores de objeto) o eliminar el código de validación de las propiedades del todo (tal vez la introducción de otra propiedad de sólo lectura como IsValid, o validar siempre que sea necesario).

ORM "hidratación" *

Hidratación, en una vista simplista, significa obtener los datos persistido de nuevo en objetos. Para mí esto es realmente el mayor problema con la adición lógica detrás de los emisores de propiedad.

Muchos / ORM mayoría de asignar el valor persistido hasta una propiedad. Si usted tiene la lógica de validación o la lógica que cambia el estado del objeto (los demás miembros) en el interior de los emisores de propiedad que va a terminar tratando de validar contra un objeto "incompleta" (todavía se está cargando). Esto es muy similar al problema objeto de inicialización ya que no puede controlar el orden de los campos son "hidratado".

La mayoría de ORM le permiten asignar la persistencia de campos privados en lugar de propiedades, y esto va a permitir que los objetos a ser hidratados, pero si sus mentiras lógica de validación en su mayoría los emisores de propiedad en el interior puede que tenga que duplicarlo en otro lugar para comprobar que la carga objeto es válido o no.

Dado que muchas herramientas ORM soportan la carga diferida (un aspecto fundamental de un ORM!) A través del uso de las propiedades virtuales (o métodos) mapeo de campos hará que sea imposible que el ORM a los objetos de carga perezosos mapeados en los campos.

Conclusión

Así que, al final, a la duplicación de código EVITAR, permiten ORM para llevar a cabo lo mejor que pudo, impiden excepciones sorprendentes dependiendo del orden se establecen los campos, es aconsejable lógica alejamiento de los emisores de propiedad.

Yo todavía estoy aprendiendo en esta lógica 'validación' debe ser. ¿Por dónde validar los invariantes aspectos de un objeto? ¿Dónde ponemos las validaciones de nivel superior? ¿Utilizamos ganchos en los ORM para realizar la validación (OnSave, OnDelete, ...)? Etc., etc., etc. Pero esto no es el alcance de esta respuesta.

Otros consejos

Setter duerma llevan ninguna información semántica.

por ejemplo.

blogpost.PublishDate = DateTime.Now;

¿Eso quiere decir que la respuesta ha sido publicada? O simplemente que la fecha de publicación se han establecido?

Considere lo siguiente:

blogpost.Publish();

Esto muestra claramente la intención de que la entrada de blog debe ser publicado.

Además, los emisores pueden romper los invariantes de objeto. Por ejemplo si tenemos una entidad "Triángulo", el invariante debe ser que la suma de todos los ángulos debe ser de 180 grados.

Assert.AreEqual (t.A + t.B + t.C ,180);

Ahora bien, si tenemos los emisores, podemos romper fácilmente los invariantes:

t.A = 0;
t.B = 0;
t.C = 0;

Así que tenemos un triángulo donde la suma de todos los ángulos son 0 ... ¿Es realmente un triángulo? Yo diría que no.

Sustitución de los emisores con métodos nos podría obligar a mantener invariantes:

t.SetAngles(0,0,0); //should fail 

Esta llamada debe lanzar una excepción que le dice que esto provoca un estado no válido para su entidad.

Para que pueda obtener la semántica e invariantes con métodos en lugar de los emisores.

La razón detrás de esto es que la propia entidad debe ser responsable de cambiar su estado. En realidad no hay ninguna razón para que deba configurar una propiedad en cualquier lugar fuera de la propia entidad.

Un ejemplo sencillo sería una entidad que tiene un nombre. Si usted tiene un regulador público que sería capaz de cambiar el nombre de una entidad que desde cualquier punto de la aplicación. Si en lugar de quitar y poner colocador que un método como ChangeName(string name) a su entidad que será la única manera de cambiar el nombre. De esa manera usted puede agregar cualquier tipo de lógica que se ejecutará siempre al cambiar el nombre porque sólo hay una manera de cambiarlo. Esto también es mucho más claro a continuación, sólo estableciendo el nombre a algo.

Básicamente, esto significa que exponga el comportamiento de sus entidades públicas mientras se mantenga el estado de forma privada.

La pregunta original es etiquetada .NET, por lo que voy a presentar un enfoque pragmático para el contexto en el que desea unir sus entidades directamente a una vista.

Sé que eso es una mala práctica, y que probablemente debería tener un modelo de vista (como en MVVM) o similares, pero para algunas pequeñas aplicaciones que sólo tiene sentido para mi humilde opinión no sobre-patternize.

El uso de propiedades es el camino fuera de las obras de enlace de datos de la caja .net. Tal vez los estipula que los datos de contexto de enlace debe funcionar en ambos sentidos, y por lo tanto de aplicación INotifyPropertyChanged y elevar PropertyChanged como parte de la lógica colocador tiene sentido.

La entidad, por ejemplo, podría añadir un elemento a una colección de reglas roto o similar (sé CSLA tenía ese concepto hace unos años y tal vez todavía lo hace) cuando el cliente establece un valor no válido, y que la colección podría ser mostrado en la interfaz de usuario. La unidad de trabajo más tarde negarse a persistir objetos no válidos, si debe venir tan lejos.

Estoy tratando de justificar el desacoplamiento, la inmutabilidad, y así sucesivamente, en gran medida. Sólo digo que en algunos contextos una arquitectura más simple se pide.

A setter simplemente establece un valor. No se supone que es "heavy" with logic.

Los métodos de los objetos que tienen buenos nombres descriptivos deben ser los que "heavy" with logic y tienen un análogo en el dominio de sí mismo.

lo recomiendo encarecidamente la lectura de la libro DDD por Eric Evans y orientada a objetos de software de construcción de Bertrand Meyer . Todos ellos tienen las muestras de lo que usted necesita.

I puede estar fuera de aquí, pero creo que los huéspedes deberían usarse para establecer, en contraposición a los métodos setter. Tengo un par de razones para esto.

a) Tiene sentido en .Net. Cada desarrollador sabe acerca de las propiedades. Esa es la forma de configurar las cosas en un objeto. ¿Por qué se desvía de la de objetos de dominio?

b) Setters pueden tener código. Antes de 3.5 creo, el establecimiento de un objeto consistió en una variable interna y una firma propiedad

private object _someProperty = null;
public object SomeProperty{
    get { return _someProperty; }
    set { _someProperty = value; }
}

Es muy fácil y elegante de la OMI para poner la validación en la incubadora. En IL, captadores y definidores se convierten en métodos de todos modos. ¿Por qué código duplicado?

En el ejemplo del método Publicar () registró anteriormente, estoy completamente de acuerdo. Hay momentos en que no queremos que otros desarrolladores establecer una propiedad. Que debe ser manejado por un método. Sin embargo, ¿tiene sentido tener un método setter para cada propiedad cuando .Net proporciona toda la funcionalidad que necesitamos en la declaración de propiedad ya?

Si usted tiene un objeto persona, ¿por qué crear métodos para cada propiedad en él si no hay ninguna razón?

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top