Reputation: 6752
I'm writing an ORM-like baseclass where I need the behaviour to change based on information in the subclass.
For example, given the class Person : OrmClass there would be functionality in OrmClass that could access data in the database table "person" using the static method Person.All(). My model here is Ruby's ActiveRecord class which I feel gives a really clean metaphor.
I've been looking at it different ways but can think of no clean way to implement this within the limits of object orientation. It's easy to do in instance methods (where the current class name is easily accessible). But All() "needs" to be a static method and then all supporting methods must follow to static-land.
I'd appreciate any advice.
Upvotes: 1
Views: 65
Reputation: 6975
Expanding my comment into an answer. What you've run into here is a demonstration of the limitations of both static methods and of the active record pattern. Static methods are limited because they can't really do polymorphism, and the active record pattern is because it forces a class to take responsibilities that it generally shouldn't have to. A Person
now has to be concerned not just with being a person, but also managing its own data access. As you've realised, this responsibility doesn't really belong to an instance of Person
, which is why you're stuck having to put some pretty heavyweight behaviour into statics.
One pattern that avoids these problems is the repository pattern. Here you have a PersonRepository
which handles all the data access, and leaves the Person
to just be a person. (You may also want to look into the related Unit of Work pattern).
The usual way of implementing this leaves the Person
completely ignorant of how it is persisted, so you wouldn't have any reference to a PersonRepository
inside a Person
. So to give a sketch:
public class PersonRepository
{
public IEnumerable<Person> All()
{
//Data access stuff here
}
//Other data access methods here
}
Then when you want your persons, you start with the repository. Often people will have a generic base repository like:
public class Repository<T>
{
public virtual IEnumerable<T> All()
{
//Data access stuff here
}
//Other data access methods here
}
Then you can just do PersonRepository : Repository<Person>
if you need to extend or override parts with person-specific elements.
I'm not exactly sure how you plan to do your data access, but potentially as a starting point you could make Repository
an abstract base class and use the template method pattern where you need subclass-specific information (like names or parameters required for executing the required sql query).
Entity framework is a popular example of this pattern, with DbContext
as its unit of work and DbSet<T>
as its generic repository. Even if you want to use your own implementation, you might look into Entity Framework as an example of ORM design
Upvotes: 1