Question

Say I have a Business class called person:

public class Student { }

Say I want to create a factory method for this Student class - something like this:

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

Should this method go in the BusinessLayer.Student class? I believe this would break the single responsibility principle. On that basis should it go in a new class called: StudentFactory?

If it should go in a new class called: StudentFactory then should this class be in the ServiceLayer (which creates the Student object) or the BusinessLayer? Finally should I use IOC (Castle Windsor) to create the Student object? The answerer in this question suggests that you should not use an IOC for business layer objects: Rich vs Anemic Domain Model

Therefore to recap:

1) Should the factory method be created in the Student class or in its own class? 2) If the answer to 1 is "its own class", then should the class be located in the service layer or business layer? 3) Should the business layer object be IOC managed?

Was it helpful?

Solution

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

Definitely its own class. As you said, your Student class has a responsibility, and constructing instances of itself is not that responsibility.

That said, I don't normally write factories for business layer objects. I use an anaemic domain model, and these object are usually constructed by my data access object (DAO) from persistence. Your example of a factory seems like just the logic that would be in my DAO. It looks like a method of polymorphic tables in a RDBMS where type is stored as a column in the table. Something like this 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 would use the abstract factory pattern for instances of the DAO. Especially if multiple implementations may exist. E.g. in-memory dao for testing and PostgreSQL impl

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?

That really depends. Who is using the factory? Do you believe that the factory could and would be used by anybody using Student? If everybody is going to use this factory to construct instances of Student than I would put it in the same layer as Student. If it's a factory only conceivably used by a different layer, then put it there. These questions usually work themselves out as the code base matures because inevitably the class will end up in the lowest common denominator layer that all dependent layers can access.

3) Should the business layer object be IOC managed?

No. As I mentioned earlier, these objects are usually created by a DAO. IOC is a reasonable choice for managing your factories and DAOs.

OTHER TIPS

The very first line and code example outlines the real problem to be solved:

Say I have a Business class called person:

public class Student { }

You have a business class called "person" but the class is named "Student." You want to know how to return a concrete type based on the kind of degree they are going for. This is the problem to solve.

You need a Person class:

public class Person { }

And you need a Student class, which takes a Person and a Degree:

public class Student
{
    private Degree degree;
    private Person person;

    public Student(Person person, Degree degree)
    {
        this.person = person;
        this.degree = degree;
    }
}

The fact that a person is a student at a school is information about a person (an attribute). A person can be a student at multiple schools. In the U.S.A. students in high school can "dual enroll" at a local college, essentially making them a student at two academic institutions.

Just thinking of colleges, a person can be a former student as an Undergrad, and come back for the Graduate degree, and then their P.H.D.

So the kind of degree is also an attribute - not of the Person but of the Student.

Any behavior that is specific to a kind of degree should be pushed down into the Degree class. The Degree class is actually where you want to take advantage of polymorphism and inheritance:

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;
    }
}

Here we have an abstract method called CanRegisterForClass(...) and three concrete classes. Each degree checks the Level of the class to see if a Student can register for it:

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;
        }
    }
}

Whether or not a student can register for a class depends on the degree. This behavior is pushed down into a class that can handle it with out repetitious if statements.

Now you are at a point where you need a factory method, which can be as simple as a static method on the Degree class:

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");
        }
    }
}

The last question to answer is who should call this factory method. In this case, the part of your application responsible for creating Student objects is where this factory method should be invoked. If you are using an ORM like Entity Framework or NHibernate to map these objects then you should be able to configure the ORM to do this mapping for you based on values in a certain column of a table in the database.

With no ORM available, a service class can do this, or even a factory class for Student objects, which just delegates to the static method on the Degree class.

Licensed under: CC-BY-SA with attribution
scroll top