This is more of a long form commentary as it explains the origin of your dilemma, but does not provide any solutions.
OOP Lesson 1: Just because you use classes does not mean that you write object-oriented code.
Your facilities rarely use a good precedent for public property. Consider the OP example:
class Gallery { public $galleryID; public $galleryName;
By defining our properties as public , how do the following two code fragments differ?
$gallery = new Gallery; $gallery->galleryId = 42; $gallery->galleryName = 'some name'; // vs: $gallery = array( 'galleryId' => 42, 'galleryName' => 'some name' );
If you said, "they are not at all completely different," then you will be right. In fact, object code will be slower due to creation overhead associated with new . There are other factors, such as the ability to pass references to an object instead of copying a new array, but this does not affect this particular situation.
OOP Lesson 2: Objects Are a Black Box
The problem with creating an object that is nothing more than a set of mutable properties is that the rest of your code has a complete picture of what is going on inside that object. Tell us why this is bad ...
People are just not very good when it comes to complexity. Good software aims to minimize complexity by encapsulating functionality in separate blocks. In this case, we want to encapsulate the entire logic of the gallery object in the Gallery class. This makes sense in a domain-based approach (DDD). What we want to do is the Gallery wall from the outside world; we want its internal implementation to be opaque to the rest of our code. The rest of our application does not need to know or care about how Gallery functions, just how it works. An added benefit here is that we can focus on making the gallery work as it should, and then forget about it. We don’t have to remember how Gallery works with Image or Revision . This loose connection is one of the most powerful tools in OO design.
While this can work on very small scales, it is not possible to simultaneously store the logic of an entire application in your head. No matter how smart you are, our brains just don't have enough RAM.
Returning to the code, if our application code knows how Gallery takes its name, we already assumed that the gallery-ness logic had leaked to the rest of the program. What happens when we decide that we want to check new gallery names when they are assigned? Now we have to put this validation logic everywhere in our code, where we indicated the names of the galleries, because we were not shy about the whole abstract concept of “gallery”. A much better design would be to encapsulate the assignment of Gallery properties within the object itself:
class Gallery { private $galleryId; private $name; public function setName($name) { $this->name = $name; } public function getName($name) { return $this->name; } }
If we structure our class this way, we will always have one entry point when we need to assign a name to the gallery. Now that our gallery requirements are changing along the way ( and they will ), all of our application code, which is blind to the gallery naming logic, is isolated from breakage. We simply add a new method to our set of names and create minimal upheaval in our program:
class Gallery { private $galleryId; private $name; public function setName($name) { $this->validateName($name); $this->name = $name; } private function validateName($name) { if (!preg_match('/^[az]+$/', $name)) { throw new Exception; } } public function getName($name) { return $this->name; } }
OP Addressing
To answer the question of how to represent an encapsulated Revision object as a property of a higher-level Gallery instance, we need a little context. It seems that what the OP is trying to do is model the entities of the domain that will be written to and retrieved from the level of ongoing support (for example, a database, a flat text file, etc.).
Anemic domain models are one way to handle this, however it is usually considered an anti-pattern. Martin Fowler writes:
The main symptom of the anemic domain model is that with the first blush it looks like the real thing. There are objects, many of which are named in nouns in the domain space, and these objects are associated with the rich relationships and structure that real domain models have. The catch comes when you look at the behavior, and you understand that there is practically no behavior on these objects, making them a little larger than bags with getters and setters. In fact, often these models come with design rules that say that you should not introduce any domain logic into domain objects. Instead, there is a set of service objects that capture the entire domain logic. These services live on top of the domain model and use the domain model for data.
Given these arguments, you should use something like DataMapper or Gateway to work with domain objects that need to be stored in some form of backend memory.
Alternatives
Forget about the Revision object for a minute and imagine that we want to use the Slideshow object to display images from the gallery. This class might look like this:
class Slideshow { private $gallery; public function __construct(Gallery $gallery) { $this->gallery = $gallery; } public function play() {
Ignore the fact that the PHP code will not actually be used to “play” the slide show, like what will happen in the client code. The important thing is that Slideshow uses Composition to access Gallery . This design is significantly superior directly to the new ing a Gallery inside Slideshow , because:
Now Slideshow connected - we can insert any object that follows the concepts of "gallery-ness" (usually Gallery will be declared in accordance with the specified interface contract).
Now Slideshow can be checked. How do we handle a situation in which Gallery does not have the appropriate image types? If we directly create a Gallery instance inside a Slideshow , we cannot imitate such conditions. By introducing Slideshow dependencies, we allow us to test the ability of the code to handle various working conditions.
Of course, sometimes he needs to directly create an instance of an object inside another class. For more information on this topic, I would advise Mishko Hevery in my article "New" or not "new . "