The problem with Object Oriented Programming is exactly that, the shape and vehicle examples. What I'm going to type below is not the actual truth, it is an opinion. An opinion shared by more people than just me though :)
Object oriented vs "Class oriented"
Your example where there would be a catalog and an episode is perfectly well. The problem is the functions you propose these Objects should get. That is where you go from Object Oriented Programming to Class Oriented Programming. Just fitting a lot of methods concerning the same concept in an Object doesn't make programming object oriented. All you do is make some sort of container for methods.
The Episode Object should be just an Object that "models" the Episode. It has a title, content, author, that kind of stuff. In no way should this Episode have access to the database. It is not supposed to save itself or be able to load more instances of other episodes. The same goes for Catalog, it should not be able to retrieve its newest Episode itself. A catalog is a model, it has a title and some other stuff.
Splitting up responsibilities
Getting stuff from and saving stuff to a DataBase is not a models responsibility. So someone else has to do the job. A good start might be to create a CatalogRepository which could look like so:
CatalogRepository
{
/**
* @param int $id
* @return Catalog
*/
public function getCatalogById($id)
{
//Do a query, populate a new Catalog and return it.
//The constructor of Catalog should not be the one quering!
//It should receive data, nothing more
}
/**
* @param Catalog $Catalog
*/
public function saveCatalog(Catalog $Catalog)
{
//stuff....
}
/**
* @param string $name
* @return Catalog[]
*/
public function searchCatalogsByName($name)
{
//do a full text search or something?
}
}
The Episode would have something along the same lines where you could get all Episodes for a specific Catalog but also the five most popular Episodes:
class EpisodeRepository
{
/**
* @param Catalog $Catalog
* @return Episode[]
*/
public function getEpisodesByCatalog(Catalog $Catalog)
{
}
/**
* @param int $amount
* @return Episode[]
*/
public function getMostPopularEpisodes($amount)
{
}
}
There are some really good guidelines for writing good object oriented code, take a look at SOLID for instance
The principle reasons for not following the ItemContainer->getItems()
approach and returning an array are:
- The initial implementation is likely to just return a reference to the original array, which would then allow the caller to modify the underlying collection. This is considered bad practice.
- The next work around is then to make a copy of the array and return that instead. This is bad because a caller may think they are still modifying the underlying collection, and it is also expensive for large collections.
If the caller really wants to have a copy of the array, then itemContainer->cloneItems()
is much clearer, and less likely to be used inappropriately.
If the caller is just wanting to have a single item, then providing a
itemContainer->getItem(index && key)
is clear and efficient
If the caller is wanting to iterate over the items, then providing a
itemContainer->getItemIterator()
is clearer. Depending on the language you may implement
itemContainer->VisitItems(visitor)
where visitor is a visitor class, delegate or function pointer.
Hopefully, I've given you some ideas on how this can be approached differently
Best Answer
There are a few different approaches you can use to do this, depending on the data set, performance needs, and your general preference.
If there's an object that is used almost exclusively for display, you would probably want to query the dataset beforehand in some sort of Factory and then create the object by passing the data into the constructor.
If there's an object that touches a bunch of data sources and is used for different things, loading all of the data at instantiation might take a long time, and all of the object's data might not be needed every time that object is instantiated. You can leverage lazy loading to accomplish this goal.
If performance is a major concern, and the object stores a ton of data but is mostly immutable, you can use something like the Flyweight pattern, which allows you to actually treat a single object like a collection.
I highly recommend you try a few different ways of doing things. For a project such as yours, how you choose to instantiate and populate the object will probably matter little, but it will be a good and fun exercise in different object instantiation patterns.
As a final note, much of the knowledge you're gaining now about OOP can be applied to nearly any language you learn in the future.