Adapter Template with Static Classes

I am looking for a good way to implement an adapter template with static classes in PHP 5.x.

One example where I would like to use this is as a Python counterpart to os.path.join() . I would have two adapted ones, a class on Windows and a Linux adapttee.

I think it makes sense to implement these classes as static classes because they have no "context". They don’t need to save any state and create an instance every time I need it, it seems redundant - so I'm looking for a clean way to implement this.

Consider the following dummy implementation:

  static public function join(){ $parts = func_get_args(); $joined = array(MY_MAGICALLY_PASSED_DEFAULT_PATH_PREFIX); foreach($parts as $part){ $part = self::$adaptee->cleanPath($path); if(self::$adaptee->isAbsolute($part)){ $joined = array($part); } else{ $joined[] = $part; } } return implode(PATH_SEPARATOR, $joined); } 

The first thing you notice is that it assumes an initialized static member called adapttee, which will contain the necessary OS-specific implementation details.

This requires that I have an arbitrarily named static constructor-like function, which I would call right after the class declaration. (Another thing that worries me is this approach).

Of course, I could initialize the local $adaptee variable with every method call, but this seems out of place, and I would have to replicate this in a static function that needs adapt.

Now ... to detail the implementation of the PHP class: they are not first-class objects, so I could not just pass the class as an argument. In this example, I need to create Adaptees as non-static (what is the term for these?) Classes, and then instantiate and ultimately assign it to the static variable $adaptee of the Adapter class.

Perhaps this is just this strange and completely subjective thought that I have ... but I really feel that it is not appropriate to do so. Do you have any ideas for a better implementation?

Another idea I used is to save the adapttee class name instead and use call_user_func , but I don't feel too comfortable using this approach.

Update

Perhaps I did not describe it correctly, so I will try to explain this in the update:

I'm not looking for how to get the base operating system, but I would like to have a neat way, since the static class will act differently depending on whether Linux, Windows, FreeBSD or something else.

I was thinking of an adapter template, but since I don't have a static constructor, I cannot initialize the class. One way is to initialize it at the beginning of each call to the public static method (or just check if it is initialized).

Another possibility is to create a static constructor method and simply call it immediately after the declaration. This may do the trick, but I'm just wondering what other, perhaps more elven methods exist to achieve this.

As for my initial example: This is supposed to be a utility function, it doesn't need to save state in any way, so I'm not looking for a Path-Object of any kind. I would like it to be a Path factory function that returns a string, without having to distinguish between different OSes each time it is called. "Library" -thing prompted me to create a static class as a pseudo-space for my related utility functions and various implementation details that the adapter template should support. Now I am looking for an elegant way to combine the two.

+4
source share
2 answers

You shoot your foot when you become static. You cannot introduce static classes, so you will always have a connection with the global scope, and because you will hardcode static calls everywhere, saving them will become a nightmare. And you also cannot mock them (okay, PHPUnit can, but it only does this to enable testing of code that would otherwise be unstable).

Just instantiate and use the usual functions and save yourself the trouble. There is no advantage to using statics. And the impact of performance is completely and completely negligible.

I would probably create an interface for the adapter and adapters for implementation

 interface IPathAdapter { public function cleanPath($path); public function isAbsolutePath($part); // more … } 

and then probably something like

 class Path implements IPathAdapter { protected $_adapter; public function __construct(IPathAdapter $adapter) { $this->_adapter = $adapter; } public function cleanPath($path) { $this->_adapter->cleanPath($part); } public function isAbsolutePath($part) { $this->_adapter->isAbsolutePath($part); } // more … public function join(){ $parts = func_get_args(); $joined = array($this->getScriptPath()); foreach($parts as $part){ $part = $this->cleanPath($path); if ($this->isAbsolutePath($part)){ $joined = array($part); } else{ $joined[] = $part; } } return implode($this->getPathSeparator(), $joined); } } 

So when I want to use Path, I will need to do

 $path = new Path(new PathAdapter_Windows); 

If you cannot enter adapters, I would probably go along the route you already suggested and pass the adapter class name as an argument to its instance from Path. Or I would completely leave detection of the corresponding adapter to the Path class, for example. find the OS, and then specify what you need.

If you want to auto-detect, look. Does PHP have a function to detect the OS on which it runs? . I would probably write a separate class to handle identification, and then make it dependent on the Path class, for example.

 public function __construct(IDetector $detector = NULL) { if($detector === NULL){ $detector = new OSDetector; } $this->_detector = $detector; } 

The reason I inject is because it will allow me to change the implementation, for example. to mock the detector in UnitTests, but can also ignore injection at runtime. He will then use the default OSDetector. Using the detector, find the OS and create the appropriate adapter somewhere along the way or in a special Factory.

+3
source

I think you can do this, you just need to put the namespace path in the global var, for example, in the autoload.php composer:

$ GLOBALS ['ADAPTED_CLASS_NAMESPACE'] = 'MyComponent \ AdapterFoo \ VendorBar';

I think this is a good approach in a context where you cannot use ie dependency injection in essence for validation (we remember that split validation classes are better).

 <?php namespace MyComponent; use MyComponent\AdaptedInterface; use ReflectionClass; class Adapter { /** * @var AdaptedInterface */ protected $adaptedClass; public function __construct(AdaptedInterface $validator = null) { if (null == $validator && $this->validateClassPath($GLOBALS['ADAPTED_CLASS_NAMESPACE'])) { $this->adaptedClass = new $GLOBALS['ADAPTED_CLASS_NAMESPACE']; } else { $this->adaptedClass = $validator; } } protected function validateClassPath($classPath) { $reflection = new ReflectionClass($classPath); if (!$reflection->implementsInterface( 'MyComponent\AdaptedInterface' )) { throw new \Exception('Your adapted class have not a valid class path :' . $classPath . 'given'); } return true; } } 

So, anywhere:

 (new Adapter())->foo($bar); 
0
source

Source: https://habr.com/ru/post/1332353/


All Articles