Not for 'how-to' coding questions but PHP theory instead, this forum is here for those of us who wish to learn about design aspects of programming with PHP.
A well known design smell is when you've got too many parameters in a function. The usual solution is to put the parameters in a parameter object and pass them to the function. You can also defer setting parameters to later function calls.
Well... I'm using the latter case and it doesn't look very pretty to me. Example:
HTMLPurifier_ConfigDef::define(
'Core', 'Encoding', 'UTF-8',
'Defines the input and output character encodings to use. HTMLPurifier '.
'internally uses UTF-8, making that the painless default choice. Note '.
'certain implementations of HTMLPurifier_Lexer are intelligent enough '.
'automatically detect encoding, however, output format will always be '.
'this value. Currently supported values are UTF-8 and '.
'ISO-8859-1 (ISO8859-1). Encoding names are case sensitive.'
);
HTMLPurifier_ConfigDef::defineAllowedValues(
'Core', 'Encoding', array('UTF-8', 'ISO-8859-1')
);
HTMLPurifier_ConfigDef::defineValueAlias(
'Core', 'Encoding', 'ISO8859-1', 'ISO-8859-1');
Those extra parameters, however, are optional and aren't used all the time. What do you think would be a nicer/prettier way to do this sort of thing?
Ambush Commander wrote:A well known design smell is when you've got too many parameters in a function. The usual solution is to put the parameters in a parameter object and pass them to the function. You can also defer setting parameters to later function calls.
The usual solution is to first see if having many parameters is actually a problem. It it actually is a problem there are a number of things you would probably do before resorting to a Parameter Object -- which is more for parameters that you don't have a home for. I would suggest for reducing the parameters to only those actually needed adn/or replacing a parameter with a method as first steps.
As of right now, having too many parameters is not too much trouble. The difficulties arise when I start adding more constraints: besides allowed values and value aliases, there could also be allowed types, case sensitivity, allowed setters, etc etc. But then again, eight still isn't that much...
I believe these parameters arise from configuration directives in your library? In which case using a parameter object seems like a good idea to me.
I've done similar things myself, though where things may have been intended to be ignored or used in different ways (like your array() to group directives) I may have had simple sub-objects whereby "instanceof" could be used to figure out what do do with the values. I was just making it up as I went along however.
HTMLPurifier_ConfigDef::defineAllowedValues(
'Core', 'Encoding', DefGroup('UTF-8', 'ISO-8859-1'),
Comment('This is an instance of "new Comment()" so it can just be ignored.')
);
I understand why you are wanting to use such objects though. How will you be retreiving values from the object? Do you have a helper object to do this or have you built methods into the parameter object class anyway?
The trouble is that I must maintain namespacing, meaning all the parameter objects must have HTMLPurifier_ prefixed.
How will you be retreiving values from the object?
When you create a configuration object, it must be passed a definition object. It then references the values in there to determine whether or not config options are okay.
Do you have a helper object to do this or have you built methods into the parameter object class anyway?
Ambush Commander wrote:The difficulties arise when I start adding more constraints: besides allowed values and value aliases, there could also be allowed types, case sensitivity, allowed setters, etc etc. But then again, eight still isn't that much...
If you are going to have a lot of constraints it makes sense that you will need a lot of code to declare them all and its not duplication so its not really a problem.
What you are doing here seems really cool to me, a very elegant solution, I don't really understand what you are concerned about.
ole wrote:What you are doing here seems really cool to me, a very elegant solution, I don't really understand what you are concerned about.
I think it's mostly a combination of aesthetics and user-friendliness. With the level of flexibility to you're aiming at through, I agree that this is an elegant solution.
ole wrote:What you are doing here seems really cool to me, a very elegant solution, I don't really understand what you are concerned about.
I think it's mostly a combination of aesthetics and user-friendliness. With the level of flexibility to you're aiming at through, I agree that this is an elegant solution.
Again...this is an example of when computer science becomes a computer art
HTMLPurifier_ConfigDef::define(
'Core', 'Encoding', 'utf-8', 'istring',
'Defines the input and output character encodings to use. HTMLPurifier '.
'internally uses UTF-8, making that the painless default choice. Note '.
'certain implementations of HTMLPurifier_Lexer are intelligent enough '.
'automatically detect encoding, however, output format will always be '.
'this value.'
);
HTMLPurifier_ConfigDef::defineAllowedValues(
'Core', 'Encoding', array(
'utf-8',
'iso-8859-1'
)
);
HTMLPurifier_ConfigDef::defineValueAliases(
'Core', 'Encoding', array(
'iso8859-1' => 'iso-8859-1'
)
);
We've got an extra type variable, in this case it's istring, or case-insensitive string.