I am building an application that requires a number of subclasses (of Person) with similar properties (ex: Student, Teacher, Contact, User). There will be considerable overlap in properties, but also many differences. For example, a Person could be a Student, Contact and User. Here is an example (limited for clarity):
Person
FirstName
LastName
DOB
CurrentAge
Student <- Person
StudentId
Average
Email
Phone
Contact <- Person
Email
Phone
Address
User <- Person
Email
UserName
DOB
CurrentAge
I would like to avoid writing code multiple times - ex: email validation code, the code required to calculate Age, etc. Also, we will probably have to add additional classes later with similar overlapp and differences.
What is considered good design for handling this, or what design pattern(s), if any, cover this?
From my basic understanding of design patterns, Decorator does not seem right b/c I am not adding behavior. Composite does not seem right b/c it is not recursive. I also understand that there may not be an ideal pattern for this, however, it seems like a very common requirement.
If it matters, this will mainly be used in ASP.NET/C#/VB.NET.
Other SO questions have answers for two similar classes/objects (generally subclassing) but I could not find anything for an arbitrary number of similar classes.
Any suggestions regarding the associ开发者_StackOverflow社区ated database design are also welcome.
You want to use Composition to combine the functionality of multiple differing classes. What you're really looking for is how to design your object hierarchy such that you can define varying levels of functionality based upon your needs. The best and simplest way to do this is to use Composition to be able to define functionality.
For example, in your example, a Person
does not need to have an email address; however, a Contact
does, and a User
does; they both inherit from Person
. The way to handle this is to have an Email
class, which then your Contact
and User
classes can have; that class can manage the validation, etc. If you really wish to have your Email
attached to a Person
, you can have a PersonWithEmail
class (choose a better name, please!) which inherits from Person
, and which composes the inherited Person
with the Email
class; that way, any class which inherits from PersonWithEmail
will gain the Person
functionality and the Email
functionality. The issues with this type of approach, however, are that you're defining your composition directly in your inheritance hierarchy; this may not be desired. The direct Composition approach is simpler.
You could have storage in the Person
class for all the different properties, and have subclasses for the different types of persons where the properties are reachable. That way the Phone
property for a student and a contact could have the same storage.
You can have flags for the subtypes, and properties to get a person as the subtype.
Example:
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; } }
}
Usage:
Person someone = GetStudentSomehow(...);
Console.WriteLine(someone.FirstName);
Console.WriteLine(someone.AsStudent.Phone);
I would argue against using subclassing for this at all, for two reasons:
- A person can sensibly be a student, a contact and a user simultaneously.
- A person can be a student now and a teacher next year (and for grad students, both at once...)
Put the things that actually identify a person in a Person
class.
Separate everything that has to do with a role that person is playing into a separate class or interface, possibly Role
, and create subclasses or implementations of that.
Allow a person to have multiple roles by putting a field in your Person
class that is a Set
containing Role
objects.
Use subclassing in case if you can say that classes are follow relation Tiger "is an" Animal, otherwise it could be a mess maintaining such a large hierarchy of classes adding a new behaviour.
Also, do not forget to make a base class as abstract
, it prevent incorrect usage of classes in feature when someone intended to use base class as instance, base class should be designed to be a base, and thats it.
For some kind of properties iI would propose to use such an approach instead of base classes which share the same properties for different kind of entities, so encapsulate such properties by an interface and just implement for entities which are aware of contact information details:
public interface IContactDetails
{
string Email { get; }
string Address { get; }
}
精彩评论