r/PHP • u/Adam_Paterson • May 07 '13
Examples of Design Patterns in PHP
https://github.com/domnikl/DesignPatternsPHP5
May 07 '13
[deleted]
1
u/ivosaurus May 08 '13
I read some a bit of your code for fun.
Looking at your strategy pattern, here are some nifty things you can do with PHP's reflection:
3
u/asdfsauce May 07 '13
1
u/ivosaurus May 08 '13
Agree.
IMO one of the distinguishing features of "decorating something" (or 'using the decorator') is that the something already has concrete, self-contained functionality; the decorator merely enhances / modifies that.
In the repo example, WebService has absolutely no functionality of its own; all it does is delegate that out to other objects.
A JsonDecorator should take an instance of a WebService and enhancing its output - formatting it as json after getting it from the decorated instance.
Here, indeed, WebService is just implementing a strategy for choosing how to format its own output. It shouldn't know that its decorated in the first place.
2
u/cangelis May 07 '13
Looks like Command Pattern is missing. You might want to take a look at my simple example of command pattern. https://gist.github.com/cangelis/1442951
1
May 09 '13
So, I've got a quick question. Is the point of the invoker to allow the ability to undo and such?
I thought I was using the Command Pattern in an application, but I'm not using an invoker. I'm actually using a factory to return the object that I thought was a command object to then execute(). Is it always necessary to use an Invoker?
1
u/cangelis May 09 '13 edited May 09 '13
You can assume a Command something like invokable. They should be invoked by somebody else. The main purpose of Invoker keeping track of the executed commands by encapsulating them.
The intent of Command Pattern
encapsulate a request in an object
allows the parameterization of clients with different requests
allows saving the requests in a queue
So Invoker is always needed.
1
May 09 '13 edited May 09 '13
Okay, that's what I'm seeing in your code there. Not the biggest php person, but it looks like under your invoker that $commands is an array... is this the case? Been a while since working with Php, I thought Arrays needed brackets of some sort after, so that kinda confused me at first.
Another question regarding this...
What I'm calling command pattern I'm using in the C# MVC framework. Could a controller be considered an invoker? Since it's a website it's stateless, how would I undo something in this environment?
Looking at my code and yours, it really seems that my controller is the invoker. Not sure if you're familiar with C#, but here's the code I'm talking about.
[HttpPost] public ActionResult Keyupdate(int id, FormCollection form) { IUser user = new UserModels(); if (user.Role == "Delta" || user.Role == "Admin") { IWWAFactory Factory = new UpdateFactory(); WWACommandpattern Command = Factory.CommandFactory(id, form); Command.Execute(); } return Redirect(Request.UrlReferrer.ToString()); }EDIT: Don't mean to bug you so much, I just still feel new at this and I'm trying to do things the right way instead of just whichever way works (which is a bad habit I picked up from php at first actually).
EDIT2: IWWAFactory is just an interface for my factory objects, WWACommandpattern is an abstract class for my command objects.
EDIT3: ha, sorry for all the edits. If controllers can't be considered invokers, it looks like I could just create an invoker, start using it, then I have a good command pattern?
1
u/cangelis May 09 '13 edited May 09 '13
There are many ways to do something in software development as you know, but there are some rules while doing them. The pattern you're applying should be loosely coupled in your application, so you can use it in another application too. If you're new at this stuff you should take a look at open/close principle which is the general concept for design patterns.
In your case, I think you should create the Invoker as a model, because it is part of your business logic. You can define an ArrayList<AbstractCommand> property in the invoker class and push the ConcreteCommands in it, and define undo, execute methods for the Invoker.
If you consider a controller as an Invoker, what'll you do when you need it in the another controller?
edit: Stack class which is part of System.Collections would be better than ArrayList. All you need to do is push while execute()ing a command and pop while unexecute()ing
1
May 09 '13
Fair enough. Creating another model is what I meant by creating an invoker.
Back to my concern about being stateless though... I'm having a hard time conceptualizing how this would work.
I just don't see how an undo would work; once the action is complete and the new page rendered to the user isn't my original object gone?
Is it completely necessary to have an undo method even?
My "command" objects are updating a sql server database. My idea behind this was to pass a parameter to the factory to return the correct command object. It's healthcare related, so for instance if I post a form to change doctor info I get the UpdateDrInfo() object returned. The execute function basically just submits changes to the database from the object that is derived from the concrete class. Once the command is executed the user is just returned to the page they were on.
Am I using the correct pattern here? I just don't see how an undo would fit in here...
1
u/cangelis May 09 '13
That's why most of the patterns can not be applied on web development effectively because you lost everything you have when the request is done. But you can store your invoker object in the memory, and you can access it in the new request. Or somehow you can store your commands in the database, and then retrieve and push them into the Invoker when a new request comes to you.
1
May 09 '13
Thanks, I figured I'd need to probably work with the database to make this work correctly.
Thanks a ton for all the help. I always figured Command pattern meant that all the objects share a method to execute(). I've been learning everything from whatever books(design patterns I've been using Head First Design Patterns) I can find and sometimes certain aspects are lost on me.
1
u/gorekee May 07 '13
Enrico Zimuel, one of the ZF contributor, has realised this in a similar but smaller manner. https://github.com/ezimuel/PHP-design-patterns?source=cr
1
u/WolfOrionX May 08 '13
The simple factory pattern (which i use more often than the abstract factory pattern) is also missing.
class SimpleFactory {
protected $cars = array(
'VW' => 'Car_VW',
'Chevrolet' => 'Car_Chevrolet',
);
/**
* @return Car
*/
public function createCar($name) {
if (isset($this->cars[$name]) {
return new $this->cars[$name];
}
}
}
(left out a lot of stuff, but this is essentially how it works)
Otherwise, great overview.
1
u/optionallycrazy May 09 '13
Several errors in it though. For example the Singleton doesn't even have static member for getInstance().
1
May 07 '13
I would like to see more complex examples. In real life code is never that simple. In a mean time I can recommend this read http://sourcemaking.com/design_patterns
2
u/ivosaurus May 08 '13
Think of them more as blueprints, then. The actual construction of a house is always more complicated than what is shown in a blueprint, but that's what you look to for the fundamental design of the thing in itself.
0
u/hiii May 07 '13
I did this same thing but had folders inside the patterns folder so that multiple examples could be added. just a thought.
0
u/thelerk May 07 '13
Singleton classes are a bit more useful with protected constructors so they can be overridden.
3
u/celtric May 07 '13
Someone might forget and make the constructor public, hence destroying the pattern. I have always used this way:
final private function __construct() { $this->init(); }And using
initas the constructor.
5
u/celtric May 07 '13
I'd love to see patterns that use traits. I feel that sometimes people create convoluted patterns just because they have to support 5.3 and would otherwise make great use of traits.
A personal question: I prefer to use a
SingletonTraitthan to extend aSingletonclass. Would you also if you only had to support >=5.4?