2009-08-01 24 views
1

Essayer de déchiffrer une conception OO appropriée à implémenter. Le scénario de base est que vous avez un PstnNumber qui est essentiellement un numéro de téléphone à 10 chiffres qui commence toujours par 0 (par exemple 0195550000). Une règle a été introduite pour permettre la correction automatique d'un nombre si le 0 principal est manquant (par exemple 195550000).Quelle option de conception est la plus appropriée pour l'auto-correction sur la construction?

DÉBUT EDIT

Je réalise peut-être été mal compris la question initiale (merci de bien vouloir ceux qui ont déjà répondu), donc j'ai modifié pour essayer de mieux expliquer le scénario.

FIN EDIT

J'ai commencé à jouer avec des concepts préliminaires et pensé que je demande s'il y avait une façon plus appropriée pour aller ou faire un de ces suffisent (à un certain niveau)?

Concept 1

public class PstnNumber 
{ 
    public virtual string Number { get; set; } 

    public PstnNumber() { } 

    public PstnNumber(string number) 
    { 
     this.Number = number; 
    } 
} 

public class AutoFormattedPstnNumber : PstnNumber 
{ 
    public override string Number 
    { 
     get { return base.Number; } 
     set { base.Number = value.PadLeft(10, '0'); } 
    } 

    public AutoFormattedPstnNumber() : base() { } 

    public AutoFormattedPstnNumber(string number) 
    { 
     this.Number = number; 
    } 
} 

Concept 2 (supprimé)

Concept 3

public class PstnNumber 
{ 
    public bool AutoCorrect { get; set; } 

    private string number; 
    public virtual string Number 
    { 
     get { return (this.AutoCorrect) ? this.number.PadLeft(10, '0') : this.number; } 
     set { this.number = value; } 
    } 

    public PstnNumber() : this(false) { } 

    public PstnNumber(bool autoCorrect) 
    { 
     this.AutoCorrect = autoCorrect; 
    } 

    public PstnNumber(string number) : this(false) 
    { 
     this.Number = number; 
    } 

    public PstnNumber(string number, bool autoCorrect) : this(autoCorrect) 
    { 
     this.Number = number; 
    } 
} 

Je pense que Concept 1 peut constituer une violation de la règle de substitution Liskov parce que la sous-classe modifie le comportement du numéro propriété (heureux d'apprendre si j'ai mal compris cela).

Toutes les suggestions alternatives seraient reçues avec bonheur.

+0

N'existe-t-il pas une option pour, où vous ne développez pas du tout la classe, et ayant simplement une fonction de créateur statique qui formate automatiquement le nombre et retourne un objet pstnnumber? – Zed

+0

@Zed: Suggérez-vous par exemple. PstnNumber AutoPstnFormatter.CreatePstn ("195550000"); ? –

Répondre

4

devez-vous effectuer l'autoformatage lorsque l'objet est instancié? Sinon, Qu'en est-:

public class PstnNumber 
    { 
    public virtual string Number { get; set; } 
    public PstnNumber() { } 
    public PstnNumber(string number) { this.Number = number; } 
    public AutoFormatNumber { get { return Numer.PadLeft(10, '0'); } } 
    } 
+0

@Charles: Merci Charles, mon souci ici serait que lorsque vous ajoutez une méthode Validate, quelle propriété validez-vous? Idéalement, vous voulez valider ceci. Le numéro et la règle de formatage automatique devraient permettre le passage de 195550000. –

+0

@Davide, la nouvelle propriété n'a qu'un getter, et utilise le même champ privé (implicite) que Number utilise, donc elle n'a pas besoin d'un validateur. –

+0

peut augmenter la complexité des opérations supplémentaires comme la comparaison, car ils doivent prendre en charge les deux formats. – peterchen

1

