2009-05-14 8 views
6

Así que se metió en una discusión amistosa con un compañero de trabajo a través de una pieza de código:¿Debo usar un campo o propiedad dentro de la clase para establecer los valores

public sealed class NewObject 
{ 
    private string _stuff = string.Empty; 

    public string Stuff 
    { 
     get { return GetAllStuff(); } 
    } 

    private string GetAllStuff() 
    { 
     //Heavy string manipulation of _stuff 
    } 

    public NewObject(string stuffToStartWith) 
    { 
     _stuff = stuffToStartWith; 
    } 

    public static NewObject operator +(NewObject obj1, NewObject obj2) 
    { 
     if (obj1 == null) 
      throw new ArgumentNullException(); 

     if (obj2 == null) 
      throw new ArgumentNullException(); 

     NewObject result = new NewObject(string.Empty); 
     result._stuff = String.Concat(obj1._stuff, obj2._stuff); 

     return result; 
    } 
} 

El argumento era sobre la anulación del operador. Mi compañero de trabajo siente que no es la mejor práctica de programación establecer valores de campos privados en ninguna parte que no sea el constructor. La solución propuesta por mi compañero de trabajo fue refactorizar el nombre de la propiedad Stuff al AllStuff y agregar una propiedad, Stuff, que tiene un acceso get Y set y usar la nueva propiedad Stuff en la anulación del operador. Para que se vea así:

public static NewObject operator +(NewObject obj1, NewObject obj2) 
    { 
     if (obj1 == null) 
      throw new ArgumentNullException(); 

     if (obj2 == null) 
      throw new ArgumentNullException(); 

     NewObject result = new NewObject(string.Empty); 
     result.Stuff = String.Concat(obj1.Stuff, obj2.Stuff); 

     return result; 
    } 

I disagree. Creo que la primera forma es mejor, ya que mantiene la propiedad solo de lectura fuera de la clase. Mi pregunta es, ¿cuál es la mejor práctica para el diseño orientado a objetos?

Respuesta

7

Puede darse un private set en la propiedad (que mantendría la visibilidad o la falta de la misma al tiempo que le permite utilizar la sintaxis de la propiedad), pero eso realmente no aborda el punto.

Dentro de la clase, digo que las variables son juego limpio. En cualquier lugar fuera, incluidas las clases heredadas, debe get y set la propiedad, pero dentro de la clase declarante, digo que está bien asignar el miembro privado.

2

Tienes razón

err ... a elaborar, sus variables privadas son suyos para hacer lo que quieras. Si alguien realiza una operación que cambia el valor del objeto (especialmente algo como +), no hay nada de malo en modificar el valor fuera del constructor. Esa es la idea de que sean privados.

A menos que quiera que inmutable ...

actualización
Cuanto más lo pienso, más me cree que su compañero de trabajo está confundiendo las variables 'privadas' con las 'constantes' - o tal vez fusionando los dos conceptos. No hay ninguna razón para que las variables privadas tengan que permanecer iguales durante toda la vida del objeto, que es lo que su amigo parece estar implicando. const es inmutable, private es solo para el objeto, son dos patrones muy distintos.

Update2
Además, su diseño se desmorona si de repente el objeto tiene más que una cadena - y las variables están entrelazados (pensar en un objeto de cadena, que tiene un char * y una len, y debe estar mantenido juntos). Lo último que desea es que el usuario tenga que ocuparse de las variables internas de un objeto. Deje que el objeto sea un objeto y mantenga sus propios valores internos y presente una sola entidad para el usuario.

+0

@cyber: Creo que esto es más una cuestión de que el OP no está del todo claro. Me pareció que su compañero de trabajo abogaba por no cambiar una variable privada directamente fuera del constructor o la implementación de la propiedad. No creo que defendiera mantener los valores iguales para la vida del objeto. –

1

No veo cuál sería el beneficio de su enfoque.

0

Yo personalmente prefiero tener ningún campo en absoluto, por lo tanto, yo uso private propiedades implementadas automáticamente en lugar de private campos y public-getprivate-set propiedades si quieren tener public propiedades de sólo lectura.
Si tengo que agregar código a la propiedad, solo uso el campo dentro de los descriptores de acceso a la propiedad y uso los getters y setters en todas partes, incluido el constructor.
También tengo que usar campos, si necesito campos readonly, pero C# 4.0 introducirá propiedades de solo lectura.


Además, habría evitado todo el problema utilizando el siguiente código.

public static NewObject operator +(NewObject obj1, NewObject obj2) 
{ 
    return new NewObject(String.Concat(obj1.Stuff, obj2.Stuff)); 
} 

