Any real world simple samples of using abstract class? I'm trying to get in PHP's OOP, but I still can't understand - why abstract class should be used and when (Yes, I know that it's impossible to create abstract class instance, only instance of class inheriting it).
问题:
回答1:
Maybe you have an image class, and you have 2 drivers, GD and ImageMagick.
Your base class may be
abstract class Image {
public function resize($width, $height) {
// Some prep code...
}
}
Then your GD driver will be like
class Image_Gd extends Image {
public function resize($width, $height) {
// Actual code
}
}
Look at Kohana's source on GitHub. It has an Image
class which is abstract
.
回答2:
OK, let's say you want to load script configuration. That configuration can be stored within database/XML/INI/YAML file. Each driver has to implement some interface, let's call it ConfigurationLoader
.
interface ConfigurationLoader {
public function load();
public function get($key, $default = null);
}
// The final usage of the code:
$configuration = new XMLConfiguration('./some/file.xml');
$configuration = new YAMLConfiguration('./some/file.yaml');
$configuration = new DatabaseConfiguration($dbHandler, 'table_name');
$configuration->load();
echo $configuration->get('abc.def.ghi');
So now we need to implement our drivers. First thing you should notice is that file based drivers probably work the same. The only difference is that each of them parse file sources different way. On the other hand database driver works totally different.
class DatabaseConfiguration implements ConfigurationLoader {
protected $config = array();
protected $pdo, $tableName;
public function __construct(PDO $dbHandler, $tableName) {
$this->pdo = $pdo;
$this->tableName = $tableName;
}
public function load() {
$this->config = /* fetch data from database */;
}
public function get($key, $default = null) {
return array_key_exists($this->config, $key) ? $this->config[$key] : $default;
}
}
Now we need to implement XML, INI and YAML driver. All of them work almost the same, so... let's create some abstract class that will handle common code:
abstract class FileConfiguration implements ConfigurationLoader {
// Each file-based configuration loader has a constructor that takes file name as first argument
protected $filename;
public function __construct($filename) {
$this->filename = $filename;
$this->getFileContents();
}
// Each file-based driver has to store file content
protected $fileContent;
protected function getFileContents() {
$this->fileContents = file_get_contents($this->filename);
}
// Each driver will have to implement its own implementation of load().
// XMLConfiguration will parse XML, INIConfiguration will parse INI etc.
abstract public function load();
// However all of them will store parsed configuration in $config array:
protected $config = array();
public function get($key, $default = null) {
return array_key_exists($this->config, $key) ? $this->config[$key] : $default;
}
}
FileConfiguration
has to be abstract because it doesn't know how to parse file contents. Only delivered classes know how to do that:
class XMLConfiguration extends FileConfiguration {
public function load() {
$xml = simplexml_load_string($this->fileContents);
foreach ($xml->root as $config) {
$this->config[(string) $config->key] = (string) $config->value;
}
}
}
class YAMLConfiguration extends FileConfiguration {
public function load() {
$yaml = new SomeYAMLParser($this->fileContents);
foreach ($yaml->parse() as $config) {
$this->config[$config['key']] = $config['value'];
}
}
}
class INIConfiguration extends FileConfiguration {
public function load() {
...
}
}
As you can see in this example there could be even one more abstract class AbstractConfiguration
that would store $config
property and get($key, $default = null)
method and it would be a parent class for DatabaseConfiguration
and FileConfiguration
.