views:

161

answers:

3

What are the relative advantages / disadvantages of chaining classes together (or rather; using the results of one class to generate another), compared to nesting classes?

I'm trying to restructure my user / authentication system and am wondering whether;

  • myAuthClass should act as a utility and if log-in is successful, simply create a new myUserClass object
  • or whether the myAuthClass should create the myUserClass internally (ie $this->user = new myUserClass)
  • or even if myUserClass should just call myAuthClass when necessary (ie when a user tries to log in) and update its internal structure (new emails, favourites, cart, etc) as necessary.

As you can tell I'm a bit of an OOP n00b. Concpetually I seem to be able to make a case for each of the methods, so I'm interested in hearing from other people regarding the +ves/-ves of the various approaches.

Cheers.

+1  A: 

From an accademic perspective, all 3 options are incorrect in that they are programmed against concrete implementations and not against an abstract interface. As such they are directly coupled together, which limits reuse - you re-use them both or not at all.

You could generate IUserClass, or IAuthClass and implement this abstract interface into a concrete class and then I would go for a situation where the auth class implementation took an IUserClass when authenticating to populate, or the user class was given an implementation of IAuthClass to authenticate against.

In each scenario this allows the most flexability, in that the auth class can be reused and different versions of the UserClass can be generated, or the user class has the ability to use multiple different authentication mechanisms, as long as the inherit from IAuthClass.

The latter approach of giving the user class an authentication object (that implements IAuthClass) would be my preference, in that authentication mechanisms differ, even with a single application, whilst the user class would change less. But to be correct, neither should be based on a concrete implementation.

This can however be seen as over the top, so it's a judgement call.

Andrew
Very good points and the most relevant to my specific project, as future development will likely include an OpenID style authentication system.
MatW
There is nothing wrong with programming against concrete implementations if there are no external dependencies on them. That is, if the classes are only ever used within the same package -- the package is highly cohesive -- there's no fault to not having interfaces.
jeyoung
+1  A: 

First of all I suggest you to use the adapter pattern. You should have an abstract class that define the interface (the public methods) than you create a class that implements a particular type of authentication. For instance you can create a class/adapter for DB base authentication, one for LADP authentication.. etc...

The best thing instead of reinventing the wheel, you should use a ready made solution. I have used Zend_Auth, which implements the adapter pattern. By using Zend_Auth you can understand good OOP practicies, the adapter pattern, the use of interfaces and abstract classes, if you want to.

Here you can see how I have used Zend_Auth for an intranet;

        protected function Authentication() {
  $strEmail = trim($this->txtEmail->Text);
  $this->txtPassword->Text = trim($this->txtPassword->Text);
  // do the process of authentication, AD and then DB
  // Get a reference to the singleton instance of QAuth
  $auth = QAuth::getInstance();
  // Set up the authentication adapter
  $authAdapter = new QAuth_Adapter_WebService(__LOGIN_WS_URL__, 
   $strEmail, $this->txtPassword->Text
  );

  // Attempt authentication, saving the result
  $result = $auth->authenticate($authAdapter);

  if ($result->isValid()) {
   $objUser = User::LoadByEmail($strEmail);

   // if there is not a user's record create one
   if(!$objUser) {
    $this->User_Create($strEmail);
    $objUser = User::LoadByEmail($strEmail);
   }

   $crypt = new Encryption();
   $encr = $crypt->encrypt(__KEY__, $objUser->UserID);    
   $_SESSION['user_id'] = $encr;
   setcookie('user_id', $_SESSION['user_id'], time()+(3600*24*365*20));
   $this->Intranet1Integration($objUser->UserID);
   QApplication::Redirect('http://'.__URL__.'/index.php');  
  }
  else {
   QApplication::DisplayAlert(
    'Log on failed. You must provide a Company email and a correct password.'
   );
  }
 }
rtacconi
+1 for the adapter pattern and the reference to Zend; it's docs will make very useful case studies.
MatW
A: 

The main advantage of chaining classes is making them independant of one another, so modifying one has absolutely no (or alot less) effect on the other. For example, say you want to change the Authentification method, you won't need to change the user, and vice-versa.

Other small advantages come out for both methods, but this maintainability is the main one here

David Menard