接口注入和公共类

2024-04-17

我正在尝试了解 OOP 原则并编写自己的类。作为一种学习方法,我决定将我在 Wordpress 中编写的几个函数转换为 OOP 类。这些函数协同工作,以便根据 URL 中设置的引荐来源网址(其中 4 个)在单个页面上输出正确的帖子链接。

这是具有基本工作流程的设置(工作流程可能会随着我的进展而改变):

根据档案页面将4个查询变量设置为URL,即,为分类页面设置1个查询变量,为作者页面设置1个查询变量,依此类推。任何页面都不能有多个自定义查询变量。这 4 个变量由我的第一个类检索,并根据给定的全局变量进行检查,在本例中$_GET。我还没有对我的类中的 4 个变量进行硬编码,这适用于$_GET以及保持班级可测试。如果 URL 中存在该值,则通过以下方式返回键/值对has*方法。这些方法返回null如果没有找到匹配项。 (这是原始数据,将由使用该数据的类进行清理/转义)

这是完整的课程

<?php
namespace PG\Single\Post\Navigation;

/**
 * Test set values against the super global given. Returns conditional properties
 * which is boolean values. true is returned on success and false on failure.
 *
 * @param $superGlobalVar Super global to test the values against
 * @param (string) $authorReferrer 
 * @param (string) $dateReferrer 
 * @param (string) $searchReferrer 
 * @param (string) $taxReferrer 
*/ 
class RequestReferrerHandler implements RequestReferrerHandlerInterface
{
    /**
     * @since 1.0.0
     * @access protected
     * @var (array) $superGlobalVar
    */
    protected $superGlobalVar;

    /**
     * @since 1.0.0
     * @access protected
     * @var (string) $authorReferrer
    */
    protected $authorReferrer;

    /**
     * @since 1.0.0
     * @access protected
     * @var (string) $dateReferrer
    */
    protected $dateReferrer;

    /**
     * @since 1.0.0
     * @access protected
     * @var (string) $searchReferrer
    */
    protected $searchReferrer;

    /**
     * @since 1.0.0
     * @access protected
     * @var (string) $taxReferrer
    */
    protected $taxReferrer;


    /**
     * Public constructor method.
     *
     * @param $superGlobalVar  Super global to get data from
     * @param $authorReferrer  Query variable from author referrer to test
     * @param $dateReferrer    Query variable from date referrer to test
     * @param $searchReferrer  Query variable from search referrer to test
     * @param $taxReferrer     Query variable from taxonomy referrer to test
    */
    public function __construct($superGlobalVar = null, $authorReferrer= null, $dateReferrer = null, $searchReferrer = null, $taxReferrer = null)
    {
        $this->superGlobalVar = $superGlobalVar;
        $this->authorReferrer = $authorReferrer;
        $this->dateReferrer   = $dateReferrer;
        $this->searchReferrer = $searchReferrer;
        $this->taxReferrer    = $taxReferrer;
    }

    /**
     * Setter setSuperGlobalVar.
     *
     * @since 1.0.0
     * @param $superGlobalVar
     * @return $this
     */
    public function setSuperGlobalVar($superGlobalVar)
    {
        $this->superGlobalVar = $superGlobalVar;
        return $this;
    }   

    /**
     * Returns an array of super global variables.
     *
     * @since 1.0.0
     * @return (array) $this->superGlobalVar
    */ 
    public function getSuperGlobalVar()
    {
        return $this->superGlobalVar;
    }

    /**
     * Setter setAuthorReferrer
     *
     * @since 1.0.0
     * @param $authorReferrer
     * @return $this
     */
    public function setAuthorReferrer($authorReferrer)
    {
        $this->authorReferrer = $authorReferrer;
        return $this;
    }   

    /**
     * Returns the value of the $authorReferrer property.
     *
     * @since 1.0.0
     * @return (array) $this->authorReferrer
    */ 
    public function getAuthorReferrer()
    {
        return $this->authorReferrer;
    }

    /**
     * Setter setDateReferrer.
     *
     * @since 1.0.0
     * @param $dateReferrer
     * @return $this
     */
    public function setDateReferrer($dateReferrer)
    {
        $this->dateReferrer = $dateReferrer;
        return $this;
    }   

    /**
     * Returns the value of the $dateReferrer property.
     *
     * @since 1.0.0
     * @return (array) $this->dateReferrer
    */ 
    public function getDateReferrer()
    {
        return $this->dateReferrer;
    }

    /**
     * Setter setSearchReferrer.
     *
     * @since 1.0.0
     * @param $searchReferrer
     * @return $this
     */
    public function setSearchReferrer($searchReferrer)
    {
        $this->searchReferrer = $searchReferrer;
        return $this;
    }   

    /**
     * Returns the value of the $searchReferrer property.
     *
     * @since 1.0.0
     * @return (array) $this->searchReferrer
    */ 
    public function getSearchReferrer()
    {
        return $this->searchReferrer;
    }

    /**
     * Setter setTaxReferrer.
     *
     * @since 1.0.0
     * @param $taxReferrer
     * @return $this
     */
    public function setTaxReferrer($taxReferrer)
    {
        $this->taxReferrer = $taxReferrer;
        return $this;
    }   

    /**
     * Returns the value of the $taxReferrer property.
     *
     * @since 1.0.0
     * @return (array) $this->taxReferrer
    */ 
    public function getTaxReferrer()
    {
        return $this->$taxReferrer;
    }

