接口注入和通用类 [英] Interface injection and common classes

查看:87
本文介绍了接口注入和通用类的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在努力了解OOP原则并编写自己的类.作为一种学习手段,我决定将我在Wordpress中编写的几个函数转换为OOP类.这些功能可以一起使用,以便根据URL中设置的引荐来源网址(其中的4个)在单个页面上输出正确的帖子链接.

I'm trying to get my head around the OOP principles and coding my own classes. As a means to learn, I have decided to convert a couple of functions I have written in Wordpress to OOP classes. These functions work together in order to output the correct post links on single pages according to referrers (4 of them) set in the URL.

这是带有基本工作流程的设置(工作流程会随着我的前进而改变):

This is the setup with a basic workflow (The workflow can change as I go along):

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

4 query variables are set to the URL according to archive page, ie, one query variable for taxonomy pages, one query variable set for the author pages and so one. No page can ever have more than one custom query variable. This 4 variables is retrieved by my first class and checked against a given global variable, in this case $_GET. I have not hardcoded the 4 variables in my class, and this goes for $_GET as well to keep the class testable. If the value exists in the URL, the key/value pair is returned through the has* methods. These methods return null if no match is found. (this is raw data which will be sanitized/escaped by the classes that will use this data)

这是完整的课程

<?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

For testing purposes you can inject something like ['aq' => '1'] into the class instead of $_GET

这是我现在停留的地方,不知道如何继续前进.我需要构造两个将使用与上述类相同的方法的类,一个将根据上述类的has*方法构造查询参数的类,并且一个类也将根据has*创建query_vars上面类中的方法将用于构建新的帖子链接

This is where I'm stuck now and have no idea how to move on. I need to construct two classes which will both use the same methods from the class above, one class that will construct query arguments from the has* methods from the above class, and one class will create query_vars also from the has* methods from the above class that will be used to construct new post links

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

So, in short, both classes will make use of the exact same for methods from the above class

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

仅作为示例,这是两个类的外观示例. (我在这里省略了一些使代码更易于管理的方法)

Just as an example, here is an example of how the two classes should look like. (I have omitted some of the methods here to make the code more manageable)

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
    }

}

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

The queryArguments() method from ClassA and the queryVars() method from ClassB will be used in other classes (or one controller class)

我完全缺乏对OOP的专业知识,与关注点分离,封装,SOLID原则以及保持类可测试性的混淆,这让我第二次猜测了我的代码,而且我确实感到自己缺少一些东西.

My total lack of proper knowledge coming into OOP, confusion with separation of concerns, encapsulation, SOLID principles and keeping class testable have me second guessing my code, and I do feel I am missing something.

无论如何,我可以优化上面的内容.我不要求编写任何类型的代码,我所需要的只是正确的指针和想法,以优化它,以使其不符合标准.如果有人可以提供代码示例(例如轮廓框架),那将是一个真正的加分

Is there anyway I can optimize the above. I am not asking for any type of code rewritting, all I need is proper pointers and ideas on optimizing this to bring it up to standard if it is not. It would be a real plus if anyone can give code samples, something like an outline skeleton

推荐答案

查看您的代码,您绝对有一个良好的开端.在OOP中进行编程时,您已经使用了一个很好的经验法则-对接口进行编程,而不是对实现进行编程.用术语 interface 不仅指的是实际接口,但也包括抽象类.

Looking over your code you are definitely off to a good start. You are already using one good rule of thumb when programming in OOP - program to an interface, not an implementation. By the term interface I'm not referring only to actual interfaces, but abstract classes as well.

因此,在您的问题中心,您想拥有两个类,分别使用RequestReferrerHandler中的通用方法的ClassAClassB.您已经准备好使用接口RequestReferrerHandlerInterface进行此操作的基础工作.因此,我们将说您拥有一个如下所示的界面:

So at the heart of your question you want to have two classes, ClassA and ClassB that both use common methods from RequestReferrerHandler. You already have the ground work laid out to do that with your interface RequestReferrerHandlerInterface. So we'll say that you have an interface that looks like this:

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

只要此接口由RequestReferrerHandler实现,您就可以键入hint接口作为ClassAClassB的构造函数要求.但这并不是什么新鲜事物,因为您已经在执行此操作.