Dans l'option 1 et l'option 2, vous n'êtes pas préservez le numéro d'origine de toute façon, ce qui rend le sans valeur de sous-classe (sauf pour savoir qu'il était autoformatted à un moment donné, ce qui n » t semble être une information utile). L'alternative pour rendre ces options plus utiles serait de formater sur Get au lieu de Set.

L'option 3 est donc le modèle préféré parmi ces trois options, mais je demanderais aussi - pourquoi le PstnNumber ne peut-il pas simplement détecter le nombre de chiffres et autoformer en conséquence?

1

Si vous suivez les règles - il y en a une qui dit que "chaque routine (classe de lecture) ne devrait faire qu'une seule chose et le faire bien".

Selon ce que je ferais PstnNumber juste tenir le nombre, et créer une sorte d'usine qui produit le bon nombre. Faire les deux dans la même classe signifie que vous tissez la logique et la représentation du domaine. Je les préfère séparés.

+0

+1 - Accepté - et PstnNumber ne doit être autorisé à contenir que des nombres correctement formatés. Terminé. Beaucoup d'autres solutions sont psychologiquement alambiquées et sur-machinées. –

1

Je demanderais pourquoi votre nom de classe est si cryptique. "Number" est clair pour moi, et "P" suggère "phone", mais qu'est-ce que le "stn" me dit? Quelques frappes supplémentaires rendraient cette classe plus auto-documentée.

Je voudrais également poser des questions sur la logique d'un constructeur par défaut qui n'initialise pas les membres de données sous-jacents à une certaine valeur. Je pense qu'un constructeur par défaut devrait avoir une valeur par défaut raisonnable si possible.J'ai l'impression que l'option 1 est trop lourde. Je ne pense pas que l'héritage rend ce modèle plus clair ou meilleur. Je ne vois pas comment il rompt la substitution de Liskov, qui exige que vous puissiez utiliser la sous-classe dans n'importe quelle situation qui appelle une classe de base. Les méthodes carte 1: 1 autant que je peux voir. Comment Liskov est-il violé?

L'option 2 indique qu'il s'agit de deux classes distinctes sans relation. Cela ne me semble pas juste.

Tout ce travail suggère que votre problème nécessitera que vous utilisiez les deux classes. Vous aurez des situations où le premier zéro n'est pas requis et d'autres où il est. Est-ce vrai? Ou allez-vous toujours exiger le zéro principal?

Je ne me soucie pas de l'une de vos options. Je préférerais une interface ou une usine statique ou même modifier la classe que vous avez à tout ce que vous avez suggéré. Cela ressemble à un simple problème de formatage. Stockez-vous le nombre avec le premier zéro? Sinon, c'est peut-être juste un point de vue.

+0

voir: http://en.wikipedia.org/wiki/Public_switched_telephone_network – Zed

+1

Merci Zed, mais je ne vois toujours pas comment "PstnNumber" est plus clair que "PhoneNumber". Quelle valeur ajoutée est ajoutée? – duffymo

+0

Je suis d'accord avec vous. "PhoneNumber" est beaucoup plus clair. Steve McConnell dit: «Une technique efficace pour trouver un bon nom est d'énoncer en mots ce que la variable représente, souvent cette déclaration elle-même est le meilleur nom de variable, facile à lire parce qu'elle ne contient pas d'abréviations cryptées et sans ambiguïté. " (p260 - Emphasis Added) –

0

Je ne suis pas familier avec C#, mais je ferais ceci:

public class PstnNumber { 
    readonly string number; 

    public PstnNumber(string number) { 
    this.number = number; 
    } 

    public string getNumber() { 
    return number; 
    } 

    static public PstnNumber createNumber(string number) { 
    return new PstnNumber(number.PadLeft(10, '0')); 
    } 
} 

Bien sûr, si je savais comment les propriétés de travail, je ferais probablement différemment :)

+0

Comme "chaîne publique numéro {get; private set;}" –

3

éviter getter -setter-surprise
Éviter que les getters retournent une valeur différente de celle acceptée par le setter. Imaginez l'extrait suivant:

if (input.Value != current.Number) 
{ 
    NumberChangedAgain = true; 
    current.Number = input.Value; 
} 

Une solution simple serait de faire PstnNumber immuable:

temp = PstnNumber.FromString(input.Value); 
if (temp != current) { ... } 

format canonique
Si certaines données ont des représentations différentes, il y a beaucoup d'avantages à stocker dans une représentation canonique, et déplacer les conversions de format vers les fonctions d'usine et les getters/formateurs. Par exemple, vous n'avez pas besoin de tester la comparaison entre court et long, long et court, court et court, long et long.

différents aspects
Avez-vous besoin de la distinction entre un « autoformatted » et un nombre « normal », ou est-ce simplement une question d'entrée et de sortie - à savoir

  • ne format d'affichage (court ou long) dépend de la façon dont le numéro a été entré, ou sur l'endroit où il est affiché?
  • est 0195550000 = 195550000?

Je préfère plier les deux classes dans une si possible (lorsque « entré avec ou sans 0 peut être oublié »):

public class PstnNumber 
{ 
    private string m_number; // always in long format 
    public static PstnNumber(string s) { ... } // accepts short and long form 

    public string Number { get { return m_number; } } 
    public string AutoFormatted { { get { ... } } 
} 

Sinon, je partirais avec l'option 3, mais stockez toujours le format long dans m_number.

1

Avez-vous une très bonne raison d'avoir un setter et de ne pas avoir vos membres définitifs? Sinon, c'est probablement un problème plus important que toute autre variation entre les trois.Donc, je voudrais un état n ° 3 qui signifie rendre le nombre final et gettng débarrasser de la variable autoFormat.

Pour simplifier, je voudrais juste avoir un getNumberRaw et getNumberFormatted

Mieux encore, vous pourriez avoir getNumberRaw et getNumber (formatType) où formatType contient en fait le code qui formate le numéro depuis le format peut changer à l'avenir et combiner la mise en forme (vue) avec votre numéro de téléphone (modèle) n'est pas optimale. (PS/EDIT): le simple fait qu'un numéro de téléphone puisse changer n'est PAS une bonne raison d'avoir un setter! Créer un nouvel objet de numéro de téléphone et remplacer l'ancien fonctionnera presque toujours!

0

Je voudrais aller avec une version beaucoup plus simple, en remplaçant la méthode ToString, ou même en créant une surcharge ToString qui reçoit le paramètre bool indiquant que le nombre doit être formaté.