接口注入和通用类
Interface injection and common classes
我正在努力了解 OOP 原则并编写自己的 classes。作为学习的一种方式,我决定将我在 Wordpress 中编写的几个函数转换为 OOP classes。这些功能协同工作,以便根据 URL.
中设置的引荐来源网址(其中 4 个)在单个页面上输出正确的 post 链接
这是具有基本工作流程的设置(工作流程可以随着我的进行而改变):
4个查询变量根据存档页面设置为URL,即分类页面的一个查询变量,作者页面的一个查询变量集等等。任何页面都不能拥有多个自定义查询变量。这 4 个变量由我的第一个 class 检索,并根据给定的全局变量进行检查,在本例中为 $_GET
。我没有在我的 class 中硬编码 4 个变量,这也适用于 $_GET
以及保持 class 可测试。如果值存在于 URL 中,key/value 对将通过 has*
方法 returned。这些方法 return null
如果找不到匹配项。 (这是原始数据,sanitized/escaped 将由 class 将使用此数据)
这是完整的class
<?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;
}
}
我就是这样使用的class
$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']
之类的内容注入 class 而不是 $_GET
这就是我现在被困的地方,不知道如何继续前进。我需要构造两个 classes,它们都将使用上面 class 中的相同方法,一个 class 将从上面 has*
方法构造查询参数 class,一个 class 也将从上述 class 中的 has*
方法创建 query_vars
,将用于构建新的 post 链接
所以,简而言之,两个 classes 将对上述 class
中的方法使用完全相同的方法
hasAuthorReferrerValue();
hasDateReferrerValue();
hasSearchReferrerValue();
hasTaxReferrerValue();
举个例子,下面是两个 class 应该是什么样子的例子。 (为了代码更易管理,这里省略了一些方法)
A类
<?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
}
}
B级
<?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()
方法将用于其他classes(或一个控制器class)
我对 OOP 完全缺乏适当的知识,对关注点分离、封装、SOLID 原则和保持 class 可测试感到困惑,这让我再次猜测我的代码,我确实觉得我遗漏了一些东西。
有没有我可以优化上面的。我不要求重写任何类型的代码,我所需要的只是适当的指导和想法来优化它以使其达到标准(如果不是)。如果有人能提供代码示例,比如大纲骨架,那将是一个真正的优势
如果你想用RequestReferrerHandler
class到ClassA和ClassB的同一个对象,那么你的策略是正确的。只需要使用RequestReferrerHandler
class的对象实例化ClassA和ClassB即可。然后您可以访问特定的方法。即 ClassA.queryArguments()
或 ClassB.queryVars()
如果你想为 ClassA 和 ClassB 创建单独的 RequestReferrerHandler
class 对象,你可以扩展 RequestReferrerHandler
class 到 ClassA 和 ClassB 而无需定义构造函数。所以,当你创建ClassA的对象时,它会自动继承RequestReferrerHandler
class的构造方法,你可以通过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 创建对象,在 Class C 中分配其基数 class 的构造函数参数,并使用 return 的值 ClassA.queryArguments()
或 ClassB.queryVars()
来自他们的对象。
查看您的代码,您肯定有了一个良好的开端。在 OOP 中编程时,您已经在使用一个好的经验法则 - 编程到接口,而不是实现。 术语 接口 我是不仅指实际接口,还指抽象 classes。
所以在你的问题的核心,你想要有两个 classes,ClassA
和 ClassB
,它们都使用 RequestReferrerHandler
中的常用方法。您已经为您的界面 RequestReferrerHandlerInterface
做好了准备。所以我们会说你有一个看起来像这样的界面:
interface RequestReferrerHandlerInterface
{
public function hasAuthorReferrerValue();
public function hasDateReferrerValue();
public function hasSearchReferrerValue();
public function hasTaxReferrerValue();
}
只要此接口由 RequestReferrerHandler
实现,您就可以键入提示接口作为 ClassA
和 ClassB
的构造函数要求。但这并不是什么新鲜事,因为您已经在这样做了。
有两件事特别让我印象深刻,因为它们可能会让人大跌眼镜。首先,既然你希望你的classes的职责很小,你就应该承担起为RequestReferrerHandler
提供数据的责任,远离它自己,交给你的Controller
。换句话说,不要将 $_GET
注入到你的 class 中。确保您的 Controller
拥有正确创建 RequestReferrerHandler
所需的所有信息 让我们来看看您的 RequestReferrerHandler
class,其中包含了它需要的所有方法。
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
和 ClassB
中是如何重复的了吗? sanitizeAuthor()
在两个 class 中是不同的,但其余的呢?在这种情况下,DRY (Don't Repeat Yourself) 原则可以提供帮助。由于多个 classes 可能必须以类似的方式清理数据,因此将其从 classes 中抽象出来是有意义的。
让我们来看看如何做到这一点,然后我们将回到您的具体 classes。首先创建一个新接口,该接口将指定必须由可以清理数据的对象公开的方法。
interface SanitizerInterface
{
public function sanitizeAuthor();
public function sanitizeDate();
public function sanitizeSearch();
public function sanitizeTaxonomy();
}
现在,如果您 ClassX
的每个对象都以不同的方式实现了这四种方法,您就可以开始在不同的 class 中实现它,它们只是清理数据。然而,对于这个例子,我们会说情况并非如此。让我们假设 sanitizeAuthor()
在 ClassA
和 ClassB
之间可能不同(它在您的代码中)并且所有其他方法将完全相同地实现。在这种情况下,我们可以使用抽象 class 来实现消毒方法。
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
的实例。为什么会有这个?大多数情况下方便。由于我们已经采取了清理行为并将其封装到它自己的 class 中,如果我们为清理程序提供了一种方法来更新它正在使用的具体 RequestReferrerHandler
以及来自清理方法的更新输出,那就太好了。
接下来,我们将使用 RequestReferrerHandler
class 中未在 RequestReferrerHandlerInterface
中指定的方法。这不是直接的问题 本身 ,因为我们知道像 getter 和 setter 这样的方法在 class 中。但是,如果您决定使用不同的具体对象实现该接口,仅对接口进行类型提示并不能保证这些方法可用。因此,我们需要使用能够保证其可用性的方法来更新 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
和 ClassB
会以不同的方式实现它们的 sanitizeAuthor()
方法。抽象 class AbstractSanitizer
之所以如此,是因为 SanitizerInteface
中的 sanitizeAuthor()
方法未在 AbstractSanitizer
中实现,因此我们必须扩展它提供功能。我们将需要以下两个 classes 来执行此操作:
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);
}
}
}
这两个具体的 classes 可以与 ClassA
和 ClassB
一起使用,以在将传递给它们的具体 RequestReferrerHandler
方法中清理数据。
那么继续,让我们看看 ClassA
和 ClassB
的规范。我们知道 ClassA
需要方法 queryArguments()
,ClassB
需要方法 queryVars()
并且两个 class 都需要允许 RequestReferrerHandlerInterface
和 SanitizerInterface
在它们的构造函数中。我们将使用一个接口处理构造函数需求,然后另外两个接口将扩展该接口以提供 ClassA
和 ClassB
所需的所有方法需求。
interface SanitizableHandlerInterface
{
public function __construct(RequestReferrerHandlerInterface $handler, SanitizerInterface $sanitizer);
}
interface QueryVarsInterface extends SanitizableHandlerInterface
{
public function queryVars();
}
interface QueryArgumentsInterface extends SanitizableHandlerInterface
{
public function queryArguments();
}
既然我们现在要开始了,让我们来看看那些将使用这些的 classes。
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 设置了属性,然后向消毒程序提供了对处理程序的引用。 (请记住,清理程序有一个对处理程序的引用,因此处理程序中经过清理的属性会自动更新。个人 class 现在不需要担心这个。)
所以现在百万美元的问题是如何使用它。那么,您需要一个可以接受 ClassA
和 ClassB
的控制器。我们也将通过它们各自的接口键入提示。
class Controller
{
public function __construct() {}
public function doStuff(QueryArgumentsInterface $argsClass, QueryVarsInterface $varsClass)
{
var_dump($argsClass->queryArguments());
var_dump($varsClass->queryVars());
}
}
在您的 queryArguments()
和 queryVars()
版本中,您期望 return 值的净化数据。让我们插入一些数据,看看我们得到了什么。 (注意:正如您已经了解到我使用的 none 消毒方法正在做您正在做的事情,它们只是说明性的。)
//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 个抽象 class 和一些具体的 classes 将一点点数据输出到屏幕。
您获得了什么? 简短回答 - 灵活性。将来您可能希望添加更多实现 QueryVarsInterface
或 QueryArgumentsInterface
的 classes。考虑这些 classes ClassC
、ClassD
和 ClassE
。所有这些 classes 都需要一个消毒剂 class 来搭配它们(也就是说,如果 SanitizerForClassA
或 SanitizerForClassB
不符合要求)并且保留它们会很乏味必须编写消毒剂 classes。好吧,对你来说是件好事,因为你一直都是针对接口编程,所以你不会有这个问题。您可以使用 sanitizeAuthor()
方法的默认实现轻松创建 GenericSanitizer
。在不需要专门的消毒剂 class 的任何情况下,都可以将此 class 与 Controller::doStuff()
一起使用。您可以轻松实现 QueryArgumentInterface
或 QueryVarsInterface
的不同具体 classes 来测试您想要添加的实验性功能,而不会篡改您当前的 classes。
希望这能让您对某些 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())
);
正如我在您的 中看到的那样,您正在寻找一种使 OOP 开发合理化的方法。这就是为什么我不给你鱼,但我会帮助你自己钓鱼。这意味着我将(尝试)提供您应该知道的基础来编写强大的 OOP 代码。
1. SRP 和构成
正如我在您的问题中看到的那样,您试图将 class 的责任分开。这在 OOP 中当然是一件好事。你做的事情叫做Single Responsability Principle (SRP)。这个原则意味着你更喜欢组合而不是继承。
// Composition
class Car implements VehicleInterface
{
private $motor;
}
class Motor implements MotorInterface
在这种情况下,汽车和电机有 2 个不同的责任。
// 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。继承只能用于具有相同职责的 classes 的分解代码。由于 class 你应该只有一个责任,你不应该使用多重继承。其他可能性是不好的,因为你不能告诉我 Car 比 DriveVehicle
.
更 MotorVehicle
In your case, you have some request referrer handlers and some sanitizer.
2。接口和低耦合
正如我在你们 class 之间的 your do right when using interfaces to make a low coupling 中告诉你们的那样。这为您提供了更易于维护、可扩展和可测试的代码。拿前面的例子来说:
class Car implements VehicleInterface
{
private $motor;
}
class PetrolMotor implements MotorInterface
class DieselMotor implements MotorInterface
你的车现在可以轻松搭载不同类型的电机了!
这里应该驱使你的想法是,class 不应该直接使用另一个 class,而是一个描述行为的接口。
In your case, class A
and class B
should implement an interface 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);
In your case, you have to inject the request referrer handler in your sanitizer.
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 中,您有一个 "service class"(此处为 SaleHandler
)(基本上实现为 singleton),它处理 "data classes"(此处为 Car
、Person
和 Store
)。您的数据 classes 中没有智能(您通常只有属性的 getter 和 setter)。这样,您就知道您的销售代码在哪里了!
In your case, it seems that your request referrer handlers and sanitizers are some kind of services, so it's ok.
结论
总之,您凭直觉使用了一些非常好的 OOP 实践。现在,您可以应用它们并知道为什么!
但是,我强烈建议您尝试使用像 Symfony2. It will provide you with a strong base for you PHP development and a really nice dependency injection component 这样的框架,它允许您在配置文件中定义 classes 的所有依赖项,以获得真正的动态代码。它还将帮助您使用其服务进行 SOA。
使用框架对于推动您的开发和您的职业生涯是一件好事(了解框架的开发人员更受欢迎)。由于 PHP 框架主要是开源的,您也可以参与并为招聘人员提供良好的知名度。
如果你专注于数据类型而不是对象的含义,这个问题可以得到简化他们已经在简化数据结构方面做得很好。所以你只需要考虑 post、post 元和分类法(索引)的热量,而不是像(出版、税收、图像、图书馆、post、产品)这样的含义。
即使在woo commerce中你也可以看到它非常简单。
你会看到的;产品是 post 的类型,产品的数据在 post 元中。图像是 post 的类型,该图像的详细信息在 post_meta 中,新闻是 post 并且该新闻的详细信息在 post 元中。
一个结构不同的意思。因此,要访问所有内容或存储所有内容的数据,您只需要这些元素。如果您能够以 restful 的方式访问此元素,则实施起来非常容易。
我希望这对您有所帮助,您可以不这么想。但我认为与您分享他的想法很重要。
我正在努力了解 OOP 原则并编写自己的 classes。作为学习的一种方式,我决定将我在 Wordpress 中编写的几个函数转换为 OOP classes。这些功能协同工作,以便根据 URL.
中设置的引荐来源网址(其中 4 个)在单个页面上输出正确的 post 链接这是具有基本工作流程的设置(工作流程可以随着我的进行而改变):
4个查询变量根据存档页面设置为URL,即分类页面的一个查询变量,作者页面的一个查询变量集等等。任何页面都不能拥有多个自定义查询变量。这 4 个变量由我的第一个 class 检索,并根据给定的全局变量进行检查,在本例中为 $_GET
。我没有在我的 class 中硬编码 4 个变量,这也适用于 $_GET
以及保持 class 可测试。如果值存在于 URL 中,key/value 对将通过 has*
方法 returned。这些方法 return null
如果找不到匹配项。 (这是原始数据,sanitized/escaped 将由 class 将使用此数据)
这是完整的class
<?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;
}
}
我就是这样使用的class
$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']
之类的内容注入 class 而不是 $_GET
这就是我现在被困的地方,不知道如何继续前进。我需要构造两个 classes,它们都将使用上面 class 中的相同方法,一个 class 将从上面 has*
方法构造查询参数 class,一个 class 也将从上述 class 中的 has*
方法创建 query_vars
,将用于构建新的 post 链接
所以,简而言之,两个 classes 将对上述 class
中的方法使用完全相同的方法hasAuthorReferrerValue();
hasDateReferrerValue();
hasSearchReferrerValue();
hasTaxReferrerValue();
举个例子,下面是两个 class 应该是什么样子的例子。 (为了代码更易管理,这里省略了一些方法)
A类
<?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
}
}
B级
<?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()
方法将用于其他classes(或一个控制器class)
我对 OOP 完全缺乏适当的知识,对关注点分离、封装、SOLID 原则和保持 class 可测试感到困惑,这让我再次猜测我的代码,我确实觉得我遗漏了一些东西。
有没有我可以优化上面的。我不要求重写任何类型的代码,我所需要的只是适当的指导和想法来优化它以使其达到标准(如果不是)。如果有人能提供代码示例,比如大纲骨架,那将是一个真正的优势
如果你想用RequestReferrerHandler
class到ClassA和ClassB的同一个对象,那么你的策略是正确的。只需要使用RequestReferrerHandler
class的对象实例化ClassA和ClassB即可。然后您可以访问特定的方法。即 ClassA.queryArguments()
或 ClassB.queryVars()
如果你想为 ClassA 和 ClassB 创建单独的 RequestReferrerHandler
class 对象,你可以扩展 RequestReferrerHandler
class 到 ClassA 和 ClassB 而无需定义构造函数。所以,当你创建ClassA的对象时,它会自动继承RequestReferrerHandler
class的构造方法,你可以通过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 创建对象,在 Class C 中分配其基数 class 的构造函数参数,并使用 return 的值 ClassA.queryArguments()
或 ClassB.queryVars()
来自他们的对象。
查看您的代码,您肯定有了一个良好的开端。在 OOP 中编程时,您已经在使用一个好的经验法则 - 编程到接口,而不是实现。 术语 接口 我是不仅指实际接口,还指抽象 classes。
所以在你的问题的核心,你想要有两个 classes,ClassA
和 ClassB
,它们都使用 RequestReferrerHandler
中的常用方法。您已经为您的界面 RequestReferrerHandlerInterface
做好了准备。所以我们会说你有一个看起来像这样的界面:
interface RequestReferrerHandlerInterface
{
public function hasAuthorReferrerValue();
public function hasDateReferrerValue();
public function hasSearchReferrerValue();
public function hasTaxReferrerValue();
}
只要此接口由 RequestReferrerHandler
实现,您就可以键入提示接口作为 ClassA
和 ClassB
的构造函数要求。但这并不是什么新鲜事,因为您已经在这样做了。
有两件事特别让我印象深刻,因为它们可能会让人大跌眼镜。首先,既然你希望你的classes的职责很小,你就应该承担起为RequestReferrerHandler
提供数据的责任,远离它自己,交给你的Controller
。换句话说,不要将 $_GET
注入到你的 class 中。确保您的 Controller
拥有正确创建 RequestReferrerHandler
所需的所有信息 让我们来看看您的 RequestReferrerHandler
class,其中包含了它需要的所有方法。
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
和 ClassB
中是如何重复的了吗? sanitizeAuthor()
在两个 class 中是不同的,但其余的呢?在这种情况下,DRY (Don't Repeat Yourself) 原则可以提供帮助。由于多个 classes 可能必须以类似的方式清理数据,因此将其从 classes 中抽象出来是有意义的。
让我们来看看如何做到这一点,然后我们将回到您的具体 classes。首先创建一个新接口,该接口将指定必须由可以清理数据的对象公开的方法。
interface SanitizerInterface
{
public function sanitizeAuthor();
public function sanitizeDate();
public function sanitizeSearch();
public function sanitizeTaxonomy();
}
现在,如果您 ClassX
的每个对象都以不同的方式实现了这四种方法,您就可以开始在不同的 class 中实现它,它们只是清理数据。然而,对于这个例子,我们会说情况并非如此。让我们假设 sanitizeAuthor()
在 ClassA
和 ClassB
之间可能不同(它在您的代码中)并且所有其他方法将完全相同地实现。在这种情况下,我们可以使用抽象 class 来实现消毒方法。
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
的实例。为什么会有这个?大多数情况下方便。由于我们已经采取了清理行为并将其封装到它自己的 class 中,如果我们为清理程序提供了一种方法来更新它正在使用的具体 RequestReferrerHandler
以及来自清理方法的更新输出,那就太好了。
接下来,我们将使用 RequestReferrerHandler
class 中未在 RequestReferrerHandlerInterface
中指定的方法。这不是直接的问题 本身 ,因为我们知道像 getter 和 setter 这样的方法在 class 中。但是,如果您决定使用不同的具体对象实现该接口,仅对接口进行类型提示并不能保证这些方法可用。因此,我们需要使用能够保证其可用性的方法来更新 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
和 ClassB
会以不同的方式实现它们的 sanitizeAuthor()
方法。抽象 class AbstractSanitizer
之所以如此,是因为 SanitizerInteface
中的 sanitizeAuthor()
方法未在 AbstractSanitizer
中实现,因此我们必须扩展它提供功能。我们将需要以下两个 classes 来执行此操作:
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);
}
}
}
这两个具体的 classes 可以与 ClassA
和 ClassB
一起使用,以在将传递给它们的具体 RequestReferrerHandler
方法中清理数据。
那么继续,让我们看看 ClassA
和 ClassB
的规范。我们知道 ClassA
需要方法 queryArguments()
,ClassB
需要方法 queryVars()
并且两个 class 都需要允许 RequestReferrerHandlerInterface
和 SanitizerInterface
在它们的构造函数中。我们将使用一个接口处理构造函数需求,然后另外两个接口将扩展该接口以提供 ClassA
和 ClassB
所需的所有方法需求。
interface SanitizableHandlerInterface
{
public function __construct(RequestReferrerHandlerInterface $handler, SanitizerInterface $sanitizer);
}
interface QueryVarsInterface extends SanitizableHandlerInterface
{
public function queryVars();
}
interface QueryArgumentsInterface extends SanitizableHandlerInterface
{
public function queryArguments();
}
既然我们现在要开始了,让我们来看看那些将使用这些的 classes。
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 设置了属性,然后向消毒程序提供了对处理程序的引用。 (请记住,清理程序有一个对处理程序的引用,因此处理程序中经过清理的属性会自动更新。个人 class 现在不需要担心这个。)
所以现在百万美元的问题是如何使用它。那么,您需要一个可以接受 ClassA
和 ClassB
的控制器。我们也将通过它们各自的接口键入提示。
class Controller
{
public function __construct() {}
public function doStuff(QueryArgumentsInterface $argsClass, QueryVarsInterface $varsClass)
{
var_dump($argsClass->queryArguments());
var_dump($varsClass->queryVars());
}
}
在您的 queryArguments()
和 queryVars()
版本中,您期望 return 值的净化数据。让我们插入一些数据,看看我们得到了什么。 (注意:正如您已经了解到我使用的 none 消毒方法正在做您正在做的事情,它们只是说明性的。)
//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 个抽象 class 和一些具体的 classes 将一点点数据输出到屏幕。
您获得了什么? 简短回答 - 灵活性。将来您可能希望添加更多实现 QueryVarsInterface
或 QueryArgumentsInterface
的 classes。考虑这些 classes ClassC
、ClassD
和 ClassE
。所有这些 classes 都需要一个消毒剂 class 来搭配它们(也就是说,如果 SanitizerForClassA
或 SanitizerForClassB
不符合要求)并且保留它们会很乏味必须编写消毒剂 classes。好吧,对你来说是件好事,因为你一直都是针对接口编程,所以你不会有这个问题。您可以使用 sanitizeAuthor()
方法的默认实现轻松创建 GenericSanitizer
。在不需要专门的消毒剂 class 的任何情况下,都可以将此 class 与 Controller::doStuff()
一起使用。您可以轻松实现 QueryArgumentInterface
或 QueryVarsInterface
的不同具体 classes 来测试您想要添加的实验性功能,而不会篡改您当前的 classes。
希望这能让您对某些 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())
);
正如我在您的
1. SRP 和构成
正如我在您的问题中看到的那样,您试图将 class 的责任分开。这在 OOP 中当然是一件好事。你做的事情叫做Single Responsability Principle (SRP)。这个原则意味着你更喜欢组合而不是继承。
// Composition
class Car implements VehicleInterface
{
private $motor;
}
class Motor implements MotorInterface
在这种情况下,汽车和电机有 2 个不同的责任。
// 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。继承只能用于具有相同职责的 classes 的分解代码。由于 class 你应该只有一个责任,你不应该使用多重继承。其他可能性是不好的,因为你不能告诉我 Car 比 DriveVehicle
.
MotorVehicle
In your case, you have some request referrer handlers and some sanitizer.
2。接口和低耦合
正如我在你们 class 之间的
class Car implements VehicleInterface
{
private $motor;
}
class PetrolMotor implements MotorInterface
class DieselMotor implements MotorInterface
你的车现在可以轻松搭载不同类型的电机了!
这里应该驱使你的想法是,class 不应该直接使用另一个 class,而是一个描述行为的接口。
In your case,
class A
andclass B
should implement an interfaceSanitizerInterface
.
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);
In your case, you have to inject the request referrer handler in your sanitizer.
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 中,您有一个 "service class"(此处为 SaleHandler
)(基本上实现为 singleton),它处理 "data classes"(此处为 Car
、Person
和 Store
)。您的数据 classes 中没有智能(您通常只有属性的 getter 和 setter)。这样,您就知道您的销售代码在哪里了!
In your case, it seems that your request referrer handlers and sanitizers are some kind of services, so it's ok.
结论
总之,您凭直觉使用了一些非常好的 OOP 实践。现在,您可以应用它们并知道为什么!
但是,我强烈建议您尝试使用像 Symfony2. It will provide you with a strong base for you PHP development and a really nice dependency injection component 这样的框架,它允许您在配置文件中定义 classes 的所有依赖项,以获得真正的动态代码。它还将帮助您使用其服务进行 SOA。
使用框架对于推动您的开发和您的职业生涯是一件好事(了解框架的开发人员更受欢迎)。由于 PHP 框架主要是开源的,您也可以参与并为招聘人员提供良好的知名度。
如果你专注于数据类型而不是对象的含义,这个问题可以得到简化他们已经在简化数据结构方面做得很好。所以你只需要考虑 post、post 元和分类法(索引)的热量,而不是像(出版、税收、图像、图书馆、post、产品)这样的含义。
即使在woo commerce中你也可以看到它非常简单。
你会看到的;产品是 post 的类型,产品的数据在 post 元中。图像是 post 的类型,该图像的详细信息在 post_meta 中,新闻是 post 并且该新闻的详细信息在 post 元中。
一个结构不同的意思。因此,要访问所有内容或存储所有内容的数据,您只需要这些元素。如果您能够以 restful 的方式访问此元素,则实施起来非常容易。
我希望这对您有所帮助,您可以不这么想。但我认为与您分享他的想法很重要。