Il primo esempio di riga e codice delinea il vero problema da risolvere:
Say I have a Business class called person:
public class Student { }
Hai una classe business chiamata "persona" ma la classe è denominata "Studente". Vuoi sapere come restituire un tipo concreto in base al tipo di laurea in cui stanno andando. Questo è il problema da risolvere.
Hai bisogno di una classe Person:
public class Person { }
E hai bisogno di una classe Student
, che prende un Person
e un Degree
:
public class Student
{
private Degree degree;
private Person person;
public Student(Person person, Degree degree)
{
this.person = person;
this.degree = degree;
}
}
Il fatto che una persona sia uno studente in una scuola è l'informazione su una persona (un attributo). Una persona può essere uno studente in più scuole. Negli studenti statunitensi al liceo è possibile "raddoppiarsi" in un college locale, essenzialmente diventando studenti di due istituzioni accademiche.
Solo pensando ai college, una persona può essere un ex studente come Undergrad e tornare per la laurea e poi il P.H.D.
Quindi il tipo di grado è anche un attributo - non della persona ma dello studente.
Qualsiasi comportamento specifico per un tipo di laurea dovrebbe essere inserito nella classe Degree. La classe Degree
è in realtà in cui desideri sfruttare il polimorfismo e l'ereditarietà:
public abstract class Degree
{
public abstract bool CanRegisterForClass(Class classToRegister);
}
public class UndergraduateDegree : Degree
{
public override bool CanRegisterForClass(Class classToRegister)
{
return classToRegister.Level < 400;
}
}
public class GraduateDegree : Degree
{
public override bool CanRegisterForClass(Class classToRegister)
{
return classToRegister.Level < 600
&& classToRegister.Level >= 200;
}
}
public class DoctoralDegree : Degree
{
public override bool CanRegisterForClass(Class classToRegister)
{
return classToRegister.Level < 700
&& classToRegister.Level >= 500;
}
}
Qui abbiamo un metodo astratto chiamato CanRegisterForClass(...)
e tre classi concrete. Ogni grado controlla il Level
della classe per vedere se un Student
può registrarsi per questo:
public class Student
{
private Degree degree;
private Person person;
private List<RegisteredClass> registeredClasses;
public Student(Person person, Degree degree)
{
this.person = person;
this.degree = degree;
this.registeredClasses = new List<RegisteredClass>();
}
public RegisteredClass RegisterForClass(Class classToRegister)
{
if (!degree.CanRegisterForClass(classToRegister))
throw new InvalidOperationException("Cannot register for this class");
var registeredClass = new RegisteredClass(this, classToRegister);
registeredClasses.Add(registeredClass);
return registeredClass;
}
}
}
Se uno studente può iscriversi o meno a una classe dipende dal grado. Questo comportamento viene inserito in una classe che può gestirlo senza dichiarazioni di if
ripetitive.
Ora sei in un punto in cui hai bisogno di un metodo factory, che può essere semplice come un metodo statico sulla classe Degree
:
public abstract class Degree
{
public abstract bool CanRegisterForClass(Class classToRegister);
public static Degree CreateDegree(string studentType)
{
switch (studentType)
{
case "U":
return new UndergraduateDegree();
case "G":
return new GraduateDegree();
case "D":
return new DoctoralDegree();
default:
throw new InvalidOperationException("Invalid student type");
}
}
}
L'ultima domanda a cui rispondere è chi dovrebbe chiamare questo metodo factory. In questo caso, la parte della tua applicazione responsabile della creazione di oggetti Student
è dove questo metodo factory dovrebbe essere invocato. Se stai utilizzando un ORM come Entity Framework o NHibernate per mappare questi oggetti, dovresti essere in grado di configurare l'ORM per fare questo mapping per te in base ai valori in una determinata colonna di una tabella nel database.
Senza ORM disponibile, una classe di servizio può farlo, o anche una classe factory per Student
oggetti, che si limita a delegare al metodo statico sulla classe Degree
.