Question

regardé quelques vidéos Greg Young ces derniers temps et je suis en train de comprendre pourquoi il y a une attitude négative envers Setters sur les objets du domaine. Je pensais que les objets de domaine étaient censés être « lourd » avec la logique en DDD. Y a-t-il des bons exemples en ligne du mauvais exemple et leur façon de corriger? Tous les exemples ou explications sont bonnes. Est-ce uniquement aux événements appliquer stockés de manière CQRS ou que cela s'applique à tous DDD?

Était-ce utile?

La solution

Je contribue cette réponse pour compléter réponse Roger Alsing à propos d'autres raisons avec invariants.

L'information sémantique

Roger a expliqué clairement que les organismes de propriété ne portent l'information sémantique. Permettre à un setter pour une propriété comme Post.PublishDate peut ajouter de la confusion que nous ne pouvons pas savoir avec certitude si le message a été publié ou que si la date de publication a été fixée. Nous ne pouvons pas savoir avec certitude que cela est tout ce qui est nécessaire pour publier un article. L'objet « interface » ne montre pas son « intention » clairement.

Je crois, cependant, que les propriétés comme « Activé » ne portent assez sémantique pour les « obtenir » et « réglage ». Il est quelque chose qui devrait être efficace immédiatement et je ne vois pas la nécessité d'SetActive () ou Activer () / désactiver () les méthodes pour la seule raison de manquer sémantique sur le setter de propriété.

Invariants

Roger a également parlé de la possibilité de briser par setters invariants propriété. Ceci est tout à fait raison, et on doit faire des propriétés qui travaillent en tandem pour fournir une « valeur invariant combiné » (comme les angles du triangle d'utiliser l'exemple de Roger) en tant que propriétés en lecture seule et de créer une méthode pour les mettre tous ensemble, ce qui peut valider toutes les combinaisons en une seule étape.

initialisation de la commande Propriété / réglage dépendances

Ceci est similaire au problème car il provoque invariants des problèmes avec des propriétés qui doivent être validées / modifiées ensemble. Imaginez le code suivant:

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

Ceci est un exemple naïf de validation « setter » qui provoque des dépendances de commande d'accès. Le code suivant illustre ce problème:

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

Étant donné que nous ne pouvons pas changer la date de début après la date de fin sur un objet de période (à moins qu'il soit un « vide » période, avec deux dates définie par défaut (DateTime) - oui, ce n'est pas un grand dessein, mais vous obtenir ce que je veux dire ...) en essayant de définir la date de début premier jetteront une exception.

Il devient plus grave lorsque nous utilisons initialiseurs d'objets. Étant donné que C # ne garantissent pas un ordre d'affectation, nous ne pouvons pas faire des hypothèses de sécurité et le code peut ou ne peut pas lancer une exception en fonction des choix du compilateur. BAD!

est en fin de compte un problème avec la conception des classes. Étant donné que la propriété ne peut pas « savoir » que vous mettez à jour les deux valeurs, il ne peut pas la validation « désactiver » jusqu'à ce que les deux valeurs sont réellement changé. Vous devez soit faire les deux propriétés en lecture seule et de fournir une méthode pour définir les deux en même temps (perdre la fonction des initialisations d'objet) ou supprimer le code de validation des propriétés tout à fait (introduction peut-être une autre propriété en lecture seule comme IsValid, ou la validation chaque fois que nécessaire).

ORM "hydratation" *

Hydration, dans une vue simpliste, un moyen d'obtenir les données persisté en objets. Pour moi, ce qui est vraiment le plus gros problème avec l'ajout d'une logique derrière setters de propriété.

De nombreux / la plupart des ORM carte la valeur dans une propriété persistante. Si vous avez une logique de validation ou de la logique qui change l'état d'objet (autres membres) à l'intérieur setters de propriétés que vous allez finir par essayer de valider contre un objet « incomplet » (un reste étant chargé). Ceci est très similaire au problème d'initialisation d'objet puisque vous ne pouvez pas contrôler l'ordre dans lequel les champs sont « hydraté ».

La plupart des ORM vous permettent de cartographier la persistance des champs privés au lieu de propriétés, et cela permettra aux objets d'être hydraté, mais si votre logique de validation réside surtout setters de propriété à l'intérieur vous pouvez avoir à dupliquer ailleurs pour vérifier qu'une charge objet est valide ou non.

