Come creare un metodo factory

0

Dire che ho una classe Business chiamata person:

public class Student { }

Dire che voglio creare un metodo factory per questa classe Student - qualcosa del genere:

public Student getStudent(string studentType)
{   
  if (StudentType=="P")
    return new PostGraduate();
else
  return new Undergraduate();
}

Questo metodo dovrebbe andare nella classe BusinessLayer.Student? Credo che ciò spezzerebbe il principio della responsabilità unica. Su questa base dovrebbe andare in una nuova classe chiamata: StudentFactory?

Se dovrebbe andare in una nuova classe chiamata: StudentFactory, allora questa classe dovrebbe essere nel ServiceLayer (che crea l'oggetto Studente) o nel BusinessLayer? Infine dovrei usare IOC (Castle Windsor) per creare l'oggetto Student? Il rispondente in questa domanda suggerisce che non si dovrebbe utilizzare un IOC per gli oggetti del livello aziendale: Modello di dominio ricco vs anemico

Quindi per riassumere:

1) Il metodo factory dovrebbe essere creato nella classe Student o nella sua classe? 2) Se la risposta a 1 è "propria classe", allora la classe dovrebbe trovarsi nel livello di servizio o nel livello aziendale? 3) L'oggetto del livello aziendale dovrebbe essere gestito da IOC?

    
posta w0051977 11.06.2017 - 21:51
fonte

2 risposte

2

1) Should the factory method be created in the Student class or in its own class?

Sicuramente la sua classe. Come hai detto, la tua classe di studenti ha una responsabilità e la costruzione di istanze di per sé non è una responsabilità.

Detto questo, di solito non scrivo le fabbriche per gli oggetti del livello aziendale. Io uso un modello di dominio anemico, e questi oggetti sono solitamente costruiti dal mio oggetto di accesso ai dati (DAO) dalla persistenza. Il tuo esempio di fabbrica sembra solo la logica che sarebbe nel mio DAO. Sembra un metodo di tabelle polimorfiche in un RDBMS in cui il tipo è memorizzato come una colonna nella tabella. Qualcosa come questo psuedocode:

def getStudents()
  dbCursor = query rdbms "select id, studentType, name from student"
  students = new List
  while dbCursor has next
    studentType = dbCursor get column 2
    if studentType == "P"
      students += new PostGraduate
    else
      students += new Undergraduate
  return students

I vorrebbe utilizzare il modello factory astratto per le istanze del DAO. Soprattutto se possono esistere più implementazioni. Per esempio. in memoria dao per il test e l'impl di PostgreSQL

StudentDaoFactory {
    create(Config config): StudentDao
}

2) If the answer to 1 is "its own class", then should the class be located in the service layer or business layer?

Questo dipende davvero. Chi sta usando la fabbrica? Credi che la fabbrica potrebbe e verrebbe utilizzata da chiunque utilizzi Student ? Se tutti useranno questo factory per costruire istanze di Student di quanto lo metterei nello stesso layer di Student . Se è una fabbrica concepibile solo da un livello diverso, allora mettila lì. Queste domande di solito si risolvono mentre la base di codice matura, perché inevitabilmente la classe finirà nel minimo comune denominatore a cui tutti i livelli dipendenti possono accedere.

3) Should the business layer object be IOC managed?

No. Come ho detto prima, questi oggetti sono generalmente creati da un DAO. IOC è una scelta ragionevole per la gestione di fabbriche e DAO.

    
risposta data 11.06.2017 - 22:47
fonte
3

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 .

    
risposta data 12.06.2017 - 14:59
fonte

Leggi altre domande sui tag