我试图了解OOP原则并编写自己的类。作为一种学习方法,我决定将我在Wordpress中编写的一些函数转换为OOP类。这些函数一起工作,以便根据URL中设置的引用(其中4个)在单个页面上输出正确的帖子链接。
这是具有基本工作流程的设置(工作流程可随着我的进展而改变):
根据归档页面将4个查询变量设置为URL,即分类页面的一个查询变量,作者页面的一个查询变量集合等等。任何页面都不能有多个自定义查询变量。这4个变量由我的第一个类检索并针对给定的全局变量进行检查,在本例中为$_GET
。我没有在我的类中硬编码4个变量,这也适用于$_GET
以保持类可测试。如果URL中存在该值,则通过has*
方法返回键/值对。如果未找到匹配项,这些方法将返回null
。 (这是将使用此数据的类进行清理/转义的原始数据)
这是完整的课程
<?php
namespace PG\Single\Post\Navigation;
/**
* Test set values against the super global given. Returns conditional properties
* which is boolean values. true is returned on success and false on failure.
*
* @param $superGlobalVar Super global to test the values against
* @param (string) $authorReferrer
* @param (string) $dateReferrer
* @param (string) $searchReferrer
* @param (string) $taxReferrer
*/
class RequestReferrerHandler implements RequestReferrerHandlerInterface
{
/**
* @since 1.0.0
* @access protected
* @var (array) $superGlobalVar
*/
protected $superGlobalVar;
/**
* @since 1.0.0
* @access protected
* @var (string) $authorReferrer
*/
protected $authorReferrer;
/**
* @since 1.0.0
* @access protected
* @var (string) $dateReferrer
*/
protected $dateReferrer;
/**
* @since 1.0.0
* @access protected
* @var (string) $searchReferrer
*/
protected $searchReferrer;
/**
* @since 1.0.0
* @access protected
* @var (string) $taxReferrer
*/
protected $taxReferrer;
/**
* Public constructor method.
*
* @param $superGlobalVar Super global to get data from
* @param $authorReferrer Query variable from author referrer to test
* @param $dateReferrer Query variable from date referrer to test
* @param $searchReferrer Query variable from search referrer to test
* @param $taxReferrer Query variable from taxonomy referrer to test
*/
public function __construct($superGlobalVar = null, $authorReferrer= null, $dateReferrer = null, $searchReferrer = null, $taxReferrer = null)
{
$this->superGlobalVar = $superGlobalVar;
$this->authorReferrer = $authorReferrer;
$this->dateReferrer = $dateReferrer;
$this->searchReferrer = $searchReferrer;
$this->taxReferrer = $taxReferrer;
}
/**
* Setter setSuperGlobalVar.
*
* @since 1.0.0
* @param $superGlobalVar
* @return $this
*/
public function setSuperGlobalVar($superGlobalVar)
{
$this->superGlobalVar = $superGlobalVar;
return $this;
}
/**
* Returns an array of super global variables.
*
* @since 1.0.0
* @return (array) $this->superGlobalVar
*/
public function getSuperGlobalVar()
{
return $this->superGlobalVar;
}
/**
* Setter setAuthorReferrer
*
* @since 1.0.0
* @param $authorReferrer
* @return $this
*/
public function setAuthorReferrer($authorReferrer)
{
$this->authorReferrer = $authorReferrer;
return $this;
}
/**
* Returns the value of the $authorReferrer property.
*
* @since 1.0.0
* @return (array) $this->authorReferrer
*/
public function getAuthorReferrer()
{
return $this->authorReferrer;
}
/**
* Setter setDateReferrer.
*
* @since 1.0.0
* @param $dateReferrer
* @return $this
*/
public function setDateReferrer($dateReferrer)
{
$this->dateReferrer = $dateReferrer;
return $this;
}
/**
* Returns the value of the $dateReferrer property.
*
* @since 1.0.0
* @return (array) $this->dateReferrer
*/
public function getDateReferrer()
{
return $this->dateReferrer;
}
/**
* Setter setSearchReferrer.
*
* @since 1.0.0
* @param $searchReferrer
* @return $this
*/
public function setSearchReferrer($searchReferrer)
{
$this->searchReferrer = $searchReferrer;
return $this;
}
/**
* Returns the value of the $searchReferrer property.
*
* @since 1.0.0
* @return (array) $this->searchReferrer
*/
public function getSearchReferrer()
{
return $this->searchReferrer;
}
/**
* Setter setTaxReferrer.
*
* @since 1.0.0
* @param $taxReferrer
* @return $this
*/
public function setTaxReferrer($taxReferrer)
{
$this->taxReferrer = $taxReferrer;
return $this;
}
/**
* Returns the value of the $taxReferrer property.
*
* @since 1.0.0
* @return (array) $this->taxReferrer
*/
public function getTaxReferrer()
{
return $this->$taxReferrer;
}
/**
* Test $authorReferrer against $superGlobalVar.
*
* @since 1.0.0
* @return (bool) true on success or false on failure
*/
public function isAuthorReferrer()
{
if ($this->authorReferrer && isset($this->superGlobalVar[$this->authorReferrer])) {
$isAuthorReferrer = true;
} else {
$isAuthorReferrer = false;
}
return $isAuthorReferrer;
}
/**
* Test $authorReferrer against $superGlobalVar
*
* @since 1.0.0
* @return (bool) true on success or false on failure
*/
public function isDateReferrer()
{
if ($this->dateReferrer && isset($this->superGlobalVar[$this->dateReferrer])) {
$isDateReferrer = true;
} else {
$isDateReferrer = false;
}
return $isDateReferrer;
}
/**
* Test $authorReferrer against $superGlobalVar.
*
* @since 1.0.0
* @return (bool) true on success or false on failure
*/
public function isSearchReferrer()
{
if ($this->searchReferrer && isset($this->superGlobalVar[$this->searchReferrer])) {
$isSearchReferrer = true;
} else {
$isSearchReferrer = false;
}
return $isSearchReferrer;
}
/**
* Test $authorReferrer against $superGlobalVar.
*
* @since 1.0.0
* @return (bool) true on success or false on failure
*/
public function isTaxReferrer()
{
if ($this->taxReferrer && isset($this->superGlobalVar[$this->taxReferrer])) {
$isTaxReferrer = true;
} else {
$isTaxReferrer = false;
}
return $isTaxReferrer;
}
/**
* Conditional which check if the current post is a referred post.
*
* @since 1.0.0
* @return (bool) true on success or false on failure
*/
public function isReferredPost()
{
if ($this->isAuthorReferrer() || $this->isDateReferrer() || $this->isSearchReferrer() || $this->isTaxReferrer()) {
$isReferredPost = true;
} else {
$isReferredPost = false;
}
return $isReferredPost;
}
/**
* Return the value from the super global when the current post is a post referred from
* an author archive page.
*
* @since 1.0.0
* @return (array) $authorReferrerValue
*/
public function hasAuthorReferrerValue()
{
if ($this->isAuthorReferrer()) {
$authorReferrerValue = [$this->authorReferrer => $this->superGlobalVar[$this->authorReferrer]];
} else {
$authorReferrerValue = null;
}
return $authorReferrerValue;
}
/**
* Return the value from the super global when the current post is a post referred from
* a date archive page.
*
* @since 1.0.0
* @return (array) $dateReferrerValue
*/
public function hasDateReferrerValue()
{
if ($this->isDateReferrer()) {
$dateReferrerValue = [$this->dateReferrer => $this->superGlobalVar[$this->dateReferrer]];
} else {
$dateReferrerValue = null;
}
return $dateReferrerValue;
}
/**
* Return the value from the super global when the current post is a post referred from
* a search page.
*
* @since 1.0.0
* @return (array) $searchReferrerValue
*/
public function hasSearchReferrerValue()
{
if ($this->isSearchReferrer()) {
$searchReferrerValue = [$this->searchReferrer => $this->superGlobalVar[$this->searchReferrer]];
} else {
$searchReferrerValue = null;
}
return $searchReferrerValue;
}
/**
* Return the value from the super global when the current post is a post referred from
* a taxonomy archive page.
*
* @since 1.0.0
* @return (array) $taxReferrerValue
*/
public function hasTaxReferrerValue()
{
if ($this->isTaxReferrer()) {
$taxReferrerValue = [$this->taxReferrer => $this->superGlobalVar[$this->taxReferrer]];
} else {
$taxReferrerValue = null;
}
return $taxReferrerValue;
}
}
这就是我使用这个类的方法
$b = new RequestReferrerHandler($_GET, 'aq', 'dq', 'sq', 'tq');
?><pre><?php var_dump($b->hasAuthorReferrerValue()); ?></pre><?php
?><pre><?php var_dump($b->hasDateReferrerValue()); ?></pre><?php
?><pre><?php var_dump($b->hasSearchReferrerValue()); ?></pre><?php
?><pre><?php var_dump($b->hasTaxReferrerValue()); ?></pre><?php
出于测试目的,您可以将['aq' => '1']
之类的内容注入课程而不是$_GET
这是我现在陷入困境的地方,不知道如何继续前进。我需要构造两个类,它们都使用上面类中的相同方法,一个类将从上面的类中的has*
方法构造查询参数,一个类也将创建query_vars
来自上述类的has*
方法将用于构建新的帖子链接
因此,简而言之,两个类都将使用与上述类
中的方法完全相同的类hasAuthorReferrerValue();
hasDateReferrerValue();
hasSearchReferrerValue();
hasTaxReferrerValue();
举个例子,这里是两个类应该是什么样子的例子。 (我在此省略了一些方法以使代码更易于管理)
ClassA的
<?php
namespace PG\Single\Post\Navigation;
class ClassA //Just a generic name for testing purposes. Will also implement ClassAInterface
{
protected $handler;
public function __construct(RequestReferrerHandlerInterface $handler)
{
$this->handler = $handler;
}
public function santizeAuthor()
{
$author = $this->handler->hasAuthorReferrerValue(); // Value will be either null or single key/value pair array. Example ['aq' => '1']
if ($author) {
$author = array_values($author);
$author = ['author' => (int)htmlspecialchars($author[0])]; //Will output ['author' => 1]
}
return $author; //Returns null or the array ['author' => 1]
}
public function santizeDate()
{
$date = $this->handler->hasDateReferrerValue();
if ($date) {
// @TODO Still to work out
}
return $date;
}
//etc
public function queryArguments() // Will be used in the controller class ClassC
{
$queryArgs = null;
if ($this->santizeAuthor()) {
$queryArgs = $this->santizeAuthor();
} elseif ($this->santizeDate) {
$queryArgs = $this->santizeDate();
} // etc
return $queryArgs; //Will return null if all 4 conditions fail or return the value from the one that returns true
}
}
ClassB的
<?php
namespace PG\Single\Post\Navigation;
class ClassB //Just a generic name for testing purposes. Will also implement ClassBInterface
{
protected $handler;
public function __construct(RequestReferrerHandlerInterface $handler)
{
$this->handler = $handler;
}
public function santizeAuthor()
{
$author = $this->handler->hasAuthorReferrerValue(); // Value will be either null or single key/value pair array. Example ['aq' => '1']
if ($author) {
foreach ($author as $k=>$v)
$author[htmlspecialchars($k)] = (int)htmlspecialchars($v);
}
return $author; //Returns null or the array ['aq' => 1]
}
public function santizeDate()
{
$date = $this->handler->hasDateReferrerValue();
if ($date) {
// @TODO Still to work out
}
return $date;
}
//etc
public function queryVars() // Will be used in the controller class ClassC
{
$queryVars = null;
if ($this->santizeAuthor()) {
$queryVars = $this->santizeAuthor();
} elseif ($this->santizeDate) {
$queryVars = $this->santizeDate();
} // etc
return $queryVars; //Will return null if all 4 conditions fail or return the value from the one that returns true
}
}
queryArguments()
中的ClassA
方法和queryVars()
中的ClassB
方法将用于其他类(或一个控制器类)
我完全缺乏适当的知识进入OOP,混淆分离关注点,封装,SOLID原则和保持类可测试让我第二次猜测我的代码,我觉得我错过了一些东西。
无论如何,我可以优化以上内容。我不是要求任何类型的代码重写,我需要的是关于优化这一点的正确指针和想法,如果不是,则将其提升到标准。如果任何人都可以提供代码示例,比如大纲骨架
,那将是一个真正的好处答案 0 :(得分:11)
查看你的代码,你肯定是一个良好的开端。在使用OOP进行编程时,您已经使用了一条经验法则 - 将程序编程到接口,而不是实现。术语接口我不仅仅指代实际接口,但也是抽象类。
因此,在您的问题的核心,您希望有两个类ClassA
和ClassB
,它们都使用RequestReferrerHandler
中的常用方法。您已经完成了使用界面RequestReferrerHandlerInterface
完成的基础工作。所以我们说你有一个如下所示的界面:
interface RequestReferrerHandlerInterface
{
public function hasAuthorReferrerValue();
public function hasDateReferrerValue();
public function hasSearchReferrerValue();
public function hasTaxReferrerValue();
}
只要此接口由RequestReferrerHandler
实现,您就可以键入提示接口作为ClassA
和ClassB
的构造函数要求。但这并不是什么新鲜事,因为你已经这样做了。
有两件事特别突出,因为潜在的疼痛拇指。首先,由于您希望课程的职责很小,因此您应该负责向RequestReferrerHandler
提供远离自身的数据并将其提供给您的Controller
。换句话说,不要将$_GET
注入您的班级。确保您的Controller
具有正确创建RequestReferrerHandler
所需的所有信息。让我们看一下您的RequestReferrerHandler
课程,并使用所需的所有方法。
class RequestReferrerHandler implements RequestReferrerHandlerInterface
{
private $author;
private $date;
private $search;
private $tax;
public function __construct($author = null, $date = null, $search = null, $tax = null)
{
$this->setAuthorReferrer($author);
$this->setDateReferrer($date);
$this->setSearchReferrer($search);
$this->setTaxReferrer($tax);
}
public function hasAuthorReferrerValue()
{
return $this->author !== null ? true : false;
}
public function hasDateReferrerValue()
{
return $this->date !== null ? true : false;
}
public function hasSearchReferrerValue()
{
return $this->search !== null ? true : false;
}
public function hasTaxReferrerValue()
{
return $this->tax !== null ? true : false;
}
public function getAuthorReferrer()
{
return $this->author;
}
public function getDateReferrer()
{
return $this->date;
}
public function getSearchReferrer()
{
return $this->search;
}
public function getTaxReferrer()
{
return $this->tax;
}
public function setAuthorReferrer($author)
{
$this->author = $author;
}
public function setDateReferrer($date)
{
$this->date = $date;
}
public function setSearchReferrer($search)
{
$this->search = $search;
}
public function setTaxReferrer($tax)
{
$this->tax = $tax;
}
}
第二件事是santize()
方法。您是否了解ClassA
和ClassB
中的重复方式? sanitizeAuthor()
在两个班级中有所不同,但其余的怎么样?这是DRY (不要重复自己)原则可以提供帮助的情况。由于多个类可能必须以类似的方式清理数据,因此从类中抽象出来是有意义的。
让我们来看看如何做到这一点,然后我们将回到你的具体课程。首先创建一个新接口,该接口将指定必须由可以清理数据的对象公开的方法。
interface SanitizerInterface
{
public function sanitizeAuthor();
public function sanitizeDate();
public function sanitizeSearch();
public function sanitizeTaxonomy();
}
现在,如果您拥有ClassX
的每个对象以不同的方式实现这四种方法,您就可以开始在不同的类中实现它,只需简单地清理数据。但是,对于这个例子,我们会说并非如此。我们假设sanitizeAuthor()
和ClassA
之间的ClassB
可能不同(它在您的代码中),并且所有其他方法将实现完全相同。这种情况下我们可以使用一个实现清理方法的抽象类。
abstract class AbstractSanitizer implements SanitizerInterface
{
protected $handler;
public function __construct() {}
public function setHandler(RequestReferrerHandlerInterface $handler)
{
$this->handler = $handler;
}
/* For this example we are saying that sanitizeDate(), sanitizeTaxonomy() and
* sanitizeSearch() will be the same no matter what. So let's implement them
* and leave the child classes to implement sanitizeAuthor().
*
* Implement the details of the sanitizer function to fit your needs.
*/
public function sanitizeDate()
{
if($this->handler !== null)
{
//Perform whatever tasks to sanitize the date
$sanitized = strtoupper($this->handler->getDateReferrer());
echo "Sanitize date -> switch to uppercase letters.\n";
$this->handler->setDateReferrer($sanitized);
}
}
public function sanitizeSearch()
{
if($this->handler !== null)
{
//Perform whatever tasks to sanitize the search
$sanitized = strtolower($this->handler->getSearchReferrer());
echo "Sanitize search -> switch to lowercase letters.\n";
$this->handler->setSearchReferrer($sanitized);
}
}
public function sanitizeTaxonomy()
{
if($this->handler !== null)
{
//Perform whatever tasks to sanitize the taxonomy
$sanitized = str_replace(" ", "_", $this->handler->getTaxReferrer());
echo "Sanitize Taxonomy -> convert spaces to underscores.\n";
$this->handler->setTaxReferrer($sanitized);
}
}
}
有些事情需要立即注意。首先,您会注意到setHandler()
方法接受RequestReferrerHandlerInterface
的实例。这是为什么?方便大部分。由于我们已经采取了消毒行为并将其封装到自己的类中,因此我们为消毒剂提供了一种更新其正在使用的具体RequestReferrerHandler
以及来自消毒方法的更新输出的方法。
接下来,我们使用RequestReferrerHandler
类中未在RequestReferrerHandlerInterface
中指定的方法。这不是本身的直接问题,因为我们知道getters和setter之类的方法都在类中。但是,如果您决定使用不同的具体对象实现该接口,则单独对接口进行类型提示并不保证这些方法可用。因此,我们需要使用可保证其可用性的方法更新RequestReferrerHandlerInterface
。
interface RequestReferrerHandlerInterface
{
public function hasAuthorReferrerValue();
public function hasDateReferrerValue();
public function hasSearchReferrerValue();
public function hasTaxReferrerValue();
public function getAuthorReferrer();
public function getDateReferrer();
public function getSearchReferrer();
public function getTaxReferrer();
public function setAuthorReferrer($author);
public function setDateReferrer($date);
public function setSearchReferrer($search);
public function setTaxReferrer($tax);
}
现在,回到那些消毒剂。我们知道ClassA
和ClassB
将以不同方式实施其sanitizeAuthor()
方法。摘要类AbstractSanitizer
的设置方式与原因相同,因为sanitizeAuthor()
中的SanitizerInteface
方法未在AbstractSanitizer
中实现,因此我们必须将其扩展为提供功能。我们需要以下两个类来执行此操作:
class SanitizerForClassA extends AbstractSanitizer
{
/* This class must provide an implementation for how ClassA will
* handle the sanitizeAuthor() method.
*/
public function sanitizeAuthor()
{
if($this->handler !== null)
{
//Perform whatever tasks to sanitize the for ClassA
$sanitized = array("author" => $this->handler->getAuthorReferrer());
echo "Sanitize author -> ClassA makes author an array.\n";
$this->handler->setAuthorReferrer($sanitized);
}
}
}
class SanitizerForClassB extends AbstractSanitizer
{
/* This class must provide an implementation for how ClassB will
* handle the sanitizeAuthor() method.
*/
public function sanitizeAuthor()
{
if($this->handler !== null)
{
//Perform whatever tasks to sanitize the for ClassB
$sanitized = new stdClass();
$sanitized->author = $this->handler->getAuthorReferrer();
echo "Sanitize author -> ClassB makes author an object property. \n";
$this->handler->setAuthorReferrer($sanitized);
}
}
}
这两个具体类可以与ClassA
和ClassB
一起使用,以清理将传递给它们的具体RequestReferrerHandler
方法中的数据。
继续前进,让我们看一下ClassA
和ClassB
的规范。我们知道ClassA
需要方法queryArguments()
,ClassB
需要方法queryVars()
,并且这两个类都需要允许RequestReferrerHandlerInterface
和{的实例{1}}在他们的构造函数中。我们将使用一个接口处理构造函数需求,然后另外两个接口将扩展它以提供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();
}
你有它,地面工作已经建成。您会注意到,在构造函数中,为给定的处理程序和清理程序类设置了属性,然后为清理程序提供了对处理程序的引用。 (请记住,清理程序会引用处理程序,以便处理程序中的清理属性会自动更新。各个类现在不必担心。)
所以现在百万美元的问题是如何使用它。好吧,你需要一个可以接受class ClassA implements QueryArgumentsInterface
{
private $handler;
private $sanitizer;
public function __construct(RequestReferrerHandlerInterface $handler, SanitizerInterface $sanitizer)
{
$this->handler = $handler;
$this->sanitizer = $sanitizer;
$this->sanitizer->setHandler($this->handler);
}
public function queryArguments() // Will be used in the controller class ClassC
{
$queryArgs = null;
if($this->handler->hasAuthorReferrerValue())
{
$this->sanitizer->sanitizeAuthor();
$queryArgs = $this->handler->getAuthorReferrer();
}
if($this->handler->hasDateReferrerValue())
{
$this->sanitizer->sanitizeDate();
$queryArgs = $this->handler->getDateReferrer();
}
if($this->handler->hasSearchReferrerValue())
{
$this->sanitizer->sanitizeSearch();
$queryArgs = $this->handler->getSearchReferrer();
}
if($this->handler->hasTaxReferrerValue())
{
$this->sanitizer->sanitizeTaxonomy();
$queryArgs = $this->handler->getTaxReferrer();
}
return $queryArgs; //Will return null if all 4 conditions fail or return the value from the one that returns true
}
}
class ClassB implements QueryVarsInterface
{
private $handler;
private $sanitizer;
public function __construct(RequestReferrerHandlerInterface $handler, SanitizerInterface $sanitizer)
{
$this->handler = $handler;
$this->sanitizer = $sanitizer;
$this->sanitizer->setHandler($this->handler);
}
public function queryVars() // Will be used in the controller class ClassC
{
$queryVars = null;
if($this->handler->hasAuthorReferrerValue())
{
$this->sanitizer->sanitizeAuthor();
$queryVars = $this->handler->getAuthorReferrer();
}
if($this->handler->hasDateReferrerValue())
{
$this->sanitizer->sanitizeDate();
$queryVars = $this->handler->getDateReferrer();
}
if($this->handler->hasSearchReferrerValue())
{
$this->sanitizer->sanitizeSearch();
$queryVars = $this->handler->getSearchReferrer();
}
if($this->handler->hasTaxReferrerValue())
{
$this->sanitizer->sanitizeTaxonomy();
$queryVars = $this->handler->getTaxReferrer();
}
return $queryVars; //Will return null if all 4 conditions fail or return the value from the one that returns true
}
}
和ClassA
的控制器。我们也将通过它们各自的接口键入提示。
ClassB
在您的class Controller
{
public function __construct() {}
public function doStuff(QueryArgumentsInterface $argsClass, QueryVarsInterface $varsClass)
{
var_dump($argsClass->queryArguments());
var_dump($varsClass->queryVars());
}
}
版本和queryArguments()
版本中,您需要清理返回值的数据。让我们插入一些数据,看看我们得到了什么。 (注意:正如您已经发现我使用的所有清理方法都没有做您正在做的事情,它们只是说明性的。)
queryVars()
这是输出:
//TEST DRIVE
//Create a controller that will use the classes
$controller = new Controller();
//Now make use of your new shiny handlers and sanitizers
$controller->doStuff(
new ClassA(new RequestReferrerHandler("Mark Twain", null, null, null), new SanitizerForClassA()),
new ClassB(new RequestReferrerHandler(null, "January 1st, 1999", null, null), new SanitizerForClassB())
);
$controller->doStuff(
new ClassA(new RequestReferrerHandler(null, null, "OK Google Now!", null), new SanitizerForClassA()),
new ClassB(new RequestReferrerHandler(null, null, null, "Super Awesome Taxonomy Tables"), new SanitizerForClassB())
);
$controller->doStuff(
new ClassA(new RequestReferrerHandler(null, "January 1st, 1999", null, null), new SanitizerForClassA()),
new ClassB(new RequestReferrerHandler("Mark Twain", null, null, null), new SanitizerForClassB())
);
$controller->doStuff(
new ClassA(new RequestReferrerHandler(null, null, null, "Super Awesome Taxonomy Tables"), new SanitizerForClassA()),
new ClassB(new RequestReferrerHandler(null, null, "OK Google Now!", null), new SanitizerForClassB())
);
那么所有这些费用是什么?简短回答 - 复杂性。它花了4个接口,1个抽象类和一些具体的类来向屏幕输出一些数据。
你获得了什么?简短回答 - 灵活性。将来您可能希望添加更多实现Sanitize author -> ClassA makes author an array.
array (size=1)
'author' => string 'Mark Twain' (length=10)
Sanitize date -> switch to uppercase letters.
string 'JANUARY 1ST, 1999' (length=17)
Sanitize search -> switch to lowercase letters.
string 'ok google now!' (length=14)
Sanitize Taxonomy -> convert spaces to underscores.
string 'Super_Awesome_Taxonomy_Tables' (length=29)
Sanitize date -> switch to uppercase letters.
string 'JANUARY 1ST, 1999' (length=17)
Sanitize author -> ClassB makes author an object property.
object(stdClass)[15]
public 'author' => string 'Mark Twain' (length=10)
Sanitize Taxonomy -> convert spaces to underscores.
string 'Super_Awesome_Taxonomy_Tables' (length=29)
Sanitize search -> switch to lowercase letters.
string 'ok google now!' (length=14)
或QueryVarsInterface
的类。请考虑这些类QueryArgumentsInterface
,ClassC
和ClassD
。所有这些类都需要一个清理程序类来与它们一起使用(即如果ClassE
或SanitizerForClassA
不适合该法案)并且继续编写清洁剂类将是繁琐的。嗯,对你好,因为你一直在编程接口,你不会遇到这个问题。您可以使用SanitizerForClassB
方法的默认实现轻松创建GenericSanitizer
。在您不需要专门的消毒剂类的任何情况下,使用此类可以使用sanitizeAuthor()
。您可以轻松实现Controller::doStuff()
或QueryArgumentInterface
的不同具体类来测试您想要添加的实验性功能,而不会篡改您当前的类。
希望这能让您对一些OOP原则有所了解。以下是上述所有代码的完整副本。将其拖放到一个空的PHP文件中并运行它以查看所有操作。快乐的节目!
QueryVarsInterface
答案 1 :(得分:2)
正如我在previous questions中所看到的,您正在寻找一种方法来合理化您的OOP开发。这就是为什么我不会给你一条鱼,但我会帮你自己钓鱼。这意味着我将(尝试)给出你应该知道的基础来做一个强大的OOP代码。
<强> 1。 SRP和组合
正如我在您的问题中所看到的,您尝试将课程的责任分开。当然,这在OOP中是件好事。你所做的是Single Responsability Principle (SRP)。这个原则意味着你更喜欢构图而不是继承。
// Composition
class Car implements VehicleInterface
{
private $motor;
}
class Motor implements MotorInterface
在这种情况下,汽车和电机有两种不同的响应能力。
// Inheritance
class Car extends MotorVehicle
{
}
在继承的情况下,您可以在车辆和电机概念之间进行高度比较。
想象一下,我希望有一个像运动这样的新概念:
// Composition
class Car implements VehicleInterface
{
private $motor;
private $movement;
}
class Motor implements MotorInterface
class Drive implements MovementInterface
构图没问题。
// Inheritance
class Car extends MotorVehicle, DriveVehicle
{
}
多重继承很糟糕(甚至在PHP中也不可能)因为你打破了SRP。继承只应用于为具有相同责任的类的代码分解。由于您应该只有一个类的责任,因此不应使用多重继承。其他可能性很糟糕,因为你不能告诉我汽车更像MotorVehicle
而不是DriveVehicle
。
在您的情况下,您有一些请求引荐来源处理程序和一些清理程序。
<强> 2。接口和低耦合
正如我在previous answer中告诉你的那样,在使用界面在你的班级之间制作low coupling时,你做对了。这为您提供了更可维护,可扩展且可测试的代码。以前面的例子为例:
class Car implements VehicleInterface
{
private $motor;
}
class PetrolMotor implements MotorInterface
class DieselMotor implements MotorInterface
你的车现在可以很容易地采用不同类型的电机了!
这个想要引起你注意的想法是,一个类不应该直接使用另一个类,而是一个描述行为的接口。
在您的情况下,
class A
和class B
应实现接口SanitizerInterface
。
第3。依赖注入
此时,您要在清洁剂中设置处理程序。更好的方法是使用dependency injection。这真的很简单!
class Car implements VehicleInterface
{
private $motor;
public function __construct(MotorInterface $motor)
{
$this->motor = $motor;
}
}
class PetrolMotor implements MotorInterface
{
}
class DieselMotor implements MotorInterface
{
}
$motor = new PetrolMotor();
$car = new Car($motor);
在您的情况下,您必须在清理程序中注入请求引荐来源处理程序。
<强> 4。 SOA 强>
应用于OOP的面向服务的体系结构(SOA)是合理化代码的好方法。
// Standard OOP
$car = new Car();
$buyer = new Person();
$store = new Store();
// Many solutions to buy a car!
$buyer->buy($car, $store);
// or
$store->sell($car, $buyer);
// or
// ...
在标准OOP中,您经常会遇到重复的代码。我应该在哪里编写这种方法?我现在可以在哪里(或其他人)已编码此方法?以前这个问题对我来说太无聊了!我无法使我的发展合理化。
// SOA
$car = new Car();
$buyer = new Person();
$store = new Store();
$saleHandler = new SaleHandler();
$saleHandler->sell($car, $buyer, $store);
在SOA中,您有一个“服务类”(此处为SaleHandler
)(基本上实现为singleton),它处理“数据类”的操作(此处为Car
,{ {1}}和Person
)。您的数据类中没有智能(您通常只在属性上有getter和setter)。这样,您就知道您的销售代码在哪里了!
在您的情况下,您的请求引荐来源处理程序和清理程序似乎是某种服务,所以没关系。
<强>结论强>
总之,您直观地使用了一些非常好的OOP实践。现在,您可以应用它们并知道原因!
但是,我强烈建议您尝试像Symfony2这样的框架。它将为您提供一个强大的PHP开发基础和一个非常好的dependency injection component,允许您在配置文件中定义类的所有依赖项,以获得真正的动态代码。它还可以帮助您使用其服务进行SOA。
使用框架是一件好事,可以为您的开发和职业生活提供动力(开发人员知道框架更受追捧)。由于PHP框架主要是开源,您也可以参与并为招聘人员提供良好的可见性。
答案 2 :(得分:1)
如果您想将RequestReferrerHandler
类的同一对象用于ClassA和ClassB,那么您的策略是正确的。只需要使用RequestReferrerHandler
类的对象来实例化ClassA和ClassB。然后,您可以访问特定的方法。即ClassA.queryArguments()
或ClassB.queryVars()
如果要为ClassA和ClassB创建RequestReferrerHandler
类的单独对象,可以将RequestReferrerHandler
类扩展为ClassA和ClassB,而无需定义构造函数。因此,当您创建ClassA的对象时,它会自动继承RequestReferrerHandler
类的构造函数方法,您可以通过parent:
关键字访问属性和方法。例如:
class ClassA extends RequestReferrerHandler
{
public function santizeAuthor()
{
$author = parent::hasAuthorReferrerValue(); // access the base class method
if ($author) {
$author = array_values($author);
$author = ['author' => (int)htmlspecialchars($author[0])]; //Will output ['author' => 1]
}
return $author; //Returns null or the array ['author' => 1]
}
public function santizeDate()
{
$date = parent::hasDateReferrerValue();
if ($date) {
// @TODO Still to work out
}
return $date;
}
//etc
public function queryArguments() // Will be used in the controller class ClassC
{
$queryArgs = null;
if ($this->santizeAuthor()) {
$queryArgs = $this->santizeAuthor();
} elseif ($this->santizeDate) {
$queryArgs = $this->santizeDate();
} // etc
return $queryArgs; //Will return null if all 4 conditions fail or return the value from the one that returns true
}
}
您可以像ClassB一样做。现在,您可以为ClassA和ClassB创建对象,在C类中分配构造函数的基类参数,并使用其对象的ClassA.queryArguments()
或ClassB.queryVars()
的返回值。
答案 3 :(得分:0)
如果您专注于数据类型而不是我在api中工作的对象的含义,能够操作并从Wordpress结构中获取信息,并且我知道他们已经做了很多很好的工作简化了他们的数据结构。所以你只需要考虑后期,后期元和分类法(索引),而不是像(出版物,税收,图像,图书馆,帖子,产品)这样的含义。
即使在woo commerce中,你也可以看到它非常简单。
你会看到;产品是一种帖子,产品的数据在后元。图像是一种帖子,该图像的细节在post_meta中,新闻是帖子,而后新闻中的新闻的详细信息。
一种结构不同的含义。因此,为了访问所有内容或存储数据,您只需要这些元素。如果您能够以一种宁静的方式访问这些元素,那么实现起来非常容易。
我希望这可以帮助你一些我的观点,你可以不这么认为。但我认为与你分享他很重要。