Neuromancer2 wrote:I would like to add functionalities to my models in a flexible manner and so that I would be able to reuse them (publication, trash bin, translation...). I already have a event system based on the observer pattern which allow me to attach listeners to an object. It's quite useful but I need the ability to add new operations to existing object. I am wondering what kind of approach/pattern would be more suitable.
Languages that allow that are called prototypical based inheritance. That is possible with anonymous functions and __call()
However, I have a strong hunch this is not what you want. What you actually want is to learn about component based re-use, or
decomposition. PCspectra's suggestion to use the decorator pattern is one example of a component based re-use rather than inheritance. Maybe overkill. Learn the basic fundamentals before trying to perform any "tricks"
You see, inheritance is often turned to as the default way to re-use code, which is wrong.
In this example I would have a Status class, each model would hold an instance of it's status object (an object is the instantiated "thing" with state, the class is skeleton, the class definition you write. The object is the variable after you instantiate it).
In this way it doesn't matter if you had a $page or an $customer, or a $user, you could always do
$user->getStatus()->isDeleted().
Even though the user (model)
class has nothing about deletions and publishing, it's status
object does know about that stuff, and it can simply "ask" its status object about the status.
basically, instead of each object somehow "inheriting" functionality, that functionality is moved to a secondary object (which keep in mind objects are
behavior ) and is "injected" to your models that need to re-use that behavior, like a plug-in. for example thru the constructor
Code: Select all
function __construct( Status $status )
{
$this->status = $status;
}
Each class that needs this "delete", "publish" functionality will simply accept it's "status" thru the constructor. The status object in this example encapsulates the behavior (publish, unpublish) and data or state (current publishing status, etc..)
This is object oriented programming basics. Re-use thru inheritance vs decomposition.
Inheritance vs. decomposition
Since inheritance is so powerful, it is often overused. Frequently a class is made a subclass of another when it should have had an instance variable of that class as a component. For example, some object-oriented user-interface systems make windows be a subclass of Rectangle, since they are rectangular in shape. However, it makes more sense to make the rectangle be an instance variable of the window. Windows are not necessarily rectangular, rectangles are better thought of as geometric values whose state cannot be changed, and operations like moving make more sense on a window than on a rectangle.
Behavior can be easier to reuse as a component than by inheriting it. There are at least two good examples of this in Smalltalk-80. The first is that a parser inherits the behavior of the lexical analyzer instead of having it as a component. This caused problems when we wanted to place a filter between the lexical analyzer and the parser without changing the standard compiler. The second example is that scrolling is an inherited characteristic, so it is difficult to convert a class with vertical scrolling into one with no scrolling or with both horizontal and vertical scrolling. While multiple inheritance might solve this problem, it has problems of its own. Moreover, this problem is easy to solve by making scrollbars be components of objects that need to be scrolled.
Most object-oriented applications have many kinds of hierarchies. In addition to class inheritance hierarchies, they usually have instance hierarchies made up of regular objects. For example, a user-interface in Smalltalk consists of a tree of views, with each subview being a child of its superview. Each component is an instance of a subclass of View, but the root of the tree of views is an instance of StandardSystemView. As another example, the Smalltalk compiler produces parse trees that are hierarchies of parse nodes. Although each node is an instance of a subclass of ParseNode, the root of the parse tree is an instance of MethodNode, which is a particular subclass. Thus, while View and ParseNode are the abstract classes at the top of the class hierarchy, the objects at the top of the instance hierarchy are instances of StandardSystemView and MethodNode.
This distinction seems to confuse many new Smalltalk programmers. There is often a phase when a student tries to make the class of the node at the top of the instance hierarchy be at the top of the class hierarchy. Once the disease is diagnosed, it can be easily cured by explaining the differences between the instance and class hierarchies.
http://www.laputan.org/drc/drc.html
If you think about it objects in the real world work the same way. When the headlight on your car gets busted, you simply order a new "headlight component" off ebay and drop it in in the old one's place.
Think of class inheritance as if the car manufacturer had welded the original headlight on, so when the headlight got busted you had to throw out the whole car and get a new one. Seems like overkill.
Another example, you think of a house as an assembly of a roof, windows, doors etc..
But if you take any one of those components, it in turn has more components (roof has shingles, crawlspace, etc...)
You can keep decomposing the system more and more. and at a high level you can think of it in high level terms. That is the other benefit to decomposition, you can think of things in terms of doors and roofs, and not the individual nuts & bolts of the system.
In your CMS example a programmer not worried about status need not read over code that deals with publishing & publishing, and all those other concerns, they just see the status object and can skip over that part more easily if thats not the part theyre looking for.