Is this a legitimate use of the Abstract Factory pattern?

design-patterns, php

Solution

It's a good implementation but it can be finetuned a bit if you use interfaces.

An abtract class with all virtual methods it's just a interface so don't use abstract classes, use interfaces.

interface IDataFileFactory{
    public function createFile($id);
}

class FileAFactory implements IDataFileFactory
class FileBFactory implements IDataFileFactory

If you find repeating code in `FileAFactory` and in `FileBFactory` methods then it is time to refactor your classes and create inheritance.

interface IDataFileFactory{
    public function createFile($id);
}

abstract class BaseFileFactory implements IDataFileFactory {

//some methods implementation with common features to avoid repeating code
//some abstract methods to be implemented for A and B FileFactories
//absolute abstract base class has no sense because in php you can use interfaces.
//...
}

class FileAFactory extends BaseFileFactory
class FileBFactory extends BaseFileFactory

Then use throug interface:

  public function readFiles(IDataFileFactory $factory){
           //create a file using factory
           return IDataFile; //return Interface implemented by all DataFile types.
        }

You can do the same thing with `DataFile` base class and so on.

I also recomend to not pass factories in parameters because a factory is out of context. Try to do not mix architecture implementation with data and info process workflow. You can create a container, in a scope accesible for your other classes, to resolve the factory.

The container can, for example, read configuration files to create concrete factory in application bootstrap; read from some value, choosed by the user in previous steps of the user case, stored in a class instance or accepting a parameter in runtime to resolve the factory. It's about implement some kind of simple dependency inyection.

Anyway, this is only my point of view and can be a hurge amount disagreements.

I hope it help.

Problem

Here's what I'm trying to implement in my program: - The program should open a zip file, which contains many data files - The format of the data files can differ between zip files (e.g. csv, tab delimited, or could even be some kind of binary file which needs decoding) - However, within a zip file all data files will be of the same type I have been reading "Design Patterns" by Gamma et al., and have been looking at the Abstract Factory pattern to try to solve this. Ideally, I want to have one class for the Zip file, which can read any type of data file within it. I guess I would have two classes - FileTypeA and FileTypeB, which could process different formats of data (although there could be more in the future). I would like a way of telling my ZipFile class which type of file to use when reading the data. So far, this is what I have come up with: ``` <?php /** * An abstract factory used for creating data files of any type */ abstract class DataFileFactory{ abstract function createFile($id); } /** * A factory for creating and setting up a data file of type 'A' */ class FileAFactory extends DataFileFactory{ public function createFile($id){ $file = new FileA(); $file->setSampleId($id); return $file; } } /** * A factory for creating and setting up a data file of type 'B' */ class FileBFactory extends DataFileFactory{ public function createFile($id){ $file = new FileB(); $file->setSampleId($id); return $file; } } /** * An abstract class which defines some functionality of a data file */ abstract class DataFile{ abstract function readData(); abstract function setSampleId(); } /** * Concrete class that processes a data file of type 'A' */ class FileA extends DataFile{ public function readData(){ echo "Reading data from a file A<br/>"; } public function setSampleId(){ echo "Setting sample id of a file A<br/>"; } } /** * Concrete class that processes a data file of type 'B' */ class FileB extends DataFile{ public function readData(){ echo "Reading data from a file B<br/>"; } public function setSampleId(){ echo "Setting sample id of a file B<br/>"; } } /** * Concrete class that reads a zip file and reads each file within the zip */ class ZipFile{ private $files = array("file1.txt","file2.txt","file3.txt","file4.txt");//this would be an array read from the zip file private $sampleId = 1;//this would be derived from some other function /** * Read all the files in a zip archive. * $factory can be an instance of any class that extends DataFileFactory, and is used for creating each file */ public function readFiles(DataFileFactory $factory){ foreach($this->files as $fileName){//loop through each file in the zip $file = $factory->createFile($this->sampleId);//use the factory to create the desired file $file->readData();//now read the data from the file! echo "object created of type: ".get_class($file)."<hr/>"; } } } /*********************************************************************************************** * IMPLEMENTATION ***********************************************************************************************/ $zip = new ZipFile();//create a new zip file $factory = new FileAFactory();//instantiate a new factory, depending on which type of file you want to create $zip->readFiles($factory);//read the files, passing the correct factory object to it ``` Can anyone tell me: (A) Whether this is a good way of achieving what I'm looking for, or is there some simpler way of doing it? (B) Is this actually the Abstract Factory pattern, or have I completely misunderstood? Thanks in advance!

Original source