    /**
     * Test $authorReferrer against $superGlobalVar.
     *
     * @since 1.0.0
     * @return (bool) true on success or false on failure
     */
    public function isAuthorReferrer()
    {
        if ($this->authorReferrer && isset($this->superGlobalVar[$this->authorReferrer])) { 
            $isAuthorReferrer = true;
        } else {
            $isAuthorReferrer = false;
        }
        return $isAuthorReferrer;
    }

    /**
     * Test $authorReferrer against $superGlobalVar
     *
     * @since 1.0.0
     * @return (bool) true on success or false on failure
     */
    public function isDateReferrer()
    {
        if ($this->dateReferrer && isset($this->superGlobalVar[$this->dateReferrer])) { 
            $isDateReferrer = true;
        } else {
            $isDateReferrer = false;
        }
        return $isDateReferrer;
    }

    /**
     * Test $authorReferrer against $superGlobalVar.
     *
     * @since 1.0.0
     * @return (bool) true on success or false on failure
     */
    public function isSearchReferrer()
    {
        if ($this->searchReferrer && isset($this->superGlobalVar[$this->searchReferrer])) { 
            $isSearchReferrer = true;
        } else {
            $isSearchReferrer = false;
        }
        return $isSearchReferrer;
    }

    /**
     * Test $authorReferrer against $superGlobalVar.
     *
     * @since 1.0.0
     * @return (bool) true on success or false on failure
     */
    public function isTaxReferrer()
    {
        if ($this->taxReferrer && isset($this->superGlobalVar[$this->taxReferrer])) { 
            $isTaxReferrer = true;
        } else {
            $isTaxReferrer = false;
        }
        return $isTaxReferrer;
    }

    /**
     * Conditional which check if the current post is a referred post.
     *
     * @since 1.0.0
     * @return (bool) true on success or false on failure
     */
    public function isReferredPost()
    {
        if ($this->isAuthorReferrer() || $this->isDateReferrer() || $this->isSearchReferrer() || $this->isTaxReferrer()) {
            $isReferredPost = true;
        } else {
            $isReferredPost = false;
        }
        return $isReferredPost;
    }

    /**
     * Return the value from the super global when the current post is a post referred from
     * an author archive page.
     *
     * @since 1.0.0
     * @return (array) $authorReferrerValue
     */ 
    public function hasAuthorReferrerValue()
    {
        if ($this->isAuthorReferrer()) {
            $authorReferrerValue = [$this->authorReferrer => $this->superGlobalVar[$this->authorReferrer]];
        } else {
            $authorReferrerValue = null;
        }
        return $authorReferrerValue;
    }

    /**
     * Return the value from the super global when the current post is a post referred from
     * a date archive page.
     *
     * @since 1.0.0
     * @return (array) $dateReferrerValue
     */ 
    public function hasDateReferrerValue()
    {
        if ($this->isDateReferrer()) {
            $dateReferrerValue = [$this->dateReferrer => $this->superGlobalVar[$this->dateReferrer]];
        } else {
            $dateReferrerValue = null;
        }
        return $dateReferrerValue;
    }

    /**
     * Return the value from the super global when the current post is a post referred from
     * a search page.
     *
     * @since 1.0.0
     * @return (array) $searchReferrerValue
     */ 
    public function hasSearchReferrerValue()
    {
        if ($this->isSearchReferrer()) {
            $searchReferrerValue = [$this->searchReferrer => $this->superGlobalVar[$this->searchReferrer]];
        } else {
            $searchReferrerValue = null;
        }
        return $searchReferrerValue;
    }

    /**
     * Return the value from the super global when the current post is a post referred from
     * a taxonomy archive page.
     *
     * @since 1.0.0
     * @return (array) $taxReferrerValue
     */ 
    public function hasTaxReferrerValue()
    {
        if ($this->isTaxReferrer()) {
            $taxReferrerValue = [$this->taxReferrer => $this->superGlobalVar[$this->taxReferrer]];
        } else {
            $taxReferrerValue = null;
        }
        return $taxReferrerValue;
    }

}

这就是我使用这个类的方式

$b = new RequestReferrerHandler($_GET, 'aq', 'dq', 'sq', 'tq');
?><pre><?php var_dump($b->hasAuthorReferrerValue()); ?></pre><?php
?><pre><?php var_dump($b->hasDateReferrerValue()); ?></pre><?php
?><pre><?php var_dump($b->hasSearchReferrerValue()); ?></pre><?php
?><pre><?php var_dump($b->hasTaxReferrerValue()); ?></pre><?php

出于测试目的,您可以注入类似的东西['aq' => '1']进入班级而不是$_GET

这就是我现在陷入困境的地方,不知道如何继续前进。我需要构造两个类,它们都使用上面类中的相同方法,其中一个类将从has*来自上述类的方法,并且一个类将创建query_vars也来自has*上述类中的方法将用于构造新的帖子链接

因此,简而言之,这两个类都将使用与上述类完全相同的方法

hasAuthorReferrerValue();
hasDateReferrerValue();
hasSearchReferrerValue();
hasTaxReferrerValue();

作为一个例子,下面是两个类应该是什么样子的示例。 (我这里省略了一些方法以使代码更易于管理)

ClassA

<?php
namespace PG\Single\Post\Navigation;

class ClassA //Just a generic name for testing purposes. Will also implement ClassAInterface
{
    protected $handler;

    public function __construct(RequestReferrerHandlerInterface $handler)
    {
        $this->handler = $handler;
    }

    public function santizeAuthor() 
    {
        $author = $this->handler->hasAuthorReferrerValue(); // Value will be either null or single key/value pair array. Example ['aq' => '1']

        if ($author) {
            $author = array_values($author);
            $author = ['author' => (int)htmlspecialchars($author[0])]; //Will output ['author' => 1]
        }

        return $author; //Returns null or the array ['author' => 1]
    }

