In a module I'm currently developing I tried to model the requirements to a class diagram with correct relations.
So I created 4 classes File, Ftp & Parser. Now I thought I should make the File class be able to download()
and parse()
itself.
class File{
private $supplier;
private $status;
private $fileName;
private $fileId;
function __construct($supplier){
$this->supplier=$supplier;
}
function downloadFile(){
$ftp= new Ftp($this->supplier);
$this->fileName=$ftp->download();
}
function parseFile(){
$parser= new Parser($this); // The parse needs the fileName in addition to the supplier info to parse the file correctly.
$parser->parse();
}
function saveFileInfoToDB(){
//Save file Info to db.
}
}
So whenever I need to download a File I did the following:
class ServiceInvoker{
function downloadFilesFromFtpServers(){
foreach($suppliers as $supplier){
/*$supplier here is an object containing all data needed to download a file
from that certain supplier filled from the database
(i.e. ftpUsername, ftpPassword, ftpHost, SupplierName, supplierFileType).*/
try{
$file= new File($supplier);
$file->downloadFile()
$file->saveFileInfoToDB();
}catch(Exception $e){
//log error
}
}
}
}
You can clearly see that to download a file I have to go through multiple download calls in different objects.
I think the problem was mainly because I thought the a File class should be able to download itself and therefore I instantiated a Ftp object in the File class itself.
The same happens when I try to parse a file as I need to pass $this
to the parse to get the info of the file.
I think that the Ftp class should have been passed an object of the Supplier directly in the ServiceInvoker and make it return a File object if the process succeeded in downloading that file.
Now how should I have correctly identified the relation between classes Ftp Parser and File ? Should File contain a parser object or should the parser itself contain the File object and be called directly from the service invoker ? I can clearly identify the relation between the Parser and File classes as Association. The same between the Ftp and file classes but who should contain who ?
In my opinion you should reconsider the responsibilties of the file. A file can be downloaded, but it isn't a responsibility of a file.
What I mean is that the class File should outline the properties and the responsibilities of a File. The responsibility to download a file should be put in a DownloadManager or somehing equal.
Some great guidelines for desigining an application can be found in [SOLID design principle, origin of the principle can be found in this paper of Uncle Bob
It gives you five guidelines which gives you a design which should be loosely coupled and easier to maintain and re-use. There are many resources available about this topic.