Depuis de nombreux outils ORM prennent en charge le chargement paresseux (un aspect fondamental d'un ORM!) Grâce à l'utilisation de propriétés virtuelles (ou méthodes) cartographie des champs, il sera impossible pour le ORM à des objets de charge paresseux cartographiées dans les champs.

Conclusion

Ainsi, à la fin, afin d'éviter la duplication de code, permettent ORM d'effectuer le mieux que possible, d'éviter des exceptions surprenantes en fonction de l'ordre des champs sont définis, il est sage de logique Éloigner setters de propriété.

Je suis encore à déterminer où cette logique « de validation » devrait être. Où allons-nous validons les aspects invariants d'un objet? Où allons-nous mettre plus haut niveau de validation? Est-ce que nous utilisons des crochets sur les ORM pour effectuer la validation (OnSave, OnDelete, ...)? Etc Etc Etc Mais ce n'est pas la portée de cette réponse.

Autres conseils

Setters ne marche pas transporter des informations sémantiques.

par exemple.

blogpost.PublishDate = DateTime.Now;

Est-ce que cela veut dire que le message a été publié? Ou tout simplement que la date de publication n'a été établie?

Considérez:

blogpost.Publish();

Cela montre clairement l'intention qui devrait être publié le blogpost.

En outre, setters peuvent briser les invariants d'objets. Par exemple, si nous avons une entité « Triangle », l'invariant doit être que la somme de tous les angles doit être de 180 degrés.

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

Maintenant, si nous avons setters, nous pouvons facilement briser les invariants:

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

Nous avons donc un triangle où la somme de tous les angles sont égaux à 0 ... Est-ce vraiment un triangle? Je dirais que non.

Remplacement setters des méthodes pourrait nous forcer à maintenir invariants:

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

Un tel appel doit lancer une exception vous dire que cela provoque un état non valide pour votre entité.

Ainsi, vous obtenez la sémantique et des méthodes invariants au lieu de setters.

La raison derrière cela est que l'entité elle-même devrait être responsable de changer son état. Il n'y a pas vraiment de raison pour laquelle vous avez besoin de définir une propriété partout en dehors de l'entité elle-même.

Un exemple simple serait une entité qui a un nom. Si vous avez un setter public, vous seriez en mesure de changer le nom d'une entité de partout dans votre application. Si vous supprimez la place que setter et mettre une méthode comme ChangeName(string name) à votre entité qui sera la seule façon de changer le nom. De cette façon, vous pouvez ajouter tout type de logique qui sera toujours exécutée lorsque vous changez le nom parce qu'il n'y a qu'une seule façon de le changer. Ceci est aussi beaucoup plus claire, puis la mise en juste le nom à quelque chose.

En gros, cela signifie que vous exposez le comportement sur vos entités publiques alors que vous gardez en privé l'État.

La question initiale est étiquetée .net, donc je vais présenter une approche pragmatique du contexte dans lequel vous souhaitez lier vos entités directement à une vue.

Je sais que c'est une mauvaise pratique, et que vous devriez probablement avoir un modèle de vue (comme dans MVVM) ou similaire, mais pour certaines petites applications il est logique de ne pas trop patternize à mon humble avis.

Utilisation des propriétés est la façon hors travaux de liaison de données de boîte en .net. Peut-être le contexte que stipule la liaison de données devrait fonctionner dans les deux sens, et donc la mise en œuvre INotifyPropertyChanged et soulevant PropertyChanged comme une partie de la logique setter est logique.

L'entité pourrait par exemple ajouter un élément à une collection de règles cassé ou analogue (je sais AAPC si ce concept quelques années et peut-être le fait encore) lorsque le client établit une valeur non valide, et que la collecte pourrait être indiquée dans l'interface utilisateur. L'unité de travail serait plus tard refuser de persister des objets non valides, si elle doit venir jusque-là.

Je suis en train de justifier le découplage, immuabilité, et ainsi de suite dans une large mesure. Je dis simplement que dans certains contextes une architecture plus simple est nécessaire.

Un setter définit simplement une valeur. Il est pas censé être "heavy" with logic.

Méthodes sur vos objets qui ont de bons noms descriptifs devraient être ceux "heavy" with logic et ont un analogue dans le domaine lui-même.

Je recommande fortement de lire le livre DDD par Eric Evans et Object-Oriented Software Construction par Bertrand Meyer . Ils ont tous les échantillons que vous avez besoin.

Je peux être hors ici, mais je crois setters doivent être utilisés pour définir, par opposition aux méthodes setter. J'ai quelques raisons pour cela.

a) Il est logique en .Net. Chaque développeur sait sur les propriétés. Voilà comment vous définissez les choses sur un objet. Pourquoi écartez de cet pour les objets de domaine?

b) Setters peut avoir le code. Avant 3.5 je crois, la fixation d'un objet composé d'une variable interne et une signature de propriété

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

Il est très facile et élégante de mettre imo la validation du compositeur. Dans IL, accesseurs sont convertis en méthodes de toute façon. Pourquoi le code en double?

Dans l'exemple de la méthode de publication () affichée ci-dessus, je suis entièrement d'accord. Il y a des moments où nous ne voulons pas d'autres développeurs de définir une propriété. Cela devrait être géré par une méthode. Cependant, il ne logique d'avoir une méthode setter pour chaque propriété en .Net offre toutes les fonctionnalités dont nous avons besoin dans la déclaration de biens déjà?

Si vous avez un objet Personne, pourquoi créer des méthodes pour chaque propriété sur elle s'il n'y a pas de raison?

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top