Приналежності більше, ніж поля. Інші вже вказали на кілька важливих відмінностей, і я збираюся додати ще одну.
Властивості беруть участь в інтерфейсних класах. Наприклад:
interface IPerson
{
string FirstName { get; set; }
string LastName { get; set; }
}
Цей інтерфейс можна задовольнити кількома способами. Наприклад:
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;
}
}
...
}
У цій реалізації ми захищаємо як Person
клас від потрапляння в недійсний стан, так і абонента від виведення нуля з непризначеного властивості.
Але ми могли б підштовхнути дизайн ще далі. Наприклад, інтерфейс може не мати справу з сеттером. Цілком законно сказати, що споживачі IPerson
інтерфейсу зацікавлені лише в тому, щоб отримати майно, а не встановлювати його:
interface IPerson
{
string FirstName { get; }
string LastName { get; }
}
Попередня реалізація Person
класу задовольняє цей інтерфейс. Те, що дозволяє абоненту також встановити властивості, безглуздо з точки зору споживачів (які споживають IPerson
). Наприклад, додаткова функціональність конкретної реалізації враховується, наприклад, будівельником:
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);
}
У цьому коді споживач не знає про власників нерухомості - про нього не знати. Споживачеві потрібні лише геттери, і він отримує геттери з інтерфейсу, тобто від контракту.
Інша цілком реальна реалізація IPerson
буде незмінним класом людини та відповідною фабрикою персоналу:
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);
}
У цьому зразку коду споживач знову не має знань щодо заповнення властивостей. Споживач має справу лише з комплектуючими та конкретною реалізацією (а бізнес-логіка, що стоїть за нею, як тестування, якщо ім’я порожнє), залишається спеціалізованим класам - будівельникам і фабрикам. Усі ці операції з полями абсолютно неможливі.