L'essenza di ciò che sto cercando di fare è ottenere un'istanza del servizio utente appropriato, quindi passare qualsiasi sottotipo di User
con cui stiamo lavorando.
Modelli:
public class User
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
public class Student : User
{
public int Grade { get; set; }
}
public class Staff : User
{
public DateTime HireDate { get; set; }
}
Servizi:
public interface IUserService<T> where T : User
{
void Save(T user);
}
public class StudentService : IUserService<Student>
{
public void Save(Student user)
{
// Student-specific code
}
}
public class StaffService : IUserService<Staff>
{
public void Save(Staff user)
{
// Staff-specific code
}
}
Il mio primo tentativo è stato di rinnovare il servizio di matching e up-cast. Ho scoperto che non funzionava dal momento che violerebbe la sicurezza del tipo: ad esempio, il compilatore non sarebbe in grado di dire se stavo passando in seguito in un utente compatibile.
public void Process(User user)
{
IUserService<User> service;
if (user is Staff)
service = (IUserService<User>)new StaffService(); // InvalidCastException
else if (user is Student)
service = (IUserService<User>)new StudentService(); // InvalidCastException
else
throw new ArgumentException("...");
service.Save(user);
}
Capisco perché è il caso ora, ma non sono riuscito a trovare un'alternativa che permetta ancora:
- Applicazione di un contratto (qualsiasi servizio utente deve implementare questi metodi)
- Chiamare i metodi in modo generico (basta passare l'oggetto utente)
- Come evitare il codice duplicato
Quello che davvero non voglio fare è finire con qualcosa di simile:
if (user is Staff)
{
var service = new StaffService();
service.Save(user);
}
else if (user is Student)
{
var service = new StudentService();
service.Save(user);
}
// etc.
Non così male con due tipi di utenti e una chiamata al metodo, ma il codice effettivo è più complesso.