php设计模式(二十四):访问者模式(Visitor)

2023-07-09 14:04:00 浏览数 (1)

访问者模式

访问者模式又称为:Visitor。访问者模式是一种行为设计模式,它能将算法与其所作用的对象隔离开来

访问者模式表示一个作用于某对象结构中各元素的操作。它可以在不修改各元素类的前提下定义作用于这些元素的新操作,即动态的增加具体访问者角色

也就是说访问者模式是适用于 稳定的结构什么是稳定的结构呢? 稳定结构是指我们的对象在早之前已经开发好了,功能都实现好了(前面版本)。访问者模式是说现在我们突然要给这个开发好的结构(稳定结构),在不影响原来稳定的基础上,增添一些职责或者功能,所以说访问者模式是马后炮行为。

访问者模式是利用了双重分派。先将访问者传入元素对象的 Accept 方法中,然后元素对象再将自己传入访问者,之后访问者执行元素的相应方法。

问题

假设我们已经编写好一个操作(打开、关闭、或者内容) Pdf、Word、Xml 文件代码,现在突然我们顶头上司,需要我们实现将这些文件里的文字内容导出到 Txt 文件中。

我们可能会对每个文件对象中都写一个将内容导出 Txt 文件的方法。

过了几天顶头上司又要我们写一个将这些文件内容导出成 图片 可视化格式(类似截图内容),我们又在每个文件对象中都写一个将内容导出 图片 文件的方法。

问题是我们后面可能顶头上司会让我们支持导出其他更多的格式,我们如果这么一个个写的对象类中,会使代码越来越复杂且臃肿,且不说我们新增导出各种格式不会影响这个类原有功能的使用,可能到后面代码多起来我们自己都看不懂?所以我们应该怎么解决呢?

解决方法

我们可以使用访问器模式,给 Pdf、Word、Xml 文件代码提供一个访问者,以用来访问它们。

顾名思义访问者就是拜访的形式去拜访 Pdf、Word、Xml 类,所以我们 Pdf、Word、Xml 类中需要添加一个 接受访问者的方法 Accept(接受访问者的方法),然后再将当前类的传递给访问者(访问者需要有获取所有数据的权限)。

我们就做到了只要在 Pdf、Word、Xml 类代码中添加一个接受方法,再将类对象传递给访问者

具体的导出工作交个访问者具体方法去做 ,就不会导致 Pdf、Word、Xml 类代码复杂且难以维护。

可能往下看代码容易理解点,那下面就看代码吧。

结构

FileInterface:文件对象接口类。 PdfFile、WordFile、XmlFile:具体的文件类,实现文件接口类,提供访问本身内容的方法。 VisitorInterface:访问者接口类,提供操作接口方法。 FileToTxt:访问者实现类,提供具体向文件的操作方法。

代码示例

文件接口类

代码语言:javascript复制
interface FileInterface
{
    /**
     * 接受者(接受访问者)
     * @return mixed
     * @author chendashengpc
     */
    public function accept(VisitorInterface $visitor);
}

文件具体实现类

代码语言:javascript复制
// pdf
class PdfFile implements FileInterface
{
    public function accept(VisitorInterface $visitor)
    {
        return $visitor->PdfToTxt($this);
    }

    /**
     * 打开 PDF 文件内容
     * @return string
     * @author chendashengpc
     */
    public function openPdf(): string
    {
        return '我是 Pdf 文件内容';
    }
}

// word
class WordFile implements FileInterface
{
    public function accept(VisitorInterface $visitor)
    {
        return $visitor->WordToTxt($this);
    }

    /**
     * 打开 Word 文件内容
     * @return string
     * @author chendashengpc
     */
    public function openWord(): string
    {
        return '我是 Word 文件内容';
    }
}

// xml
class XmlFile implements FileInterface
{
    public function accept(VisitorInterface $visitor)
    {
        return $visitor->XmlToTxt($this);
    }

    /**
     * 打开 Xml 文件内容
     * @return string
     * @author chendashengpc
     */
    public function openXml(): string
    {
        return '我是 Xml 文件内容';
    }
}

访问者接口类

代码语言:javascript复制
interface VisitorInterface
{
    /**
     * pdf 转 txt
     * @param FileInterface $file
     * @return mixed
     * @author chendashengpc
     */
    public function PdfToTxt(FileInterface $file);

    /**
     * Word 转 txt
     * @param FileInterface $file
     * @return mixed
     * @author chendashengpc
     */
    public function WordToTxt(FileInterface $file);

    /**
     * Xml 转 txt
     * @param FileInterface $file
     * @return mixed
     * @author chendashengpc
     */
    public function XmlToTxt(FileInterface $file);
}

访问者具体实现类

代码语言:javascript复制
/**
 * 文件转 txt
 */
class FileToTxt implements VisitorInterface
{
    /**
     * pdf 转 txt
     * @param FileInterface $file
     * @return mixed
     * @author chendashengpc
     */
    public function PdfToTxt(FileInterface $file)
    {
        return $file->openPdf();
    }

    /**
     * Word 转 txt
     * @param FileInterface $file
     * @return mixed
     * @author chendashengpc
     */
    public function WordToTxt(FileInterface $file)
    {
        return $file->openWord();
    }

    /**
     * Xml 转 txt
     * @param FileInterface $file
     * @return mixed
     * @author chendashengpc
     */
    public function XmlToTxt(FileInterface $file)
    {
        return $file->openXml();
    }
}

客户端使用

代码语言:javascript复制
$fileToTxt = new FileToTxt();

$pdf = new PdfFile();
echo $pdf->accept($fileToTxt) . PHP_EOL;

$word = new WordFile();
echo $word->accept($fileToTxt) . PHP_EOL;

$xml = new XmlFile();
echo $xml->accept($fileToTxt) . PHP_EOL;

输出

代码语言:javascript复制
我是 Pdf 文件内容
我是 Word 文件内容
我是 Xml 文件内容

UML

优缺点

优点

  • 开闭原则。 可以引入在不同类对象上执行的新行为,且无需对这些类做出修改。
  • 单一职责原则。可将同一行为的不同版本移到同一个类中。
  • 访问者对象可以在与各种对象交互时收集一些有用的信息。当你想要遍历一些复杂的对象结构(例如对象树),并在结构中的每个对象上应用访问者时, 这些信息可能会有所帮助。

缺点

  • 每次在元素层次结构中添加或移除一个类时,都要更新所有的访问者。
  • 在访问者同某个元素进行交互时,它们可能没有访问元素私有成员变量和方法的必要权限。

0 人点赞