So long as this interface is implemented by the RequestReferrerHandler you can type hint the interface as the constructor requirements for ClassA and ClassB. But this isn't anything new because you were already doing this.

特别有两件事使我成为潜在的酸痛拇指.首先,由于您希望减小类的职责,因此您应该承担将数据提供给RequestReferrerHandler的责任,并将其提供给您的Controller.换句话说,请勿将$_GET注入您的班级.确保您的Controller具有正确创建RequestReferrerHandler所需的所有信息.让我们看一下您的RequestReferrerHandler类,其中已包含所有需要的方法.

There are two things in particular that stand out at me as potential sore thumbs. First, since you want the responsibilities of your classes to be small, you should take the responsibility of providing data to the RequestReferrerHandler away from itself and give it to your Controller. In other words, do not inject $_GET into your class. Make sure your Controller has all the information it needs to properly create the RequestReferrerHandler Let's take a look at your RequestReferrerHandler class, flushed with all of the methods it will need.

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 (不要重复自己)原则可以为您提供帮助.由于多个类可能必须以类似的方式清理数据,因此有必要将其从类中抽象出来.

The second thing that sticks out is the santize() methods. Do you see how they are duplicated in both ClassA and ClassB? The sanitizeAuthor() is different among the two classes, but how about the rest? This is a case where the DRY (Don't Repeat Yourself) principle can help out. Since multiple classes may have to sanitize data in similar ways it make sense to abstract that away from your classes.

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

Let's take a look at how to do that and then we will come back to your concrete classes. First create a new interface that will specify methods that must be exposed by an object that can sanitize data.

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

现在,如果您拥有ClassX的每个对象都以不同的方式实现了这四个方法,则可以在简单地清理数据的不同类中开始实现它.但是,对于此示例,我们将说并非如此.让我们假设sanitizeAuthor()ClassAClassB之间可能有所不同(在您的代码中),并且所有其他方法都将完全相同地实现.在这种情况下,我们可以使用将实现消毒剂方法的抽象类.

Now, if every object you had of ClassX implemented these four methods in different ways you could start implementing it in different classes that simply sanitize data. However, for this example we will say that is not the case. Let's make the assumption that sanitizeAuthor() could be different among ClassA and ClassB (which it is in your code) and all the other methods will be implemented exactly the same. This is case where we can use an abstract class that will implement the sanitizer methods.

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的方法.

Some things to take note of right off the bat. First, you'll notice there is the setHandler() method which accepts an instance of the RequestReferrerHandlerInterface. Why is this there? Convenience for the most part. Since we have taken the sanitizing behavior and encapsulated it into its own class it would be nice it we gave the sanitizer a way to update the concrete RequestReferrerHandler it is using with the updated output from a sanitize method.

接下来,我们正在使用RequestReferrerHandler类中未在RequestReferrerHandlerInterface中指定的方法.这本身不是一个直接的问题,因为我们知道诸如getter和setter之类的方法都在该类中.但是,仅对接口进行类型提示并不能保证,如果您决定使用其他具体对象来实现该接口,则这些方法将可用.因此,我们需要使用保证其可用性的方法来更新RequestReferrerHandlerInterface.

Next thing, we are using methods from the RequestReferrerHandler class that are not specified in the RequestReferrerHandlerInterface. This isn't an immediate problem per se, because we know that methods like the getters and setters are in the class. However, type hinting to the interface alone doesn't guarantee that those methods will be available if you ever decided to implement that interface with a different concrete object. Therefore, we need to update the RequestReferrerHandlerInterface with methods that will guarantee their availability.

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的创建方式是因为SanitizerInteface中的sanitizeAuthor()方法未在AbstractSanitizer中实现,因此我们必须对其进行扩展以提供功能.我们将需要以下两个类来做到这一点:

Now, back to those sanitizers. We know that ClassA and ClassB will implement their sanitizeAuthor() methods differently. The abstract class AbstractSanitizer was made the way it was because the sanitizeAuthor() method from the the SanitizerInteface isn't implemented in AbstractSanitizer so we have to extend it to provide the functionality. We will need the following two classes to do this:

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方法中的数据.

These two concrete classes can be used with ClassA and ClassB to sanitize data within the concrete RequestReferrerHandler methods that will be passed into them.

接下来,让我们看一下ClassAClassB的规格.我们知道ClassA将需要方法queryArguments()ClassB将需要方法queryVars(),并且这两个类都将需要允许在其构造函数中使用RequestReferrerHandlerInterfaceSanitizerInterface的实例.我们将用一个接口处理构造函数要求,然后扩展另外两个接口以提供ClassAClassB所需的所有方法要求.

So moving on, let's look at the spec for ClassA and ClassB. We know that ClassA will need the method queryArguments(), ClassB will need the method queryVars() and both classes will need to allow and instance of a RequestReferrerHandlerInterface and SanitizerInterface in their constructors. We will handle the constructor requirement with one interface, then two other interfaces will extend that to provide all the method requirements needed for 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();
}