    public function santizeDate() 
    {
        $date = $this->handler->hasDateReferrerValue();

        if ($date) {
            // @TODO Still to work out
        }

        return $date;
    }

    //etc

    public function queryArguments() // Will be used in the controller class ClassC
    {
        $queryArgs = null;

        if ($this->santizeAuthor()) {

            $queryArgs = $this->santizeAuthor();

        } elseif ($this->santizeDate) {

            $queryArgs = $this->santizeDate();

        } // etc
        return $queryArgs; //Will return null if all 4 conditions fail or return the value from the one that returns true
    }

}

ClassB

<?php
namespace PG\Single\Post\Navigation;

class ClassB //Just a generic name for testing purposes. Will also implement ClassBInterface
{
    protected $handler;

    public function __construct(RequestReferrerHandlerInterface $handler)
    {
        $this->handler = $handler;
    }

    public function santizeAuthor() 
    {
        $author = $this->handler->hasAuthorReferrerValue(); // Value will be either null or single key/value pair array. Example ['aq' => '1']

        if ($author) {
            foreach ($author as $k=>$v)
                $author[htmlspecialchars($k)] = (int)htmlspecialchars($v);
        }

        return $author; //Returns null or the array ['aq' => 1]
    }

    public function santizeDate() 
    {
        $date = $this->handler->hasDateReferrerValue();

        if ($date) {
            // @TODO Still to work out
        }

        return $date;
    }

    //etc

    public function queryVars() // Will be used in the controller class ClassC
    {
        $queryVars = null;

        if ($this->santizeAuthor()) {

            $queryVars = $this->santizeAuthor();

        } elseif ($this->santizeDate) {

            $queryVars = $this->santizeDate();

        } // etc
        return $queryVars; //Will return null if all 4 conditions fail or return the value from the one that returns true
    }

}

The queryArguments()方法来自ClassAqueryVars()方法来自ClassB将在其他类(或一个控制器类)中使用

我完全缺乏 OOP 方面的适当知识,对关注点分离、封装、SOLID 原则和保持类可测试性感到困惑,这让我重新猜测我的代码,我确实觉得我错过了一些东西。

无论如何我可以优化上面的内容。我并不是要求重写任何类型的代码,我所需要的只是适当的指导和优化此代码的想法,以使其达到标准(如果不是)。如果任何人都可以提供代码示例(例如大纲框架),那将是一个真正的优势


查看您的代码,您肯定有一个良好的开端。在 OOP 中编程时,您已经使用了一条好的经验法则 -对接口进行编程,而不是对实现进行编程。按术语界面我不仅指实际的接口,还指抽象类。

所以你的问题的核心是你想要上两门课,ClassA and ClassB两者都使用通用方法RequestReferrerHandler。您已经为界面做好了基础工作RequestReferrerHandlerInterface。所以我们会说你有一个如下所示的界面:

interface RequestReferrerHandlerInterface
{
    public function hasAuthorReferrerValue();
    public function hasDateReferrerValue();
    public function hasSearchReferrerValue();
    public function hasTaxReferrerValue();
}

只要这个接口是由实现的RequestReferrerHandler您可以键入提示接口作为构造函数的要求ClassA and ClassB。但这不是什么新鲜事,因为您已经这样做了。

有两件事特别让我觉得潜在的麻烦。首先,由于您希望类的职责较小,因此您应该承担向类提供数据的责任RequestReferrerHandler远离它自己并把它交给你Controller。换句话说,不要注入$_GET进入你的班级。确保您的Controller拥有正确创建所需的所有信息RequestReferrerHandler让我们看看您的RequestReferrerHandler类,充满了它需要的所有方法。

class RequestReferrerHandler implements RequestReferrerHandlerInterface
{
    private $author;
    private $date;
    private $search;
    private $tax;

    public function __construct($author = null, $date = null, $search = null, $tax = null)
    {
        $this->setAuthorReferrer($author);
        $this->setDateReferrer($date);
        $this->setSearchReferrer($search);
        $this->setTaxReferrer($tax);
    }

    public function hasAuthorReferrerValue()
    {
        return $this->author !== null ? true : false;
    }

    public function hasDateReferrerValue()
    {
        return $this->date !== null ? true : false;
    }

    public function hasSearchReferrerValue()
    {
        return $this->search !== null ? true : false;
    }

    public function hasTaxReferrerValue()
    {
        return $this->tax !== null ? true : false;
    }

    public function getAuthorReferrer()
    {
        return $this->author;
    }

    public function getDateReferrer()
    {
        return $this->date;
    }

    public function getSearchReferrer()
    {
        return $this->search;
    }

    public function getTaxReferrer()
    {
        return $this->tax;
    }

    public function setAuthorReferrer($author)
    {
        $this->author = $author;
    }

    public function setDateReferrer($date)
    {
        $this->date = $date;
    }

    public function setSearchReferrer($search)
    {
        $this->search = $search;
    }

    public function setTaxReferrer($tax)
    {
        $this->tax = $tax;
    }
}

第二个突出的事情是santize()方法。你看到它们是如何在两者中重复的吗?ClassA and ClassB? The sanitizeAuthor()两个类之间有所不同,但是其余的呢?这是 DRY 的情况(不要重复自己)原则可以提供帮助。由于多个类可能必须以类似的方式清理数据,因此将其从类中抽象出来是有意义的。

让我们看看如何做到这一点,然后我们将回到您的具体课程。首先创建一个新接口,该接口将指定必须由可以清理数据的对象公开的方法。

