OOP不仅涉及单个类的外观和操作方式。 它还涉及一个或多个类的实例如何协同工作。
这就是为什么您看到这么多基于“汽车”和“人”的示例的原因,因为它们实际上在说明这一原理方面做得非常好。
我认为,OOP中最重要的课程是封装和多态性。
封装:以简洁,逻辑的方式将数据和对该数据进行操作的逻辑耦合在一起多态性:一个对象看起来和/或表现类似另一个对象的能力。
一个真实的例子就是目录迭代器。 该目录在哪里? 也许是本地文件夹,或者像FTP服务器一样远程。 谁知道!
这是演示封装的基本类树:
interface DirectoryIteratorInterface
{
/**
* @return \Traversable|array
*/
public function listDirs();
}
abstract class AbstractDirectoryIterator implements DirectoryIteratorInterface
{
protected $root;
public function __construct($root)
{
$this->root = $root;
}
}
class LocalDirectoryIterator extends AbstractDirectoryIterator
{
public function listDirs()
{
// logic to get the current directory nodes and return them
}
}
class FtpDirectoryIterator extends AbstractDirectoryIterator
{
public function listDirs()
{
// logic to get the current directory nodes and return them
}
}
每个类/对象都负责其自己的检索目录列表的方法。 数据(变量)耦合到使用该数据的逻辑(类函数,即方法)。
但是故事还没有结束-还记得我所说的OOP是关于类实例如何协同工作的,而不是单个类或对象如何协同工作的吗?
好的,让我们对该数据进行一些处理-将其打印到屏幕上吗? 当然。 但是如何? HTML? 纯文本? RSS? 让我们解决这个问题。
interface DirectoryRendererInterface
{
public function render();
}
abstract class AbstractDirectoryRenderer implements DirectoryRendererInterface
{
protected $iterator;
public function __construct(DirectoryIteratorInterface $iterator)
{
$this->iterator = $iterator;
}
public function render()
{
$dirs = $this->iterator->listDirs();
foreach ($dirs as $dir) {
$this->renderDirectory($dir);
}
}
abstract protected function renderDirectory($directory);
}
class PlainTextDirectoryRenderer extends AbstractDirectoryRenderer
{
protected function renderDirectory($directory)
{
echo $directory, "\n";
}
}
class HtmlDirectoryRenderer extends AbstractDirectoryRenderer
{
protected function renderDirectory($directory)
{
echo $directory, "
";
}
}
好的,现在我们有了几个用于遍历和呈现目录列表的类树。 我们如何使用它们?
// Print a remote directory as HTML
$data = new HtmlDirectoryRenderer(
new FtpDirectoryIterator('ftp://example.com/path')
);
$data->render();
// Print a local directory a plain text
$data = new PlainTextDirectoryRenderer(
new LocalDirectoryIterator('/home/pbailey')
);
$data->render();
现在,我知道您在想什么:“但是,彼得,我不需要这些大型树来执行此操作!” 但是,如果您认为那是错的话,就像我怀疑您曾经使用过“汽车”和“人”的例子一样。 不要专注于示例的细节-而是尝试了解这里发生的事情。
我们创建了两个类树,其中一个(*DirectoryRenderer)以预期的方式使用另一个(*DirectoryIterator)-这通常称为合同。 *DirectoryRenderer的实例不在乎它接收的是哪种类型的*DirectoryIterator,也不在乎*DirectoryIterator的实例如何呈现它们。
为什么? 因为我们是按照这种方式设计的。 他们只是彼此插入并工作。 这是面向对象的操作。