Что считается хорошим дизайном для нескольких похожих классов - PullRequest
3 голосов
/ 11 июля 2011

Я создаю приложение, для которого требуется несколько подклассов (Person) с похожими свойствами (например, Student, Teacher, Contact, User).Там будет значительное совпадение свойств, но также много различий.Например, Человек может быть Студентом, Контактом и Пользователем.Вот пример (ограниченный для ясности):

Person
  FirstName
  LastName
  DOB
  CurrentAge

Student <- Person
  StudentId
  Average
  Email
  Phone

Contact <- Person
  Email
  Phone
  Address

User <- Person
  Email
  UserName
  DOB
  CurrentAge

Я бы хотел избежать многократного написания кода - например: код проверки электронной почты, код, необходимый для вычисления возраста, и т. Д. Кроме того, у нас, вероятно, будетдобавить дополнительные классы позже с аналогичным перекрытием и отличиями.

Что считается хорошим дизайном для того, чтобы справиться с этим, или какие шаблоны дизайна, если таковые имеются, покрывают это?

Из моего базового понимания шаблонов дизайна Decorator не кажется правильным b /c Я не добавляю поведение.Композит не кажется правильным, потому что он не является рекурсивным.Я также понимаю, что не может быть идеального шаблона для этого, однако это кажется очень распространенным требованием.

Если это имеет значение, это будет в основном использоваться в ASP.NET/C#/VB.NET.

На другие вопросы SO есть ответы для двух похожих классов / объектов (обычно это подклассы), но я не смог найти ничего для произвольного числа похожих классов.приветствуется.

Ответы [ 4 ]

4 голосов
/ 11 июля 2011

Вы хотите использовать Composition для объединения функций нескольких разных классов. Что вы действительно ищете, так это то, как спроектировать иерархию объектов таким образом, чтобы вы могли определять различные уровни функциональности в зависимости от ваших потребностей. Лучший и самый простой способ сделать это - использовать Composition для определения функциональности.

Например, в вашем примере Person не обязательно иметь адрес электронной почты; однако, Contact делает, а User делает; они оба наследуются от Person. Чтобы справиться с этим, нужно иметь класс Email, который могут иметь ваши классы Contact и User; этот класс может управлять проверкой и т. д. Если вы действительно хотите, чтобы ваш Email был присоединен к Person, вы можете иметь класс PersonWithEmail (выберите более подходящее имя, пожалуйста!), который наследуется от Person, и который составляет унаследованный Person с классом Email; таким образом, любой класс, который наследуется от PersonWithEmail, получит функциональность Person и Email. Однако проблемы с этим типом подхода заключаются в том, что вы определяете свою композицию непосредственно в иерархии наследования; это может быть нежелательно. Подход прямой композиции проще.

0 голосов
/ 15 июля 2011

Используйте подклассы, если вы можете сказать, что классы следуют по отношению к тигру " - это " Животное, иначе это может быть беспорядок, поддерживающий такую ​​большую иерархию классов, добавляющую новое поведение.

Кроме того, не забудьте сделать базовый класс как abstract, это предотвратит неправильное использование классов в функции, когда кто-то намеревался использовать базовый класс в качестве экземпляра, базовый класс должен быть разработан как базовый, и это все.

Для некоторых типов свойств я бы предложил использовать такой подход вместо базовых классов, которые имеют одни и те же свойства для различных типов объектов, поэтому инкапсулируйте такие свойства с помощью интерфейса и просто реализуйте для объектов, которые осведомлены о деталях контактной информации. :

public interface IContactDetails
{
    string Email { get; }
    string Address { get; }
}
0 голосов
/ 13 июля 2011

Я бы вообще не согласился использовать для этого подклассы по двум причинам:

  • Человек может быть разумным учеником, контактом и пользователем одновременно.
  • Человекможет быть студентом сейчас и учителем в следующем году (и для аспирантов, одновременно ...)

Поместите вещи, которые действительно идентифицируют человека, в класс Person.Отделите все, что связано с ролью, которую этот человек играет, с отдельным классом или интерфейсом, возможно, Role, и создайте подклассы или реализации этого.

Разрешите человеку выполнять несколько ролей, поместив полев вашем Person классе, который является Set, содержащим Role объектов.

0 голосов
/ 11 июля 2011

У вас может быть хранилище в классе Person для всех различных свойств, а также могут быть подклассы для разных типов людей, для которых эти свойства доступны. Таким образом, свойство Phone для студента и контакта может иметь одинаковое хранилище.

Вы можете иметь флаги для подтипов и свойства, чтобы получить человека в качестве подтипа.

Пример:

public class Person {

  public string FirstName { get; set; }
  public string LastName { get; set; }
  public string DOB { get; set; }
  public int CurrentAge { get; set; }

  private string _studentId;
  private double _average;
  private string _email;
  private string _phone;

  public bool IsStudent { get; private set; }

  public Student AsStudent {
    get {
      if (IsStudent) {
        return this as Student;
      } else {
        throw new Exception("This Person is not Student.");
      }
    }
  }

}

public class Student : Person {
  public string StudentId { get { return _studentId; } set { _studentId = value; } }
  public double Average { get { return _average; } set { _average = value; } }
  public string Email { get { return _email; } set { _email = value; } }
  public string Phone { get { return _phone; } set { _phone = value; } }
}

Использование:

Person someone = GetStudentSomehow(...);
Console.WriteLine(someone.FirstName);
Console.WriteLine(someone.AsStudent.Phone);
...