interface SanitizerInterface
{
    public function sanitizeAuthor();
    public function sanitizeDate();
    public function sanitizeSearch();
    public function sanitizeTaxonomy();
}

现在,如果你拥有的每一个物体ClassX以不同的方式实现这四种方法,您可以开始在不同的类中实现它,这些类只是清理数据。然而,对于这个例子,我们会说情况并非如此。让我们假设sanitizeAuthor()之间可能有所不同ClassA and ClassB(它在您的代码中)并且所有其他方法将完全相同地实现。在这种情况下,我们可以使用一个抽象类来实现消毒剂方法。

abstract class AbstractSanitizer implements SanitizerInterface
{
    protected $handler;

    public function __construct() {}

    public function setHandler(RequestReferrerHandlerInterface $handler)
    {
        $this->handler = $handler;
    }   

    /* For this example we are saying that sanitizeDate(), sanitizeTaxonomy() and
     * sanitizeSearch() will be the same no matter what.  So let's implement them 
     * and leave the child classes to implement sanitizeAuthor(). 
     * 
     * Implement the details of the sanitizer function to fit your needs.
     */

    public function sanitizeDate()
    {
        if($this->handler !== null)
        {
            //Perform whatever tasks to sanitize the date
            $sanitized = strtoupper($this->handler->getDateReferrer());
            echo "Sanitize date -> switch to uppercase letters.\n";
            $this->handler->setDateReferrer($sanitized);
        }
    }

    public function sanitizeSearch()
    {
        if($this->handler !== null)
        {
            //Perform whatever tasks to sanitize the search
            $sanitized = strtolower($this->handler->getSearchReferrer());
            echo "Sanitize search -> switch to lowercase letters.\n";
            $this->handler->setSearchReferrer($sanitized);
        }
    }

    public function sanitizeTaxonomy()
    {
        if($this->handler !== null)
        {
            //Perform whatever tasks to sanitize the taxonomy
            $sanitized = str_replace(" ", "_", $this->handler->getTaxReferrer());
            echo "Sanitize Taxonomy -> convert spaces to underscores.\n";
            $this->handler->setTaxReferrer($sanitized);
        }
    }

}

有些事情需要立即注意。首先,你会注意到有setHandler()方法接受一个实例RequestReferrerHandlerInterface。为什么会有这个?大部分都是方便的。既然我们已经采取了消毒行为并将其封装到自己的类中,那么如果我们为消毒器提供了一种更新具体行为的方法,那就太好了RequestReferrerHandler它与清理方法的更新输出一起使用。

接下来,我们使用以下方法RequestReferrerHandler中未指定的类RequestReferrerHandlerInterface。这不是一个迫在眉睫的问题per se,因为我们知道像 getter 和 setter 这样的方法在类中。然而,仅对界面进行类型提示并不能保证如果您决定使用不同的具体对象来实现该接口,那么这些方法将可用。因此,我们需要更新RequestReferrerHandlerInterface以及保证其可用性的方法。

interface RequestReferrerHandlerInterface
{
    public function hasAuthorReferrerValue();
    public function hasDateReferrerValue();
    public function hasSearchReferrerValue();
    public function hasTaxReferrerValue();
    public function getAuthorReferrer();
    public function getDateReferrer();
    public function getSearchReferrer();
    public function getTaxReferrer();
    public function setAuthorReferrer($author);
    public function setDateReferrer($date);
    public function setSearchReferrer($search);
    public function setTaxReferrer($tax);
}

现在,回到那些消毒剂。我们知道ClassA and ClassB将实施他们的sanitizeAuthor()方法不同。抽象类AbstractSanitizer之所以被做成这样是因为sanitizeAuthor()方法从SanitizerInteface未实施于AbstractSanitizer所以我们必须扩展它以提供功能。我们需要以下两个类来执行此操作:

class SanitizerForClassA extends AbstractSanitizer
{
    /* This class must provide an implementation for how ClassA will
     * handle the sanitizeAuthor() method.
     */

    public function sanitizeAuthor()
    {
        if($this->handler !== null)
        {
            //Perform whatever tasks to sanitize the for ClassA
            $sanitized = array("author" => $this->handler->getAuthorReferrer());
            echo "Sanitize author -> ClassA makes author an array.\n";
            $this->handler->setAuthorReferrer($sanitized);
        }   
    }
}

class SanitizerForClassB extends AbstractSanitizer
{
    /* This class must provide an implementation for how ClassB will
     * handle the sanitizeAuthor() method.
     */

    public function sanitizeAuthor()
    {
        if($this->handler !== null)
        {
            //Perform whatever tasks to sanitize the for ClassB
            $sanitized = new stdClass();
            $sanitized->author = $this->handler->getAuthorReferrer();
            echo "Sanitize author -> ClassB makes author an object property. \n";
            $this->handler->setAuthorReferrer($sanitized);
        }   
    }
}

这两个具体类可以与ClassA and ClassB净化混凝土中的数据RequestReferrerHandler将传递给它们的方法。

那么继续,让我们看看规格ClassA and ClassB。我们知道ClassA将需要该方法queryArguments(), ClassB将需要该方法queryVars()并且两个类都需要允许一个实例RequestReferrerHandlerInterface and SanitizerInterface在他们的构造函数中。我们将使用一个接口来处理构造函数要求,然后另外两个接口将扩展该接口以提供所需的所有方法要求ClassA and ClassB.

interface SanitizableHandlerInterface
{       
    public function __construct(RequestReferrerHandlerInterface $handler, SanitizerInterface $sanitizer);
}

interface QueryVarsInterface extends SanitizableHandlerInterface
{
    public function queryVars();
}

interface QueryArgumentsInterface extends SanitizableHandlerInterface
{
    public function queryArguments();
}

既然我们现在要开始讨论它,那么让我们看一下将使用这些的类。

class ClassA implements QueryArgumentsInterface
{
    private $handler;
    private $sanitizer;

    public function __construct(RequestReferrerHandlerInterface $handler, SanitizerInterface $sanitizer)
    {
        $this->handler = $handler;
        $this->sanitizer = $sanitizer;
        $this->sanitizer->setHandler($this->handler);
    }

    public function queryArguments() // Will be used in the controller class ClassC
    {
        $queryArgs = null;
        if($this->handler->hasAuthorReferrerValue())
        {
            $this->sanitizer->sanitizeAuthor();
            $queryArgs = $this->handler->getAuthorReferrer();
        }
        if($this->handler->hasDateReferrerValue())
        {
            $this->sanitizer->sanitizeDate();
            $queryArgs = $this->handler->getDateReferrer();
        }
        if($this->handler->hasSearchReferrerValue())
        {
            $this->sanitizer->sanitizeSearch();
            $queryArgs = $this->handler->getSearchReferrer();
        }
        if($this->handler->hasTaxReferrerValue())
        {
            $this->sanitizer->sanitizeTaxonomy();
            $queryArgs = $this->handler->getTaxReferrer();
        }
        return $queryArgs; //Will return null if all 4 conditions fail or return the value from the one that returns true
    }

}

class ClassB implements QueryVarsInterface
{
    private $handler;
    private $sanitizer;

    public function __construct(RequestReferrerHandlerInterface $handler, SanitizerInterface $sanitizer)
    {
        $this->handler = $handler;
        $this->sanitizer = $sanitizer;
        $this->sanitizer->setHandler($this->handler);       
    }

    public function queryVars() // Will be used in the controller class ClassC
    {
        $queryVars = null;
        if($this->handler->hasAuthorReferrerValue())
        {
            $this->sanitizer->sanitizeAuthor();
            $queryVars = $this->handler->getAuthorReferrer();
        }
        if($this->handler->hasDateReferrerValue())
        {
            $this->sanitizer->sanitizeDate();
            $queryVars = $this->handler->getDateReferrer();
        }
        if($this->handler->hasSearchReferrerValue())
        {
            $this->sanitizer->sanitizeSearch();
            $queryVars = $this->handler->getSearchReferrer();
        }
        if($this->handler->hasTaxReferrerValue())
        {
            $this->sanitizer->sanitizeTaxonomy();
            $queryVars = $this->handler->getTaxReferrer();
        }
        return $queryVars; //Will return null if all 4 conditions fail or return the value from the one that returns true
    }
}

到这里,基础工作就完成了。您会注意到,在构造函数中,为给定的处理程序和消毒剂类设置了属性,然后为消毒剂提供了对处理程序的引用。(请记住,清理程序具有对处理程序的引用,以便处理程序中的清理属性会自动更新。各个类现在不需要担心这一点。)

所以现在最重要的问题是如何使用它。好吧,你需要一个可以接受的控制器ClassA and ClassB。我们也将通过它们各自的接口输入提示。

class Controller
{
    public function __construct() {}

    public function doStuff(QueryArgumentsInterface $argsClass, QueryVarsInterface $varsClass)
    {
        var_dump($argsClass->queryArguments());
        var_dump($varsClass->queryVars());
    }
}

在你的版本中queryArguments() and queryVars()您期望返回值是经过清理的数据。让我们插入一些数据,看看会得到什么。(注意:正如您已经发现我使用的任何清理方法都没有做您正在做的事情,它们只是说明性的。)

//TEST DRIVE

//Create a controller that will use the classes
$controller = new Controller();

//Now make use of your new shiny handlers and sanitizers
$controller->doStuff(
    new ClassA(new RequestReferrerHandler("Mark Twain", null, null, null), new SanitizerForClassA()), 
    new ClassB(new RequestReferrerHandler(null, "January 1st, 1999", null, null), new SanitizerForClassB())
);

$controller->doStuff(
    new ClassA(new RequestReferrerHandler(null, null, "OK Google Now!", null), new SanitizerForClassA()), 
    new ClassB(new RequestReferrerHandler(null, null, null, "Super Awesome Taxonomy Tables"), new SanitizerForClassB())
);

$controller->doStuff(
    new ClassA(new RequestReferrerHandler(null, "January 1st, 1999", null, null), new SanitizerForClassA()), 
    new ClassB(new RequestReferrerHandler("Mark Twain", null, null, null), new SanitizerForClassB())
);

$controller->doStuff(
    new ClassA(new RequestReferrerHandler(null, null, null, "Super Awesome Taxonomy Tables"), new SanitizerForClassA()), 
    new ClassB(new RequestReferrerHandler(null, null, "OK Google Now!", null), new SanitizerForClassB())
);

这是输出:

Sanitize author -> ClassA makes author an array.
array (size=1)
  'author' => string 'Mark Twain' (length=10)
Sanitize date -> switch to uppercase letters.
string 'JANUARY 1ST, 1999' (length=17)
Sanitize search -> switch to lowercase letters.
string 'ok google now!' (length=14)
Sanitize Taxonomy -> convert spaces to underscores.
string 'Super_Awesome_Taxonomy_Tables' (length=29)
Sanitize date -> switch to uppercase letters.
string 'JANUARY 1ST, 1999' (length=17)
Sanitize author -> ClassB makes author an object property.
object(stdClass)[15]
  public 'author' => string 'Mark Twain' (length=10)
Sanitize Taxonomy -> convert spaces to underscores.
string 'Super_Awesome_Taxonomy_Tables' (length=29)
Sanitize search -> switch to lowercase letters.
string 'ok google now!' (length=14)

那么这一切让你付出了什么代价?简短的回答 - 复杂性。它需要 4 个接口、1 个抽象类和一些具体类才能将一点数据输出到屏幕上。

你有什么收获?简短的回答——灵活性。将来您可能希望添加更多的类来实现QueryVarsInterface or QueryArgumentsInterface。考虑这些课程ClassC, ClassD and ClassE。所有这些类都需要一个消毒剂类来配合它们(也就是说,如果SanitizerForClassA or SanitizerForClassB不符合要求)并且继续编写消毒剂类会很乏味。嗯,对你来说是件好事,因为你一直在对接口进行编程,你不会有这个问题。您可以轻松地制作一个GenericSanitizer与默认实现sanitizeAuthor()方法。使用可以将此类与Controller::doStuff()在任何情况下,您都不需要专门的消毒剂课程。您可以轻松地实现不同的具体类QueryArgumentInterface or QueryVarsInterface测试您想要添加的实验功能而不篡改当前课程。

希望这能让您对一些 OOP 原则有所了解。这是上面所有代码的完整副本。将其放入一个空的 PHP 文件中并运行它以查看所有操作。快乐编程!

    <?php

/*
 * INTERFACES
 */

interface RequestReferrerHandlerInterface
{
    public function hasAuthorReferrerValue();
    public function hasDateReferrerValue();
    public function hasSearchReferrerValue();
    public function hasTaxReferrerValue();
    public function getAuthorReferrer();
    public function getDateReferrer();
    public function getSearchReferrer();
    public function getTaxReferrer();
    public function setAuthorReferrer($author);
    public function setDateReferrer($date);
    public function setSearchReferrer($search);
    public function setTaxReferrer($tax);
}

interface SanitizerInterface
{
    public function sanitizeAuthor();
    public function sanitizeDate();
    public function sanitizeSearch();
    public function sanitizeTaxonomy();
}

interface SanitizableHandlerInterface
{       
    public function __construct(RequestReferrerHandlerInterface $handler, SanitizerInterface $sanitizer);
}

interface QueryVarsInterface extends SanitizableHandlerInterface
{
    public function queryVars();
}

interface QueryArgumentsInterface extends SanitizableHandlerInterface
{
    public function queryArguments();
}

/*
 * ABSTRACT CLASSES
 */

abstract class AbstractSanitizer implements SanitizerInterface
{
    protected $handler;

    public function __construct() {}

    public function setHandler(RequestReferrerHandlerInterface $handler)
    {
        $this->handler = $handler;
    }   

    /* For this example we are saying that sanitizeDate(), sanitizeTaxonomy() and
     * sanitizeSearch() will be the same no matter what.  So let's implement them 
     * and leave the child classes to implement sanitizeAuthor(). 
     * 
     * Implement the details of the sanitizer function to fit your needs.
     */

    public function sanitizeDate()
    {
        if($this->handler !== null)
        {
            //Perform whatever tasks to sanitize the date
            $sanitized = strtoupper($this->handler->getDateReferrer());
            echo "Sanitize date -> switch to uppercase letters.\n";
            $this->handler->setDateReferrer($sanitized);
        }
    }

    public function sanitizeSearch()
    {
        if($this->handler !== null)
        {
            //Perform whatever tasks to sanitize the search
            $sanitized = strtolower($this->handler->getSearchReferrer());
            echo "Sanitize search -> switch to lowercase letters.\n";
            $this->handler->setSearchReferrer($sanitized);
        }
    }

    public function sanitizeTaxonomy()
    {
        if($this->handler !== null)
        {
            //Perform whatever tasks to sanitize the taxonomy
            $sanitized = str_replace(" ", "_", $this->handler->getTaxReferrer());
            echo "Sanitize Taxonomy -> convert spaces to underscores.\n";
            $this->handler->setTaxReferrer($sanitized);
        }
    }

}

/*
 * CONCRETE CLASSES
 */

class RequestReferrerHandler implements RequestReferrerHandlerInterface
{
    private $author;
    private $date;
    private $search;
    private $tax;

    public function __construct($author = null, $date = null, $search = null, $tax = null)
    {
        $this->setAuthorReferrer($author);
        $this->setDateReferrer($date);
        $this->setSearchReferrer($search);
        $this->setTaxReferrer($tax);
    }

    public function hasAuthorReferrerValue()
    {
        return $this->author !== null ? true : false;
    }

    public function hasDateReferrerValue()
    {
        return $this->date !== null ? true : false;
    }

    public function hasSearchReferrerValue()
    {
        return $this->search !== null ? true : false;
    }

    public function hasTaxReferrerValue()
    {
        return $this->tax !== null ? true : false;
    }

    public function getAuthorReferrer()
    {
        return $this->author;
    }

    public function getDateReferrer()
    {
        return $this->date;
    }

    public function getSearchReferrer()
    {
        return $this->search;
    }

    public function getTaxReferrer()
    {
        return $this->tax;
    }

    public function setAuthorReferrer($author)
    {
        $this->author = $author;
    }

    public function setDateReferrer($date)
    {
        $this->date = $date;
    }

    public function setSearchReferrer($search)
    {
        $this->search = $search;
    }

    public function setTaxReferrer($tax)
    {
        $this->tax = $tax;
    }
}

class SanitizerForClassA extends AbstractSanitizer
{
    /* This class must provide an implementation for how ClassA will
     * handle the sanitizeAuthor() method.
     */

    public function sanitizeAuthor()
    {
        if($this->handler !== null)
        {
            //Perform whatever tasks to sanitize the for ClassA
            $sanitized = array("author" => $this->handler->getAuthorReferrer());
            echo "Sanitize author -> ClassA makes author an array.\n";
            $this->handler->setAuthorReferrer($sanitized);
        }   
    }
}

class SanitizerForClassB extends AbstractSanitizer
{
    /* This class must provide an implementation for how ClassB will
     * handle the sanitizeAuthor() method.
     */

    public function sanitizeAuthor()
    {
        if($this->handler !== null)
        {
            //Perform whatever tasks to sanitize the for ClassB
            $sanitized = new stdClass();
            $sanitized->author = $this->handler->getAuthorReferrer();
            echo "Sanitize author -> ClassB makes author an object property. \n";
            $this->handler->setAuthorReferrer($sanitized);
        }   
    }
}

class ClassA implements QueryArgumentsInterface
{
    private $handler;
    private $sanitizer;

    public function __construct(RequestReferrerHandlerInterface $handler, SanitizerInterface $sanitizer)
    {
        $this->handler = $handler;
        $this->sanitizer = $sanitizer;
        $this->sanitizer->setHandler($this->handler);
    }

    public function queryArguments() // Will be used in the controller class ClassC
    {
        $queryArgs = null;
        if($this->handler->hasAuthorReferrerValue())
        {
            $this->sanitizer->sanitizeAuthor();
            $queryArgs = $this->handler->getAuthorReferrer();
        }
        if($this->handler->hasDateReferrerValue())
        {
            $this->sanitizer->sanitizeDate();
            $queryArgs = $this->handler->getDateReferrer();
        }
        if($this->handler->hasSearchReferrerValue())
        {
            $this->sanitizer->sanitizeSearch();
            $queryArgs = $this->handler->getSearchReferrer();
        }
        if($this->handler->hasTaxReferrerValue())
        {
            $this->sanitizer->sanitizeTaxonomy();
            $queryArgs = $this->handler->getTaxReferrer();
        }
        return $queryArgs; //Will return null if all 4 conditions fail or return the value from the one that returns true
    }

}

class ClassB implements QueryVarsInterface
{
    private $handler;
    private $sanitizer;

    public function __construct(RequestReferrerHandlerInterface $handler, SanitizerInterface $sanitizer)
    {
        $this->handler = $handler;
        $this->sanitizer = $sanitizer;
        $this->sanitizer->setHandler($this->handler);       
    }

    public function queryVars() // Will be used in the controller class ClassC
    {
        $queryVars = null;
        if($this->handler->hasAuthorReferrerValue())
        {
            $this->sanitizer->sanitizeAuthor();
            $queryVars = $this->handler->getAuthorReferrer();
        }
        if($this->handler->hasDateReferrerValue())
        {
            $this->sanitizer->sanitizeDate();
            $queryVars = $this->handler->getDateReferrer();
        }
        if($this->handler->hasSearchReferrerValue())
        {
            $this->sanitizer->sanitizeSearch();
            $queryVars = $this->handler->getSearchReferrer();
        }
        if($this->handler->hasTaxReferrerValue())
        {
            $this->sanitizer->sanitizeTaxonomy();
            $queryVars = $this->handler->getTaxReferrer();
        }
        return $queryVars; //Will return null if all 4 conditions fail or return the value from the one that returns true
    }
}

class Controller
{
    public function __construct() {}

    public function doStuff(QueryArgumentsInterface $argsClass, QueryVarsInterface $varsClass)
    {
        var_dump($argsClass->queryArguments());
        var_dump($varsClass->queryVars());
    }
}

/*
 * TEST DRIVE
 */

//Create a controller that will use the classes
$controller = new Controller();

//Now make use of your new shiny handlers and sanitizers
$controller->doStuff(
    new ClassA(new RequestReferrerHandler("Mark Twain", null, null, null), new SanitizerForClassA()), 
    new ClassB(new RequestReferrerHandler(null, "January 1st, 1999", null, null), new SanitizerForClassB())
);

$controller->doStuff(
    new ClassA(new RequestReferrerHandler(null, null, "OK Google Now!", null), new SanitizerForClassA()), 
    new ClassB(new RequestReferrerHandler(null, null, null, "Super Awesome Taxonomy Tables"), new SanitizerForClassB())
);

$controller->doStuff(
    new ClassA(new RequestReferrerHandler(null, "January 1st, 1999", null, null), new SanitizerForClassA()), 
    new ClassB(new RequestReferrerHandler("Mark Twain", null, null, null), new SanitizerForClassB())
);

$controller->doStuff(
    new ClassA(new RequestReferrerHandler(null, null, null, "Super Awesome Taxonomy Tables"), new SanitizerForClassA()), 
    new ClassB(new RequestReferrerHandler(null, null, "OK Google Now!", null), new SanitizerForClassB())
);
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

接口注入和公共类 的相关文章

随机推荐

  • 让人们在电影院就座

    这是基于我读到的一篇关于大型软件公司提出的谜题和面试问题的文章 但它有一个转折 一般问题 有一种算法可以让人们在电影院就座 让他们直接坐在朋友旁边 而不是敌人旁边 技术问题 给定一个 N M 网格 用 N M 1 项填充网格 每个项目都有一
  • 作业计划程序未在 Android N 上运行

    作业计划程序在 Android Marshmallow 和 Lollipop 设备上按预期工作 但在 Nexus 5x Android N 预览版 上未运行 安排作业的代码 ComponentName componentName new C
  • Chrome 堆快照——分离节点没有颜色

    我正在跟进本教程 https developers google com web tools chrome devtools memory problems 在 使用堆快照发现分离的 DOM 树内存泄漏 下 当我搜索分离节点时 我看到一堆
  • 如何在 Spring 中将对象添加到应用程序范围

    我们可以使用设置请求属性Model or ModelAndViewSpring 中的对象 我们可以用 SessionAttributes将属性保留在会话范围内 那么我怎样才能将属性放入applicationSpring中的作用域 sprin
  • 四叉树和Kd树

    我有一组不同位置的纬度和经度 也知道我当前位置的纬度和经度 我必须找出距离当前位置最近的地方 Kdtree 和四叉树中哪种算法最适合从纬度和经度集合中找出邻居位置 一种相对于另一种有什么优势 我们如何在 C 中将这些实现到算法中以实现上述目
  • 可以集成到 C# 应用程序中的 Diff 工具 [关闭]

    就目前情况而言 这个问题不太适合我们的问答形式 我们希望答案得到事实 参考资料或专业知识的支持 但这个问题可能会引发辩论 争论 民意调查或扩展讨论 如果您觉得这个问题可以改进并可能重新开放 访问帮助中心 help reopen questi
  • Swift - 从 ISO8601 日期字符串检索时区

    我以这种格式保存在数据库中的日期 yyyy MM dd T HH mm ssZ 例如 2018 05 17T11 15 00 0330 时区有所不同 以用户本地时区为准 我想检索并显示日期 如 2018 年 5 月 17 日上午 11 点
  • 选择 Scala Web 框架 [关闭]

    就目前情况而言 这个问题不太适合我们的问答形式 我们希望答案得到事实 参考资料或专业知识的支持 但这个问题可能会引发辩论 争论 民意调查或扩展讨论 如果您觉得这个问题可以改进并可能重新开放 访问帮助中心 help reopen questi
  • 每页新/更新图标

    认为有人可能知道那里的脚本 或者知道如何更新网站每个部分的图标 我正在开发的构建的每个部分都有强烈的颜色 我认为根据您所在的部分更新图标的颜色可能是一个很好的 UI 风格 这些可能是一系列图标 但会强制进行更改 而不仅仅是在每个部分中添加
  • 使用C++访问桌面上的文件

    我使用的是 Windows 7 我在桌面上创建了一个名为test txt 如何使用 C 访问该文件 例如 考虑以下代码 include
  • Jprofile可以连接到docker中运行的JVM

    我是 JProfiler 的新手 我最近遇到了一个问题 我的Java应用程序在docker中运行 这意味着JVM在docker中运行 但我的jprofile安装在主机上 我知道 jprofiler 必须连接到 JVM 那么 jprofile
  • 如何在不设置完整路径的情况下访问 C++ 中的资源

    我希望在程序中访问我的资源 但我不想使用包括 C Users USER EXAMPLE 的完整路径 在 java 中 可以选择使用 getClass getResources Folder test txt 我想做的事情是读取和写入文件 而
  • 关闭 JSP 时调用操作

    我是java网络世界的新手 所以如果我说一些愚蠢的话请原谅我 我正在使用 struts 2 当 jsp 关闭时 我需要删除一个文件 位于服务器上 有谁知道该怎么做吗 提前致谢 The window onunload建议很好 但不能保证 aj
  • Excel 2007 VBA问题设置轴标题

    我需要帮助在 Excel 2007 VBA 中设置 X 轴和 Y 轴标题 它一直抱怨 需要对象 Sub macro2 Dim xAxis As Axis icount 1 Charts Add Charts icount Name iske
  • 从基类指针访问派生私有成员函数到派生对象[重复]

    这个问题在这里已经有答案了 可能的重复 为什么我可以通过指向派生对象的基类指针访问派生私有成员函数 https stackoverflow com questions 3610936 why can i access a derived p
  • 如何设置IntelliJ IDEA项目SDK

    我刚刚安装了 IntelliJ IDEA 当我尝试创建我的第一个项目时 它要求我设置项目 SDK 当我单击 JDK 时 它要求我选择 JDK 的主目录 如下图所示 我无法找到它在哪里 对于新项目 选择 jdk 的主目录 eg C Java
  • 如何使用 tf2 为 seq2seq 构建自定义双向编码器?

    class Encoder tf keras Model def init self vocab size embedding dim enc units batch sz super Encoder self init self batc
  • 禁用 Android 幼儿应用程序中的主页按钮?

    我开发了一个应用程序 它是一个图片幻灯片放映 当您点击它们时 每个图片都会播放声音 这就像一本适合2 4岁孩子的图画书 问题是 由于 Android 不会让您捕获按下主页按钮并基本上禁用它 因此当父母将手机交给孩子与无人看管 勇敢的父母 一
  • 非 Spring Boot 应用程序中的 hystrix @EnableCircuitBreaker

    我有一个基于应用程序上下文 xml 文件的简单 spring 项目 非 spring boot 如何为我的客户端使用 EnableCircuitBreaker 以便在我的 RestTemplate 中使用 hystrix 命令进行回退 En
  • 接口注入和公共类

    我正在尝试了解 OOP 原则并编写自己的类 作为一种学习方法 我决定将我在 Wordpress 中编写的几个函数转换为 OOP 类 这些函数协同工作 以便根据 URL 中设置的引荐来源网址 其中 4 个 在单个页面上输出正确的帖子链接 这是