2008-09-17 7 views
4

J'ai le type:une sorte de modèle créationnelle nécessaire en C#

// incomplete class definition 
public class Person 
{ 
    private string name; 

    public string Name 
    { 
     get { return this.name; } 
    } 
} 

Je veux que ce type à créé et mis à jour avec une sorte de contrôleur/constructeur dédié, mais je veux pour rester en lecture seule pour les autres types.

Cet objet doit également déclencher un événement chaque fois qu'il est mis à jour par son contrôleur/générateur.

Pour résumé, selon le squelette précédent de définition de type:

  • Le Person ne pouvait être instanciée par un contrôleur spécifique
  • Ce contrôleur pourrait mise à jour l'état du (champ name) Person à à tout moment
  • Le Person besoin d'envoyer une notification au reste du monde quand il se produit
  • Tous les autres types ne devraient pouvoir lire Person attributs

Comment dois-je mettre en œuvre ce? Je parle d'un contrôleur/constructeur ici, mais toutes les autres solutions sont les bienvenues.

Note: Je serais en mesure de compter sur le modificateur internal, mais idéalement tout mon matériel devrait être dans le même assemblage.

Répondre

1

J'aime avoir une interface en lecture seule. Ensuite, le constructeur/contrôleur/tout peut référencer l'objet directement, mais lorsque vous exposez cet objet à l'extérieur, vous n'affichez que l'interface.

1

Utilisez une interface IPerson et une classe imbriquée:

public class Creator 
{ 
    private class Person : IPerson 
    { 
     public string Name { get; set; } 
    } 

    public IPerson Create(...) ... 


    public void Modify(IPerson person, ...) 
    { 
     Person dude = person as Person; 
     if (dude == null) 
      // wasn't created by this class. 
     else 
      // update the data. 
    } 
} 
5

Créer une interface IReadOnlyPerson qui expose seulement obtenir accesseurs. Demander à Person d'implémenter IReadOnlyPerson. Enregistrez la référence à la personne dans votre contrôleur. Ne donnez à d'autres clients que la version en lecture seule.

Ceci protégera contre les erreurs, mais pas la fraude, comme avec la plupart des fonctionnalités OO. Les clients peuvent lancer un cast vers Person s'ils savent (ou suspectent) que IReadOnlyPerson est implémenté par Person.

mise à jour, par le commentaire:

L'interface en lecture seule peut également exposer un délégué d'événement, comme tout autre objet. L'idiome généralement utilisé en C# n'empêche pas les clients de jouer avec la liste des écouteurs, mais la convention est seulement d'ajouter des écouteurs, donc cela devrait être adéquat. À l'intérieur de n'importe quel accesseur ou fonction avec des effets secondaires changeant d'état, appelez simplement le délégué de l'événement avec un garde pour le cas null (no listeners).

+0

Cette réponse pourrait être la bonne, mais il ne couvre pas la deuxième partie de la question: La personne a besoin d'envoyer une notification au reste du monde quand il se produit – Seb

+2

Vérifiez l'interface "System.ComponentModel.INotifyPropertyChanged" pour la méthode standard de notification aux parties intéressées qu'une propriété de l'objet a changé. –

0

Peut-être quelque chose comme ça?

public class Person 
{ 
    public class Editor 
    { 
     private readonly Person person; 

     public Editor(Person p) 
     { 
      person = p; 
     } 

     public void SetName(string name) 
     { 
      person.name = name; 
     } 

     public static Person Create(string name) 
     { 
      return new Person(name); 
     } 
    } 

    protected string name; 

    public string Name 
    { 
     get { return this.name; } 
    } 

    protected Person(string name) 
    { 
     this.name = name; 
    } 
} 

Person p = Person.Editor.Create("John"); 
Person.Editor e = new Person.Editor(p); 
e.SetName("Jane"); 

Pas joli, mais je pense que cela fonctionne. Vous pouvez également utiliser les propriétés à la place des méthodes SetX dans l'éditeur.

1

Je pense que internal est l'approche la moins complexe et la meilleure (cela implique bien entendu plusieurs assemblages). À court de faire une pile intensive en tête à pied pour déterminer l'appelant dans le poseur de propriété que vous pouvez essayer:

interface IPerson 
{ 
    Name { get; set; } 
} 

et mettre en œuvre cette interface explicitement:

class Person : IPerson 
{ 
    Name { get; private set; } 
    string IPerson.Name { get { return Name; } set { Name = value; } } 
} 

puis effectuer une interface explicite jette dans votre constructeur pour la mise en Propriétés. Cela ne protège toujours pas votre implémentation et n'est pas une bonne solution, même si cela contribue à renforcer votre intention.

Dans vos paramètres de propriété, vous devez implémenter une notification d'événement. En approchant moi-même ce problème, je ne créerais pas d'événements et de gestionnaires d'événements séparés pour chaque propriété mais créerais un seul événement PropertyChanged et le déclencherais dans chaque propriété lorsqu'une modification surviendrait (où les arguments d'événement incluraient le nom de la propriété, ancienne valeur et nouvelle valeur).

1

Cela semble étrange que, bien que je ne puisse pas changer le nom de l'objet Personne, je peux simplement attraper son contrôleur et le changer là. Ce n'est pas un bon moyen de sécuriser les données de votre objet.

Mais, malgré, voici une façon de le faire:

/// <summary> 
    /// A controlled person. Not production worthy code. 
    /// </summary> 
    public class Person 
    { 
     private string _name; 
     public string Name 
     { 
      get { return _name; } 
      private set 
      { 
       _name = value; 
       OnNameChanged(); 
      } 
     } 
     /// <summary> 
     /// This person's controller 
     /// </summary> 
     public PersonController Controller 
     { 
      get { return _controller ?? (_controller = new PersonController(this)); } 
     } 
     private PersonController _controller; 

     /// <summary> 
     /// Fires when <seealso cref="Name"/> changes. Go get the new name yourself. 
     /// </summary> 
     public event EventHandler NameChanged; 

     private void OnNameChanged() 
     { 
      if (NameChanged != null) 
       NameChanged(this, EventArgs.Empty); 
     } 

     /// <summary> 
     /// A Person controller. 
     /// </summary> 
     public class PersonController 
     { 
      Person _slave; 
      public PersonController(Person slave) 
      { 
       _slave = slave; 
      } 
      /// <summary> 
      /// Sets the name on the controlled person. 
      /// </summary> 
      /// <param name="name">The name to set.</param> 
      public void SetName(string name) { _slave.Name = name; } 
     } 
    } 
Questions connexes