Los accesores son más que campos. Otros ya han señalado varias diferencias importantes, y voy a agregar una más.
Las propiedades participan en las clases de interfaz. Por ejemplo:
interface IPerson
{
string FirstName { get; set; }
string LastName { get; set; }
}
Esta interfaz se puede satisfacer de varias maneras. Por ejemplo:
class Person: IPerson
{
private string _name;
public string FirstName
{
get
{
return _name ?? string.Empty;
}
set
{
if (value == null)
throw new System.ArgumentNullException("value");
_name = value;
}
}
...
}
En esta implementación, estamos protegiendo tanto a la Person
clase de entrar en un estado no válido como a la persona que llama a salir de la propiedad no asignada.
Pero podríamos impulsar el diseño aún más. Por ejemplo, la interfaz podría no tratar con el configurador. Es bastante legítimo decir que los consumidores de la IPerson
interfaz solo están interesados en obtener la propiedad, no en configurarla:
interface IPerson
{
string FirstName { get; }
string LastName { get; }
}
La implementación previa de la Person
clase satisface esta interfaz. El hecho de que permite que la persona que llama también establezca las propiedades no tiene sentido desde el punto de vista de los consumidores (que consumen IPerson
). El constructor, por ejemplo, tiene en cuenta la funcionalidad adicional de la implementación concreta:
class PersonBuilder: IPersonBuilder
{
IPerson BuildPerson(IContext context)
{
Person person = new Person();
person.FirstName = context.GetFirstName();
person.LastName = context.GetLastName();
return person;
}
}
...
void Consumer(IPersonBuilder builder, IContext context)
{
IPerson person = builder.BuildPerson(context);
Console.WriteLine("{0} {1}", person.FirstName, person.LastName);
}
En este código, el consumidor no sabe acerca de los creadores de propiedades, no es su negocio saberlo. El consumidor solo necesita captadores, y obtiene captadores de la interfaz, es decir, del contrato.
Otra implementación completamente válida de IPerson
sería una clase de persona inmutable y una fábrica de personas correspondiente:
class Person: IPerson
{
public Person(string firstName, string lastName)
{
if (string.IsNullOrEmpty(firstName) || string.IsNullOrEmpty(lastName))
throw new System.ArgumentException();
this.FirstName = firstName;
this.LastName = lastName;
}
public string FirstName { get; private set; }
public string LastName { get; private set; }
}
...
class PersonFactory: IPersonFactory
{
public IPerson CreatePerson(string firstName, string lastName)
{
return new Person(firstName, lastName);
}
}
...
void Consumer(IPersonFactory factory)
{
IPerson person = factory.CreatePerson("John", "Doe");
Console.WriteLine("{0} {1}", person.FirstName, person.LastName);
}
En este ejemplo de código, el consumidor una vez más no tiene conocimiento de llenar las propiedades. El consumidor solo se ocupa de los captadores y la implementación concreta (y la lógica empresarial detrás de esto, como probar si el nombre está vacío) se deja a las clases especializadas: constructores y fábricas. Todas estas operaciones son completamente imposibles con los campos.