Search code examples
phpumlassociationsclass-designaggregation

Defining the correct association relation between classes


In a module I'm currently developing I tried to model the requirements to a class diagram with correct relations.

The requirements:

  1. The module will connect to a set of ftp servers and download some files.
  2. The module will parse each file to generate a set of invoices.

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 ?


Solution

  • 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.