Mi aplicación preferida sería algo como esto.

public sealed class NewObject 
{ 
    private String Stuff { get; set; } 

    // Use a method instead of a property because the operation is heavy.  
    public String GetAllStuff() 
    { 
     // Heavy string manipulation of this.Stuff. 
     return this.Stuff; 
    } 

    // Or lets use a property because this.GetAllStuff() is not to heavy. 
    public String AllStuff 
    { 
     get { return this.GetAllStuff(); } 
    } 

    public NewObject(String stuffToStartWith) 
    { 
     this.Stuff = stuffToStartWith; 
    } 

    public static NewObject operator +(NewObject obj1, NewObject obj2) 
    { 
     // Error handling goes here. 

     return new NewObject(String.Concat(obj1.Stuff, obj2.Stuff); 
    } 
} 
+0

¿Entiendo mal lo que quiere decir con "propiedades de solo lectura"? Ahora mismo puedo codificar una propiedad sin setter, por lo que es de solo lectura ... –

2

La cuestión general tiene que ver con una política de contrato.

La noción de una propiedad (conjunto público) es que cuando se invoca, se pueden realizar otras acciones además de la noción semántica de cambio de estado. Por ejemplo, llamar a un setter puede disparar eventos, activar un dispositivo periférico, etc.

Su compañero de trabajo dice que al no usar la propiedad, usted está dejando de lado el contrato y no se realizarán eventos.

Así que aquí es que debe hacer desde el punto de vista de su compañero de trabajo:

this.Prop = CalculateSomeValue(); 
if (this.Prop < kPropMin) { 
    this.Prop = kPropMin; 
} 
else if (this.Prop > kPropMax * 2) { 
    this.Prop = kPropMax * 2; 
} 
this.Prop = this.Prop/2; 

Ahora, este es un caso artificial, pero yo sólo le he pegado a una posible propiedad de peso pesado hasta tres veces en el get y hasta hasta tres veces en el conjunto, y una de ellas podría ser ilegal (estableciéndose en kHighLimit/2). Puedo solucionar esto utilizando un local y llamando al conjunto precisamente una vez al final. Prefiero meterme en el campo, sin embargo.

Creo que un mejor enfoque es tomarlo pragmáticamente: use la propiedad dentro de su clase si y solo si desea invocar todos los efectos secundarios de un conjunto o un obtener, de lo contrario obedecer el espíritu de la propiedad en su lugar.

- aclaración - Por obedecen al espíritu de la propiedad, digamos que mi propiedad conjunto se parece a esto:

bool PropValueOutOfRange(int val) { 
    return val < kPropMin || val > kPropMax; 
} 

public int Prop { 
    set { 
     if (PropValueOutOfRange(value)) 
      throw new ArgumentOutOfRangeException("value"); 
     if (PropValueConflictsWithInternalState(value)) 
      throw new ArgumentException("value"); 
     _prop = value; 
     NotifyPeriperalOfPropChange(_prop); 
     FirePropChangedEvent(/* whatever args might be needed */); 
    } 
} 

En este he refactorizada muchos de los detalles sucios, pero eso me permite reutilizarlos. Así que ahora estoy seguro de tocar el campo privado _prop porque tengo la misma infraestructura para asegurarme de mantenerlo en rango y notificar a los periféricos y activar el evento.

Esto me permite escribir este código:

_prop = CalculateSomeValue(); 

if (_prop < kPropMin) 
    _prop = kPropMin; 
else if (_prop > kPropMax * 2) 
    _prop = kPropMax; 

_prop /= 2; 

NotifyPeripheralOfPropChange(); 
FirePropChangedEvent(); 

estoy usando las mismas herramientas que los utilizados para la construcción de la propiedad, así que estoy trabajando dentro del espíritu de la propiedad. Mantengo el rango correcto (pero no lo arrojo, lo sé mejor, soy el implementador), golpeo los eventos periféricos y de disparo, y lo hago de forma cuidadosa, legible y eficiente, no indiscriminadamente.

+0

+1 porque después de leer esto creo que este es probablemente el proceso de pensamiento de mi compañero de trabajo. Sin embargo, a los efectos del código en disputa, la propiedad nunca debe cambiarse fuera de la clase por ningún motivo. –

+0

Pregunta: En su enfoque pragmático, cuando dice 'obedecer el espíritu de la propiedad', ¿quiere decir que si la propiedad nunca cambia fuera de la clase, entonces use el campo para actualizar como lo estoy haciendo arriba? –

Cuestiones relacionadas