问题
I have created a File class, which takes care of all operations on files, I/O, and which acts differently depending on the nature of the files. I'm not happy with its actual structure, which looks like this:
class File
{
function __construct($id)
{
$bbnq = sprintf("
SELECT *
FROM documents
WHERE id = %u",
$id);
$req = bbnf_query($bbnq);
$bbn = $req->fetch();
$this->file_type = $bbn['file_type'];
$this->file_name = $bbn['file_name'];
$this->title = $bbn['title'];
}
function display()
{
return '<a href="'.$this->file_name.'">'.$this->title.'</a>';
}
}
class Image extends File
{
function __construct($id)
{
global $bbng_imagick;
if ( $bbng_imagick )
$this->imagick = true;
parent::__construct($id);
}
function display()
{
return '<img src="'.$this->file_name.'" alt="'.$this->title.'" />';
}
}
Here I need first to know the file type in order to determine which class/subclass to use.
And I'd like to achieve the opposite, i.e. send an ID to my class, which returns an object corresponding to the file type.
I have recently updated to PHP 5.3, and I know there are some new features which could be of use for creating a "factory" (late static bindings?). My OOP knowledge is pretty light, so I wonder if some have structural suggestions in order to make a unique class which will call the right constructor.
Thanks!
回答1:
I don't think late static bindings is relevant here - a factory pattern doesn't require them. Try this:
class FileFactory
{
protected static function determineFileType($id)
{
// Replace these with your real file logic
$isImage = ($id>0 && $id%2);
$isFile = ($id>0 && !($id%2));
if ($isImage) return "Image";
elseif ($isFile) return "File";
throw new Exception("Unknown file type for #$id");
}
public static function getFile($id) {
$class = self::determineFileType($id);
return new $class($id);
}
}
// Examples usage(s)
for ($i=3; $i>=0; $i--) {
print_r(FileFactory::getFile($i));
}
As an aside, you should definitely escape your output from the DB, no matter how safe you think it is. Test with double quotes in a title, for example (let alone more malicious input).
Also if it's part of a project, you might want to separate the View layer (your HTML output) from this Model layer, ie implement MVC...
回答2:
In your factory's constructor, you need to determine the file type, then with that, create an object of the corresponding class. Something like this perhaps:
class File
{
public static function factory($id)
{
$fileData = <query this $id>
switch ($fileData->type) {
case image:
return new ImageFile($fileData);
break;
case html:
return new HtmlFile($fileData);
break;
default:
// error?
}
}
}
abstract class FileAbstract
{
// common file methods here
}
// override the custom bits for each type
class ImageFile extends FileAbstract
{
public function display()
{
// ...
}
}
class HtmlFile extends FileAbstract
{
public function display()
{
// ...
}
}
Your code would then simply be:
$myFile = File::factory($id);
$myFile->display();
来源:https://stackoverflow.com/questions/4907018/building-a-factory-in-php