简体   繁体   中英

Static reference of a class object

I have written several classes that all need to be instantiated when used.

My goal was to be able to have the perfect organization when trying to create dynamic access between instantiated classes.

In one class it would have several methods it calls based on what it just did. Inside one of these methods would be methods from other classes. (Currently static methods) I would edit these lines to control the behavior between classes.

What i mean by being dynamic is, if one of the static methods is not available or will be available later, then it simply executes a different method to complete the task internally.

My question is a matter of concern of proper OOP. I am not sure if there is a better way or if what im doing can cause problems. Although, it works pretty well.

I have an example prepared below. Of which, is a representation, not actual situation. executingFileOne is one situation and ExecutingFileTwo is another.

executingFileOne.php

require_once 'login.php';
$login = new Login();

require_once 'serverData.php';
$servDat = new serverData();

//blah blah blah

executingFileTwo.php

require_once 'serverData.php';
$servDat = new serverData();

//blah blah blah

login.php

class Login {

    private static $thisClass = null;

    public function __construct(){
        //Get and set user data.
        self::setStaticClass($this);
    }

    public static function setStaticClass(&$inputClass){
        self::$thisClass = $inputClass;
    }

    //Methods Methods Methods

    //---Methods Executed by class at specific times---------------
    // - - - Used to get input from other classes

    //Methods Methods Methods

    //-------------------------------------------------------------

    //---Methods for other classes.--------------------------------
    // - - - Used to output data to other classes
    public static function getUserID(){
        die('Test'.self::$thisClass->userID);
    }

    //Methods Methods Methods of other classes.

    //-------------------------------------------------------------
}

serverData.php

class serverData {

    private static $thisClass = null;

    public function __construct(){
        //work work work
        //an IF statement detected needing to register userID on serverData
            $this->registerOnServer();
        //work work work
        self::setStaticClass($this);
    }

    //Methods Methods Methods

    public function registerOnServer(){
        //work work work
        $this->userID = $this->getUserID();
        //add userID to registration data.
    }

    //---Methods Executed by class at specific times---------------
    // - - - Used to get input from other classes
    private function getUserID(){
        if (class_exists('Login')) {
            return Login::getUserID();
        } else {
            return 0;
        }
    }
    //-------------------------------------------------------------

    //---Methods for other classes.--------------------------------
    // - - - Used to output data to other classes

    //Methods Methods Methods of other classes.

    //-------------------------------------------------------------
}

This is one of the several issues when using static classes, and it is compounded due to the sneaky self-registration and strong coupling at the usage-site. Currently the "best" solution I know of to this problem in general, and one which I encourage, to this issue is to use Dependency Injection.

Further, using an IoC (Inversion of Control) container on top of DI can making wiring everything together and managing lifetimes a sane process. (Do not revert to a Service Locator pattern except in very specific cases or the beautiy of DI+IoC is lost!)

While an informal duck-typed "interface" is sufficient, using codified interfaces helps with classification - and are often (but not always) required for IoC containers to be able to register and resolve components.

// This is the service (interface) that different components will provider
inteface ILogin {
   public function getUserID ();
}

// Primary component (implementation) for the login service (interface)
class Login implements ILogin {
   // Note:
   // Constructor does NOT "register itself statically"; even when using
   // an approach similar to the original, use a proper Singleton Design.

   public function getUserID () { /* .. */ }
}

// Alternative/mock component (implementation) for the login service (interface)
class NoLogin implements ILogin {
   public function getUserID () { return 0; }
}

// Class (possibly also a component) that uses the login service dependency
class ServerData {
    // Constructor-based DI; the components are supplied as arguments
    // (There is also property-based DI.)
    public function __constructor(ILogin $login) {
        $this->login = $login;
    }

    // Later on we use the service
    // (It is a bit silly just to proxy the service, which should be
    //  injected elsewhere as required, but this mirrors the original.)
    public function getUserID () {
        // If not using a "mock service", then guard $this->login for null
        // because it is now an optional dependency; the appropriate approach
        // will vary based upon specific use-case.
        return $this->login->getUserID();
    }
}

Then later on:

// File 1-
// Note how dependency is "injected" into the server data
$servDat = new ServerData(new Login());

// File 2-
// A different dependency providing the same contract/service is used
$servDat = new ServerData(new NoLogin());
// Or, if ServerData guards usage and the service is "optional"
// $servDat = new ServerData(null);

So that is one form of DI (although general term is IoC, of which DI is an implementation) at the core - simple, but not very exciting and it can get tedious. Also, even though dependencies have been moved "outside" the immediate usage-site, they are still effectively hard-code at the consumer creating the instance and manually injecting the components.

Now, where does the magic with DI+IoC fit in? Components are registered into an IoC container and the IoC container is used to instantiate the objects - such a container is smart enough to automatically resolve DI dependencies. Then the only difference between File#1 and File#2 is that they use a slightly different IoC configuration (one uses Login and the other NoLogin for the ILogin service).

Other useful feature of IoC is the ability to configure object lifetimes. In this case the Login component (fulfilling the login service) might be instance-per-request which is effectively "singleton behavior" - except that it can't leak between requests and the Login component is now loosely-coupled with the ServerData class.

There is a lot more to an IoC container - see PHP DI which seems to have a fairly good introduction. Note that the same DI-ready code presented above could be used (in PHP-DI) for the component resolution to occur. (Bugs are free.)

A friend of mine showed me an option that is a better approach than mine. Probably not the right solution, but better.

Seeing as i instantiate my classes and can simply access the within other classes by calling the variable, globally. Instead of creating anything static.

For example:

require_once 'login.php';
$login = new Login();

require_once 'serverData.php';
$servDat = new serverData();

//blah blah blah


class Login {

    public function __construct(){
        //Get and set user data.
    }

    //Methods Methods Methods

    //---Methods Executed by class at specific times---------------
    // - - - Used to get input from other classes

    //Methods Methods Methods

    //-------------------------------------------------------------

    //---Methods for other classes.--------------------------------
    // - - - Used to output data to other classes
    public static function getUserID(){
        die('Test'.$this->userID);
    }
    //-------------------------------------------------------------
}

class serverData {

    public function __construct(){
        //work work work
        //an IF statement detected needing to register userID on serverData
            $this->registerOnServer();
        //work work work
    }

    //Methods Methods Methods

    public function registerOnServer(){
        //work work work
        $this->userID = $this->getUserID();
        //add userID to registration data.
    }

    //---Methods Executed by class at specific times---------------
    // - - - Used to get input from other classes
    private function getUserID(){
        global $login;
        if ($login != null) {
            return $login->getUserID();
        } else {
            return 0;
        }
    }
    //-------------------------------------------------------------

    //---Methods for other classes.--------------------------------
    // - - - Used to output data to other classes

    //Methods Methods Methods

    //-------------------------------------------------------------
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM