接口注入和通用类

时间:2015-02-24 19:27:06

标签: php wordpress oop

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

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

enter image description here

根据归档页面将4个查询变量设置为URL,即分类页面的一个查询变量,作者页面的一个查询变量集合等等。任何页面都不能有多个自定义查询变量。这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
    }

}

queryArguments()中的ClassA方法和queryVars()中的ClassB方法将用于其他类(或一个控制器类)

我完全缺乏适当的知识进入OOP,混淆分离关注点,封装,SOLID原则和保持类可测试让我第二次猜测我的代码,我觉得我错过了一些东西。

无论如何,我可以优化以上内容。我不是要求任何类型的代码重写,我需要的是关于优化这一点的正确指针和想法,如果不是,则将其提升到标准。如果任何人都可以提供代码示例,比如大纲骨架

,那将是一个真正的好处

4 个答案:

答案 0 :(得分:11)

查看你的代码,你肯定是一个良好的开端。在使用OOP进行编程时,您已经使用了一条经验法则 - 将程序编程到接口,而不是实现。术语接口我不仅仅指代实际接口,但也是抽象类。

因此,在您的问题的核心,您希望有两个类ClassAClassB,它们都使用RequestReferrerHandler中的常用方法。您已经完成了使用界面RequestReferrerHandlerInterface完成的基础工作。所以我们说你有一个如下所示的界面:

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

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

有两件事特别突出,因为潜在的疼痛拇指。首先,由于您希望课程的职责很小,因此您应该负责向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()方法。您是否了解ClassAClassB中的重复方式? sanitizeAuthor()在两个班级中有所不同,但其余的怎么样?这是DRY (不要重复自己)原则可以提供帮助的情况。由于多个类可能必须以类似的方式清理数据,因此从类中抽象出来是有意义的。

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

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

现在,如果您拥有ClassX的每个对象以不同的方式实现这四种方法,您就可以开始在不同的类中实现它,只需简单地清理数据。但是,对于这个例子,我们会说并非如此。我们假设sanitizeAuthor()ClassA之间的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中指定的方法。这不是本身的直接问题,因为我们知道getters和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);
}

现在,回到那些消毒剂。我们知道ClassAClassB将以不同方式实施其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);
        }   
    }
}

这两个具体类可以与ClassAClassB一起使用,以清理将传递给它们的具体RequestReferrerHandler方法中的数据。

继续前进,让我们看一下ClassAClassB的规范。我们知道ClassA需要方法queryArguments()ClassB需要方法queryVars(),并且这两个类都需要允许RequestReferrerHandlerInterface和{的实例{1}}在他们的构造函数中。我们将使用一个接口处理构造函数需求,然后另外两个接口将扩展它以提供SanitizerInterfaceClassA所需的所有方法要求。

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的控制器。我们也将通过它们各自的接口键入提示。

ClassB

在您的class Controller { public function __construct() {} public function doStuff(QueryArgumentsInterface $argsClass, QueryVarsInterface $varsClass) { var_dump($argsClass->queryArguments()); var_dump($varsClass->queryVars()); } } 版本和queryArguments()版本中,您需要清理返回值的数据。让我们插入一些数据,看看我们得到了什么。 (注意:正如您已经发现我使用的所有清理方法都没有做您正在做的事情,它们只是说明性的。)

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())
);

那么所有这些费用是什么?简短回答 - 复杂性。它花了4个接口,1个抽象类和一些具体的类来向屏幕输出一些数据。

你获得了什么?简短回答 - 灵活性。将来您可能希望添加更多实现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) QueryVarsInterface的类。请考虑这些类QueryArgumentsInterfaceClassCClassD。所有这些类都需要一个清理程序类来与它们一起使用(即如果ClassESanitizerForClassA不适合该法案)并且继续编写清洁剂类将是繁琐的。嗯,对你好,因为你一直在编程接口,你不会遇到这个问题。您可以使用SanitizerForClassB方法的默认实现轻松创建GenericSanitizer。在您不需要专门的消毒剂类的任何情况下,使用此类可以使用sanitizeAuthor()。您可以轻松实现Controller::doStuff()QueryArgumentInterface的不同具体类来测试您想要添加的实验性功能,而不会篡改您当前的类。

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

QueryVarsInterface

答案 1 :(得分:2)

正如我在previous questions中所看到的,您正在寻找一种方法来合理化您的OOP开发。这就是为什么我不会给你一条鱼,但我会帮你自己钓鱼。这意味着我将(尝试)给出你应该知道的基础来做一个强大的OOP代码。

<强> 1。 SRP和组合

正如我在您的问题中所看到的,您尝试将课程的责任分开。当然,这在OOP中是件好事。你所做的是Single Responsability Principle (SRP)。这个原则意味着你更喜欢构图而不是继承。

// Composition
class Car implements VehicleInterface
{
    private $motor;
}

class Motor implements MotorInterface

在这种情况下,汽车和电机有两种不同的响应能力。

// Inheritance
class Car extends MotorVehicle
{

}

在继承的情况下,您可以在车辆和电机概念之间进行高度比较。

想象一下,我希望有一个像运动这样的新概念:

// Composition
class Car implements VehicleInterface
{
    private $motor;
    private $movement;
}

class Motor implements MotorInterface

class Drive implements MovementInterface

构图没问题。

// Inheritance
class Car extends MotorVehicle, DriveVehicle
{

}

多重继承很糟糕(甚至在PHP中也不可能)因为你打破了SRP。继承只应用于为具有相同责任的类的代码分解。由于您应该只有一个类的责任,因此不应使用多重继承。其他可能性很糟糕,因为你不能告诉我汽车更像MotorVehicle而不是DriveVehicle

  

在您的情况下,您有一些请求引荐来源处理程序和一些清理程序

<强> 2。接口和低耦合

正如我在previous answer中告诉你的那样,在使用界面在你的班级之间制作low coupling时,你做对了。这为您提供了更可维护,可扩展且可测试的代码。以前面的例子为例:

class Car implements VehicleInterface
{
    private $motor;
}

class PetrolMotor implements MotorInterface

class DieselMotor implements MotorInterface

你的车现在可以很容易地采用不同类型的电机了!

这个想要引起你注意的想法是,一个类不应该直接使用另一个类,而是一个描述行为的接口。

  

在您的情况下,class Aclass B应实现接口SanitizerInterface

第3。依赖注入

此时,您要在清洁剂中设置处理程序。更好的方法是使用dependency injection。这真的很简单!

class Car implements VehicleInterface
{
    private $motor;

    public function __construct(MotorInterface $motor)
    {
        $this->motor = $motor;
    }
}

class PetrolMotor implements MotorInterface
{
}

class DieselMotor implements MotorInterface
{
}

$motor = new PetrolMotor();
$car = new Car($motor);
  

在您的情况下,您必须在清理程序中注入请求引荐来源处理程序

<强> 4。 SOA

应用于OOP的面向服务的体系结构(SOA)是合理化代码的好方法。

// Standard OOP
$car = new Car();
$buyer = new Person();
$store = new Store();

// Many solutions to buy a car!
$buyer->buy($car, $store);
// or
$store->sell($car, $buyer);
// or
// ...

在标准OOP中,您经常会遇到重复的代码。我应该在哪里编写这种方法?我现在可以在哪里(或其他人)已编码此方法?以前这个问题对我来说太无聊了!我无法使我的发展合理化。

// SOA
$car = new Car();
$buyer = new Person();
$store = new Store();

$saleHandler = new SaleHandler();
$saleHandler->sell($car, $buyer, $store);

在SOA中,您有一个“服务类”(此处为SaleHandler)(基本上实现为singleton),它处理“数据类”的操作(此处为Car,{ {1}}和Person)。您的数据类中没有智能(您通常只在属性上有getter和setter)。这样,您就知道您的销售代码在哪里了!

  

在您的情况下,您的请求引荐来源处理程序清理程序似乎是某种服务,所以没关系。

<强>结论

总之,您直观地使用了一些非常好的OOP实践。现在,您可以应用它们并知道原因!

但是,我强烈建议您尝试像Symfony2这样的框架。它将为您提供一个强大的PHP开发基础和一个非常好的dependency injection component,允许您在配置文件中定义类的所有依赖项,以获得真正的动态代码。它还可以帮助您使用其服务进行SOA。

使用框架是一件好事,可以为您的开发和职业生活提供动力(开发人员知道框架更受追捧)。由于PHP框架主要是开源,您也可以参与并为招聘人员提供良好的可见性。

答案 2 :(得分:1)

如果您想将RequestReferrerHandler类的同一对象用于ClassA和ClassB,那么您的策略是正确的。只需要使用RequestReferrerHandler类的对象来实例化ClassA和ClassB。然后,您可以访问特定的方法。即ClassA.queryArguments()ClassB.queryVars()

如果要为ClassA和ClassB创建RequestReferrerHandler类的单独对象,可以将RequestReferrerHandler类扩展为ClassA和ClassB,而无需定义构造函数。因此,当您创建ClassA的对象时,它会自动继承RequestReferrerHandler类的构造函数方法,您可以通过parent:关键字访问属性和方法。例如:

class ClassA extends RequestReferrerHandler
{

public function santizeAuthor()
{

    $author = parent::hasAuthorReferrerValue();  // access the base class method

    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 = parent::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一样做。现在,您可以为ClassA和ClassB创建对象,在C类中分配构造函数的基类参数,并使用其对象的ClassA.queryArguments()ClassB.queryVars()的返回值。

答案 3 :(得分:0)

如果您专注于数据类型而不是我在api中工作的对象的含义,能够操作并从Wordpress结构中获取信息,并且我知道他们已经做了很多很好的工作简化了他们的数据结构。所以你只需要考虑后期,后期元和分类法(索引),而不是像(出版物,税收,图像,图书馆,帖子,产品)这样的含义。

即使在woo commerce中,你也可以看到它非常简单。

你会看到;产品是一种帖子,产品的数据在后元。图像是一种帖子,该图像的细节在post_meta中,新闻是帖子,而后新闻中的新闻的详细信息。

一种结构不同的含义。因此,为了访问所有内容或存储数据,您只需要这些元素。如果您能够以一种宁静的方式访问这些元素,那么实现起来非常容易。

我希望这可以帮助你一些我的观点,你可以不这么认为。但我认为与你分享他很重要。