由于我们现在要讲究它,所以让我们看一下将使用这些类的那些类.

Since we are now getting down to it, let's take a look at those classes that will use these.

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
    }
}

有了它,便完成了基础工作.您会注意到,在构造函数中,为给定的处理程序和sanitizer类设置了属性,然后为sanitizer提供了对处理程序的引用. (请记住,清理程序具有对处理程序的引用,以便处理程序中经过清理的属性会自动更新.各个类现在无需担心.)

There you have it, the ground work is built. You'll notice that in the constructors properties are set for the handler and sanitizer class that was given and then the sanitizer is given the reference to the handler. (Remember, the sanitizers have a reference to the handler so that sanitized properties in the handler are automatically updated. The individual classes don't need to worry about that now.)

因此,现在百万美元的问题是如何使用它.好吧,您需要一个可以接受ClassAClassB的控制器.我们还将通过它们各自的界面键入提示.

So now the million dollar question is how to use this. Well, you need a controller that can accept ClassA and ClassB. We will type hint these by their respective interfaces as well.

class Controller
{
    public function __construct() {}

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

在您的queryArguments()queryVars()版本中,您希望对返回值进行清理后的数据.让我们插入一些数据,看看会得到什么. (注意:您已经发现我使用的任何一种消毒方法都无法完成您的工作,它们只是说明性的.)

In your version of queryArguments() and queryVars() you expected sanitized data for a return value. Let's plug some data in and see what we get. (Note: As you already figured out none of the sanitize methods I used are doing what you were doing, they are illustrative only.)

//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个抽象类和几个具体类才能将少量数据输出到屏幕.

So what did all of this cost you? Short answer - complexity. It took 4 interfaces, 1 abstract class and a handful of concrete classes to output a little bit of data to the screen.

您将获得什么?简短答案-灵活性.将来,您可能希望添加更多实现QueryVarsInterfaceQueryArgumentsInterface的类.考虑这些类ClassCClassDClassE.所有这些类都需要一个消毒剂类来配合使用(也就是说,如果SanitizerForClassASanitizerForClassB不符合要求),而不得不继续编写消毒剂类将很繁琐.好吧,对您来说很高兴,因为您一直在为接口编程,所以您不会遇到问题.您可以使用sanitizeAuthor()方法的默认实现轻松创建GenericSanitizer.在不需要特殊的消毒剂类的任何情况下,Use都可以将此类与Controller::doStuff()一起使用.您可以轻松地实现QueryArgumentInterfaceQueryVarsInterface的不同具体类来测试要添加的实验功能,而不会破坏当前的类.

What do you gain? Short answer - flexibility. In the future you may wish to add more classes that implement either the QueryVarsInterface or QueryArgumentsInterface. Consider these classes ClassC, ClassD and ClassE. All of these classes will need a sanitizer class to go with them (that is if SanitizerForClassA or SanitizerForClassB do not fit the bill) and it would be tedious to keep having to write sanitizer classes. Well, good thing for you, since you were programming to an interface all along, you won't have that problem. You can easily make a GenericSanitizer with a default implementation of the sanitizeAuthor() method. Use can use this class with Controller::doStuff() in any case where you don't need a specialized sanitizer class. You could just as easily implement different concrete classes of QueryArgumentInterface or QueryVarsInterface to test out experimental features you want to add without tampering with your current classes.

希望这使您对某些OOP原则有所了解.这是上面所有代码的完整副本.将其放在一个空的PHP文件中,然后运行它以查看所有操作.编程愉快!

Hopefully this has given you some insight on some OOP principles. Here is a complete copy of all of the code above. Slap this in an empty PHP file and run it to see everything in action. Happy programming!

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

这篇关于接口注入和通用类的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